Beat: Object Getter/Setter und undef

Hi
Ich sitze gerade for einem Designproblem.

Ein Object Getter/Setter der einfachsten Art ist

  
sub midi {  
  my $score = shift;  
  if( $_[0] ){ $score->{midi} = $_[0] }  
  return $score->{midi};  
}  

nun sollte mein Getter/Setter aber folgendes erfüllen:

  
$ob->midi  
$ob->midi()  
# gibt den Inhalt aus $score->{midi} zurück  
  
$ob->midi("val")  
# setzt den Inhalt von $score->{midi}  
# und gibt den neuen Inhalt zurück  
  
$ob->midi(undef)  
# entfernt die Eigenschaft $score->{midi}  

obiges ist aber identisch mit
$ob->midi()
und ist deshalb ein einfacher Getter.

Ich könnte zwar folgendes Design wählen:
$ob->midi("")
Jedoch muss ich eine leeren aber definierte Wert vorhalten
so kommt das also nicht in Frage.

nun ist undef ja sowohl ein undefinierter Wert als auch ein Operator
undef $hash->{value} würde also den Wert auf undef setzen. Das iist mein Ziel.

Nun kann ich aber nicht schreiben:

undef $ob->midi();

Mir bleibt nur der Ausweg, eine Methode zu schreiben:

$ob->remove_midi

Wie kann ich das vermeiden?
Es handelt sich um einige Methoden die konsistent behandelt werden müssen.

mfg Beat

