[Perl] Weitergabe von geschachtelten Hashes zwischen Funktionen

bitmuncher

Senior-Nerd
Ich stelle gerade fest, dass man scheinbar Hashes, die Hashes enthalten in Perl nicht einfach mittels 'return' zurückgeben kann, aber vielleicht übersehe ich ja auch was. Also Problem ist folgendes...

Ich habe eine Funktion, die ein Hash generiert, das wiederum Hashes enthält. Das sieht stark vereinfacht so aus:

Code:
sub meinefunktion
{
  my ($self_str, $opp_str) = @_; 
  # $self_str und $opp_str haben jeweils das format: eigenschaft1:wert,eigenschaft2:wert,usw.
  # im echten programm werden sie aus einer db geholt
  my %effects = (); # hash fuer die rueckgabe, wird im folgenden gefuellt
  if($self_str eq '0') {
    $effects{'self'}{'nothing'} = 1;
  } else {
    my @self_effects = split(/,/, $self_str); # die einzelnen 'eigenschaft:wert'-abschnitte trennen
    my %effects_tmp = ();
    foreach $effect (@self_effects) {
      my @temp = split(/:/, $effect); # aufteilen in eigenschaft und wert
      my ($effect_tmp, $value_tmp) = @temp;
      %effects_tmp{$effect_tmp} = $value_tmp;
    }
    $effects{'self'} = %effects_tmp;
  }
  # selbiges nochmal fuer $opp_str, nur dass das resultierende hash in $effects{'opp'}
  # abgelegt wird
  ...
  return %effects;
}

Nun will ich die Werte an einer anderen Stelle im Code etwa so weiterverarbeiten:

Code:
my %action_effects = &meinefunktion($self, $opp);
if($action_effects{'self'}{'nothing'} != 1) {
  my %self_effects = $action_effects{'self'};
  foreach my $effect (keys %self_effects) {
    $report .= "Eigenschaft: ".$effect." Wert: ".$self_effects{$effect}.".\n";
  }
}

Dummerweise bekomme ich aber als Ausgabe in $report lediglich ein 'Eigenschaft: 2/8 Wert: .' Erwarten tue ich bei meinen Testdaten aber ein 'Eigenschaft: attributname Wert: +20%'.

In der Zeile 'my %self_effects = $action_effects{'self'};' beschwert sich Perl auch mit 'Odd number of elements in hash assignment...'. Nun stellt sich mir natürlich die Frage, wie ich ein Hash aus Hashes in Perl von einer Funktion weitergeben lassen kann ohne es global zu machen. Hat evtl. jemand eine Idee?
 
Ich hab das hier dazu gefunden:

http://www.cs.mcgill.ca/~abatko/computers/programming/perl/howto/hash/#function_to_build_a_hash_of_hashes__return_a_reference

Code:
    sub foo
    {
        my ( $login, $p, $uid, $gid, $gecos, $dir, $s );
     
        my %HoH = ();
     
        my $file = '/etc/passwd';
        open( PASSWD, "< $file" ) or die "Can't open $file : $!";
     
        while( <PASSWD> ) {
            ( $login, $p, $uid, $gid, $gecos, $dir, $s ) = split( ':' );
     
            $HoH{ $login }{ 'uid' } = $uid;
            $HoH{ $login }{ 'gid' } = $gid;
            $HoH{ $login }{ 'dir' } = $dir;
        }
     
        close PASSWD;
     
        return \%HoH;
    }

Du musst soweit ich das verstanden habe eine Reference auf den Hash zurückgeben mit

return \%myhash

cu
serow
 
Damit bekomme ich als Eigenschaft (also als Hash-Index) ein Hash, aber auch keinen Wert. Der Output wird dadurch 'Eigenschaft: HASH(0x989dd20) Wert: . '.
 
Hi,

ja weil du weiter mit der Reference arbeitest und nicht mit dem Hash auf den die Reference zeigt:

Code:
#!/bin/perl -w
use strict;
use warnings FATAL => 'all';

sub foo {

        my %HoH = (
                flintstones => {
                        husband   => "fred",
                        pal       => "barney",
                },
                jetsons => {
                        husband   => "george",
                        wife      => "jane",
                        "his boy" => "elroy",  # Key quotes needed.
                },
                simpsons => {
                        husband   => "homer",
                        wife      => "marge",
                        kid       => "bart",
                },
        );

        return \%HoH;

}


my $myhash = &foo();

for my $key ( keys %$myhash ) {

        print "$key\n";

}

cu
serow
 
Das geht so leider nicht, da %$variable ein Syntaxfehler ist. Verwende ich '%variable = function()' bekomme ich ein 'Reference found where even-sized list expected'. Nutze ich '$variable = function()' bekomme ich zwar keine Fehlermeldung, aber ich bekomme auch aus $variable{'self'} kein Hash, wenn ich 'my $unterhash = $variable{'self'};' ausführe.
 
Also mein Code läuft hier ohne Syntaxfehler:

Code:
mathias@x61t:~$ cat test.pl 
#!/bin/perl -w
use strict;
use warnings FATAL => 'all';
sub foo {
        my %HoH = (
                flintstones => {
                        husband   => "fred",
                        pal       => "barney",
                },
                jetsons => {
                        husband   => "george",
                        wife      => "jane",
                        "his boy" => "elroy",  # Key quotes needed.
                },
                simpsons => {
                        husband   => "homer",
                        wife      => "marge",
                        kid       => "bart",
                },
        );
        return \%HoH;
}


my $myhash = &foo();
for my $key ( keys %$myhash ) {
        print "$key\n";
}
mathias@x61t:~$ perl test.pl 
simpsons
jetsons
flintstones
mathias@x61t:~$

cu
serow
 
Mal abgesehen vom allgemeingueltigen Programmierer Mantra "Parse nicht", besser mit key->value strukturen arbeitest. Das kann ne Menge arbeit ersparen :)

In diesem (echt genialen) How-To wird das (tatsaechliche) Dilemma zwischen %hash und %$hash_ref ein wenig genauer beschrieben.

http://www.cs.mcgill.ca/~abatko/computers/programming/perl/howto/hash/#perl_hash_howto

Nutze ich '$variable = function()' bekomme ich zwar keine Fehlermeldung, aber ich bekomme auch aus $variable{'self'} kein Hash, wenn ich 'my $unterhash = $variable{'self'};' ausführe.

$unterhash = $$variable{'self'};
Wenns was zum de-referenzieren gibt.
 
Da auch die Wege aus dem Hash-Howto nicht funktionierten, hab ich nun eine andere Lösung über 2 getrennte Funktionen gemacht, wodurch ich auf die Verschachtelung des Hashs verzichten konnte. Danke für die Tipps und Infos. Ich hab zumindest draus gelernt, dass ich die Verschachtelung von Hashes besser vermeide, wenn die Werte durch eine Funktion weitergegeben werden müssen. ;)
 
Zurück
Oben