--
><o(((°>           ><o(((°>
   <°)))o><                     ><o(((°>o
Der Valigator leibt diese Fische
  1. moin,

      
    $ob->method();  
    $ob->method;    # same as above  
    $ob->method('4711'); # ein Argument  
      
    sub method{  
      my $self = shift; # das erste Arg ist immer das Objekt  
      my $val  = shift || undef; # zweites Arg ist das erste aus der Klammer  
      $self->{KEY} = $val;  
    }  
    
    

    Wenn eine Methode nur die Attribute eines Objekts ändert, brauchst Du keine Return-Values weil Du eine Referenz übergibst.

    Hotti

    1. $ob->method();
      $ob->method;    # same as above
      $ob->method('4711'); # ein Argument

      sub method{
        my $self = shift; # das erste Arg ist immer das Objekt
        my $val  = shift || undef; # zweites Arg ist das erste aus der Klammer
        $self->{KEY} = $val;
      }

      
      >   
      > Wenn eine Methode nur die Attribute eines Objekts ändert, brauchst Du keine Return-Values weil Du eine Referenz übergibst.  
        
      Dadurch aber setzt $ob->method den hashwert auf undef.  
      Ich würde eher erwarten, den Wert zurückzuerhalten, ohne ihn gleichzeitig auf undef zu setzen.  
        
      Ich komme anscheinend nicht um eine extra Methode aus.  
        
      mfg Beat
      
      -- 
      
      ><o(((°>           ><o(((°>  
      
         <°)))o><                     ><o(((°>o  
      Der Valigator leibt diese Fische
      
      1. Hi,

        Dadurch aber setzt $ob->method den hashwert auf undef.
        Ich würde eher erwarten, den Wert zurückzuerhalten, ohne ihn gleichzeitig auf undef zu setzen.

        wenn Du beim Wert undef ein besonderes Verhalten wünschst, dann implementiere dies doch einfach. Perl kennt sowohl "if" also auch Mechanismen zum Vergleich von Werten.

        Cheatah

        --
        X-Self-Code: sh:( fo:} ch:~ rl:| br:> n4:& ie:% mo:) va:) de:] zu:) fl:{ ss:) ls:~ js:|
        X-Self-Code-Url: http://emmanuel.dammerer.at/selfcode.html
        X-Will-Answer-Email: No
        X-Please-Search-Archive-First: Absolutely Yes
  2. Moin Moin!

    Hi
    Ich sitze gerade for einem Designproblem.

    Ein Object Getter/Setter der einfachsten Art ist

    sub midi {
      my $score = shift;
      if( $[0] ){ $score->{midi} = $[0] }
      return $score->{midi};
    }

    
    >   
    > nun sollte mein Getter/Setter aber folgendes erfüllen:  
    > ~~~perl
      
    
    > $ob->midi  
    > $ob->midi()  
    > # gibt den Inhalt aus $score->{midi} zurück  
    >   
    > $ob->midi("val")  
    > # setzt den Inhalt von $score->{midi}  
    > # und gibt den neuen Inhalt zurück  
    >   
    > $ob->midi(undef)  
    > # entfernt die Eigenschaft $score->{midi}  
    > 
    
    

    obiges ist aber identisch mit
    $ob->midi()
    und ist deshalb ein einfacher Getter.

    Falsch. $_[1] ist zwar bei $ob->midi() und $ob->midi(undef) undefiniert, aber @_ enthält im ersten Fall nur ein Element, im zweiten Fall zwei.

      
    sub midi  
    {  
        my $self=shift;  
        if (@_) {  
            my $value=shift;  
            if (defined $value) {  
                $self->{'midi'}=$value;  
                return $value; # oder $self, oder den alten Wert, oder gar nichts  
            } else {  
                delete $self->{'midi'};  
                return; # oder $self, oder den alten Wert (return value von delete)  
            }  
        } else {  
            return $self->{'midi'};  
        }  
    }  
    
    

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
    1. Falsch. $_[1] ist zwar bei $ob->midi() und $ob->midi(undef) undefiniert, aber @_ enthält im ersten Fall nur ein Element, im zweiten Fall zwei.

      sub midi
      {
          my $self=shift;
          if (@_) {
              my $value=shift;
              if (defined $value) {
                  $self->{'midi'}=$value;
                  return $value; # oder $self, oder den alten Wert, oder gar nichts
              } else {
                  delete $self->{'midi'};
                  return; # oder $self, oder den alten Wert (return value von delete)
              }
          } else {
              return $self->{'midi'};
          }
      }

        
      Danke, das ist die Lösung.  
      Irgendwo im Hinterstüchen war, dass ich @\_ abfragen kann.  
      Ich verfolge nur nicht konsequent jeden Gedankenstrang.  
        
      mfg Beat
      
      -- 
      
      ><o(((°>           ><o(((°>  
      
         <°)))o><                     ><o(((°>o  
      Der Valigator leibt diese Fische
      
      1. hi Beat,

        Irgendwo im Hinterstüchen war, dass ich @_ abfragen kann.

        Der arrow-OP hat im Fall der Methodenaufrufe eine andere Bedeutung:

        $obj->method;

        bewirkt, dass in der Methode "method" das erste Element in @_ bzw. $_[0] das Objekt selbst ist. Perler benennen das i.d.R. $self, geläufig ist aber auch $this.

        Im Konstruktor ist das erste Argument die Klasse, die aufgerufen wurde:
        $obj = Class->new;

        sub new{
          my $class = $_[0]; # oder shift
          my $this = [];     # hier isses mal eine Array-Ref
          return bless $this, $class;
        }

        In Objekt-Strukturen ermöglicht der arrow-OP den Zugriff auf die Werte, ist klar:
        my $val = $ob->{erster}->{name};

        Ich verfolge nur nicht konsequent jeden Gedankenstrang.

        Alles wird Dir sehr schnell in Fleisch und Blut übergehen. Hashes als Objekte ermöglichen infolge einfacher Zuweisungen z.b. den Einbau von Objekten in ganze Sammlungen von Objekten. Eigenschaften sind namentlich ansprechbar. Eine Anweisung

        $objects->{1} = $obj->{1};

        Hängt $obj an der richtigen Stelle {1} in den Strukturbaum ein. Oder mal so:

        $Artikel->{'handy'}->{'preis'} = $rest->{1}->{preis}; # rest-Schnittstelle, neuer Preis

        Dahinter können sich die verwegensten Datenstrukturen verbergen, Anwendungen z.b. auch CMS, Einbau neuer Ressourcen+Attribute.

        Objekte müssen nicht unbedingt Hashrefs sein. In "sortierten Daten" kannst Du selbstverständlich auch eine Array-Ref verwenden, darin wieder {}-Zeugs usw. Deiner Kreativität sind keine Grenzen gesetzt.

        Hotti

  3. Mir bleibt nur der Ausweg, eine Methode zu schreiben:

    $ob->remove_midi

    Wie kann ich das vermeiden?

    Das wäre der sauberste Weg.

    Eine Funktion sollte nicht zuviele Aufgaben erledigen. In Java sind schon i.d.R. setter und getter zwei Funktionen, du willst gleich drei in eine packen - aus OOP Sicht ist dies nicht zu empfehlen.

    Struppi.

    1. Eine Funktion sollte nicht zuviele Aufgaben erledigen. In Java sind schon i.d.R. setter und getter zwei Funktionen, du willst gleich drei in eine packen - aus OOP Sicht ist dies nicht zu empfehlen.

      Ich verfolge nun Alexanders Weg.
         $ob->property(undef)
      Setzt den Wert auf undef
      Jedoch habe ich nun auch vereinzelt implementiert:
         $ob->remove('hashkey')
      Um das Element ganz aus der Hashreferenz zu entfernen.

      Zugabe:
      Wie käme ich da an die Syntax
         $ob->remove->hashkey
      ?
      und wäre das sinnvoll?

      Hintergrund:
      Ich schreibe einen Preprozessor für Lilypond Markup.
      Dabei spielen leere Kontexte auch eine aktive Rolle (im Vergleich zu abwesenden Kontext)
      Beispiel
      \score {
        <<
        >>
        \midi{}
      }
      Erzeugt ein Midifile für diesen Score

      \score {
        <<
        >>
      }
      gibt kein Midifile für diesen Score aus

      mfg Beat

      --
      ><o(((°>           ><o(((°>
         <°)))o><                     ><o(((°>o
      Der Valigator leibt diese Fische
      1. Moin Moin!

        Zugabe:
        Wie käme ich da an die Syntax
           $ob->remove->hashkey
        ?

        Was sieht Perl dort ohne Source-Filter?

        Rufe die Methode "remove" des Objektes "$ob" auf.

        Rufe die Methode "hashkey" des von "remove" gelieferten Objekts bzw. der gelieferten Klasse auf.

        Die "remove"-Methode könnte eine Instanz einer fast komplett leeren Klasse liefern, die in einer AUTOLOAD-Methode den Namen der aufgerufenen Methode benutzt, um aus $ob einen Hash-Key zu löschen. Dazu muß die Instanz entweder $ob enthalten oder eine Callback-Methode / Callback-Funktion in $ob aufrufen.

          
        #!/usr/bin/perl  
          
        use 5.010;  
        use strict;  
        use warnings;  
          
        {  
        	package SomeClass;  
          
        	sub new  
        	{  
        		my $proto=shift;  
        		return bless {},ref($proto)||$proto;  
        	}  
          
        	sub midi  
        	{  
        		my $self=shift;  
        		if (@_) {  
        			my $v=shift;  
        			if (defined $v) {  
        				$self->{'midi'}=$v;  
        				return $self;  
        			} else {  
        				delete $self->{'midi'};  
        				return $self;  
        			}  
        		} else {  
        			return $self->{'midi'};  
        		}  
        	}  
          
        	sub remove  
        	{  
        		my $self=shift;  
        		return bless [ $self ],'KillerClass';  
        	}  
          
        	sub killer_callback  
        	{  
        		my ($self,$name)=@_;  
        		delete $self->{$name};  
        		return $self;  
        	}  
        }  
        {  
        	package KillerClass;  
          
        	sub AUTOLOAD  
        	{  
        		our $AUTOLOAD;  
        		my $self=shift;  
        		my $name=$AUTOLOAD;  
        		$name=~s/'/::/g;  
        		$name=~s/.+:://;  
        		return $self->[0]->killer_callback($name);  
        	}  
          
        	sub DESTROY { }  
        }  
        {  
        	package main;  
          
        	use Data::Dumper;  
          
        	my $o=SomeClass->new();  
        	$o->midi(42);  
        	print Dumper($o);  
        	$o->remove->midi();  
        	print Dumper($o);  
        }  
        
        

        und wäre das sinnvoll?

        Jedenfalls extrem ungewohnt und sehr umständlich.

        Was passiert, wenn Du zwar remove aufrufst, aber den Methodenaufruf bei der zurückgelieferten Hilfsklassen-Instanz vergißt?

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
        1. Moin Moin!

          Zugabe:
          Wie käme ich da an die Syntax
             $ob->remove->hashkey
          ?

          Was sieht Perl dort ohne Source-Filter?

          Auf jeden Fall sucht Perl nach einer Methode remove im Package des Objects.
          Wenn diese nicht etwas zurückliefert, das dann sinnvoll aufgelöst werden kann
          _returnvalue_->hashkey
          also selbst ein Objekt liefert, ist hier ende.

          Rufe die Methode "remove" des Objektes "$ob" auf.

          Rufe die Methode "hashkey" des von "remove" gelieferten Objekts bzw. der gelieferten Klasse auf.

          Die "remove"-Methode könnte eine Instanz einer fast komplett leeren Klasse liefern, die in einer AUTOLOAD-Methode den Namen der aufgerufenen Methode benutzt, um aus $ob einen Hash-Key zu löschen. Dazu muß die Instanz entweder $ob enthalten oder eine Callback-Methode / Callback-Funktion in $ob aufrufen.

          ...
          ...

          und wäre das sinnvoll?

          Jedenfalls extrem ungewohnt und sehr umständlich.

          Ich würde sagen, es ist teuer. Ich erzeuge ein Objekt nur um eine Eigenschaft zu zerstören.

          Was passiert, wenn Du zwar remove aufrufst, aber den Methodenaufruf bei der zurückgelieferten Hilfsklassen-Instanz vergißt?

          du meinst:
          $ob->remove->someprop ?
          oder
          $ob->remove
          Dann bekomme ich das erzeugte Objekt der KillerClasse ohne dass ich etwas sinnvolles bewirke.

          Mir scheint ein:
          $ob->remove('key1',key2')
          ist da doch effizienter.

          Danke

          mfg Beat

          --
          ><o(((°>           ><o(((°>
             <°)))o><                     ><o(((°>o
          Der Valigator leibt diese Fische
          1. Moin Moin!

            Jedenfalls extrem ungewohnt und sehr umständlich.

            Ich würde sagen, es ist teuer. Ich erzeuge ein Objekt nur um eine Eigenschaft zu zerstören.

            Yepp. Aber cool, das man solche perversen Sachen bauen *kann* ... ;-)

            Was passiert, wenn Du zwar remove aufrufst, aber den Methodenaufruf bei der zurückgelieferten Hilfsklassen-Instanz vergißt?

            du meinst:
            $ob->remove->someprop ?
            oder
            $ob->remove
            Dann bekomme ich das erzeugte Objekt der KillerClasse ohne dass ich etwas sinnvolles bewirke.

            Richtig, *ÜBERRASCHUNG!* Ein immerwährender Quell von Bugs.

            Und Du bist auf Identifier beschränkt, während Du bei Strings alle Namen benutzen kannst.

            Mir scheint ein:
            $ob->remove('key1',key2')
            ist da doch effizienter.

            Ja.

            delete @$ob{'key1','key2'} ist natürlich noch flotter, bricht aber die Kapselung der Daten.

            Alexander

            --
            Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
      2. Das liest sich bereits wie das Metaobjektprotokoll. Willst du das etwa neu schreiben?

        perl -Moose=Foo -E'
                has "midi" => (is => "rw");

        my $f = Foo->new;
                say "# has midi" if $f->meta->has_attribute("midi");
                $f->meta->remove_attribute("midi");
                say "# still has midi" if $f->meta->has_attribute("midi");
            '

        # has midi

        1. Das liest sich bereits wie das Metaobjektprotokoll. Willst du das etwa neu schreiben?

          perl -Moose=Foo -E'
                  has "midi" => (is => "rw");

          my $f = Foo->new;
                  say "# has midi" if $f->meta->has_attribute("midi");
                  $f->meta->remove_attribute("midi");
                  say "# still has midi" if $f->meta->has_attribute("midi");
              '

          # has midi

          Ehrlich gesagt, reichen mir standard Object Methoden.
          Ich plage mich schon genug mit Lilypond Syntax und Documakulatur, falls das jemand nachvollziehen kann.

          Dennoch interessant, was Moose so bereithält.

          mfg Beat

          --
          ><o(((°>           ><o(((°>
             <°)))o><                     ><o(((°>o
          Der Valigator leibt diese Fische