Stefan Welscher: Teil eines komplexen Hashes referenziert heraustrennen

Moin moin,
die Überschrift ist evtl. etwas verwirrend.
Was ich habe ist ein Hash mit mehreren Levels:

z.B.

my %I;
$I{'level1'}{'level2'}{'level3'}{'value1'}=1;
$I{'level1'}{'level2'}{'level3'}{'value2'}=2;
$I{'level1'}{'level2'}{'level3'}{'value3'}=3;
$I{'level1'}{'level2'}{'level3'}{'value4'}=4;
$I{'level1'}{'level2'}{'level3'}{'level4'}{'value1'}=5;
$I{'level1'}{'level2'}{'level3'}{'level4'}{'value2'}=6;
$I{'level1'}{'level2'}{'level3'}{'level4'}{'level5'}{'value1'}=7;
$I{'level1'}{'level2'}{'level3'}{'level4'}{'level5'}{'value2'}=8;
$I{'level1'}{'level2'}{'level3'}{'value5'}=9;

Das "Master-Hash" kann dabei nahezu unendlich viele Ebenen haben, bzw. lässt sich die Anzahl der Ebenen zumindest nicht vorhersehen.
Jetzt möchte ich z.B. alles ab Level3 in einem anderen Hash referenzieren.

So funzt es nicht, weil hier dereferenziert wird:

my %I_level3=%{$I{'level1'}{'level2'}{'level3'}};

Hier das gleiche:

my $I_ref=%{$I{'level1'}{'level2'}{'level3'}};
my %I_level3=%{$I_ref};

Ich könnte zwar mit $I2->{...} die Referenz nutzen, müsste aber eben bei jeder Verwendung des Teilhashes den Pfeil mit verwenden.
Ist es nicht irgendwie möglich, dass die Referenz "unsichtbar" bleibt und ich eben nicht 11000 Zeilen Quelltext umschreiben muss?

Beispiel:

  
#/usr/bin/perl  
  
use strict;  
  
  
my %I;  
$I{'level1'}{'level2'}{'level3'}{'value1'}=1;  
$I{'level1'}{'level2'}{'level3'}{'value2'}=2;  
$I{'level1'}{'level2'}{'level3'}{'value3'}=3;  
$I{'level1'}{'level2'}{'level3'}{'value4'}=4;  
$I{'level1'}{'level2'}{'level3'}{'level4'}{'value1'}=5;  
$I{'level1'}{'level2'}{'level3'}{'level4'}{'value2'}=6;  
$I{'level1'}{'level2'}{'level3'}{'level4'}{'level5'}{'value1'}=7;  
$I{'level1'}{'level2'}{'level3'}{'level4'}{'level5'}{'value2'}=8;  
$I{'level1'}{'level2'}{'level3'}{'value5'}=9;  
  
my %I_level3=%{$I{'level1'}{'level2'}{'level3'}};  
  
print "\nDEBUG:SOURCE_HASH:".$I{'level1'}{'level2'}{'level3'}{'value1'};  
print "\nDEBUG:DESTINATION_HASH:".$I_level3{'value1'}."\n";  
  
$I_level3{'value1'}=2;  
  
print "\nDEBUG:SOURCE_HASH:".$I{'level1'}{'level2'}{'level3'}{'value1'};  
print "\nDEBUG:DESTINATION_HASH:".$I_level3{'value1'}."\n";  

Besten Dank schonmal!

  1. Ich könnte zwar mit $I2->{...} die Referenz nutzen, müsste aber eben bei jeder Verwendung des Teilhashes den Pfeil mit verwenden.

    Im Sinne der Gesundheit: es erinnert dich daran, dass der Wert der Variable eine Referenz ist.

    $hash{elem}{elem}
    und
    $hash{elem}->{elem}

    sind im übrigen das gleiche. Ein Hash kann keinen anderen Hashes enthalten sondern lediglich Hashreferenzen.

    Ist es nicht irgendwie möglich, dass die Referenz "unsichtbar" bleibt und ich eben nicht 11000 Zeilen Quelltext umschreiben muss?

    Nein, den Quältext hast du dir in dem Fall verdient.

    Du kannst zwar sagen
    my %newhash=();
    $newhash{dummy} = $oldhash{some}{silly}{data}{structure};
    und dann sagen
    print $newhash{dummy}{somepar};
    aber eben nur, weil das das gleiche ist wie
    print $newhash{dummy}->{somepar};

    mfg Beat

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

      Im Sinne der Gesundheit: es erinnert dich daran, dass der Wert der Variable eine Referenz ist.

      Genau. Und als Schlüssel wird stets ein Scalar erwartet. D.h. die Quoterei kannste auch noch weglassen.

      $foo{'bar'} zeigt auf denselben Speicherbereich wie $foo{bar}.

      Aus der Perldoc: Sie brauchen nur 10% Verständnis für Datenstrukturen um 90% der Aufgabenstellungen abzudecken.

      Btw., vor einiger Zeit hatte ich malne Frage zu $hash{time}. Da als key stets ein String erwartet wird, denkt Perl nicht im Geringsten daran, die Funktion time() an dieser Stelle aufzurufen.

      Horst Hashisch

    2. Ist es nicht irgendwie möglich, dass die Referenz "unsichtbar" bleibt und ich eben nicht 11000 Zeilen Quelltext umschreiben muss?

      Nein, den Quältext hast du dir in dem Fall verdient.

      Naja, verdient ist jetzt subjektiv... Das ist eben ein großes, zentrales Datenhash, das direkt vom User über tabellarische Zuordnungen oder Commands gebildet wird und an zahlreiche Unterfunktionen weitergereicht wird. Das könnte ich anders kaum abfangen, OO evtl., aber da habe ich nicht viel Ahnung von.

      Trotzdem Danke, muss ich eben erstmal mit nem Workaround arbeiten... nach der Bearbeitung des Teilhashes einfach wieder...:

      %{$I{'level1'}{'level2'}{'level3'}=%I_level3;

      1. Trotzdem Danke, muss ich eben erstmal mit nem Workaround arbeiten... nach der Bearbeitung des Teilhashes einfach wieder...:

        %{$I{'level1'}{'level2'}{'level3'}=%I_level3;

        Umständlicher geht es kaum, ich weiß nicht wie weit da Perl optimiert, aber es dürfte auch unperfomant sein.

        $I{level1}->{level2}->{level3} = \%I_level3;

        Struppi.

      2. hi,

        .. OO evtl., aber da habe ich nicht viel Ahnung von.

        Was Du hier schreibst, sind Perl-Objekte. D.h., genauso werden die in OOP notiert:

        $obj->{name} = 'Otto';
        $obj->{vname} = 'Horst';

        Kannst auch schreiben: $$obj{name}, aber der Pfeil bringt bischen mehr Übersicht (danke Struppi).

        Data::Dump (Hinweis von Struppi) zeigt Dir auch die Klasse, zu der ein Objekt gehört. Das kann auch die Klasse main sein, d.h., Du kannst Perl-Objekte auch vorzüglich nutzen, ohne eine neue eigene Klasse zu erstellen. Z.B. um benamste Parameter an Subs zu übergeben, das macht die Sache einfach und scalierbar, wenn Du mal was ändern willst:

        sub fooooo{
         my $p = shift; # thats all :-)
         print "Name: $p->{name}, Hausnummer: $p->{nummer}";
        }

        So, das wars für heute, Feierabend :-)
        Horst Hamilch

  2. z.B.

    my %I;
    $I{'level1'}{'level2'}{'level3'}{'value1'}=1;

    Oh seit wann geht das? Ich bin mir sicher, dass alte Perlversionen dir hier gesagt hätten, dass level1 keine Hashreferenz ist. Sie werden aber anscheinend mittlerweile automatisch erzeugt.

    Du hast hier also schon ein Hash, mit drei Hashreferenzen erzeugt.

    Jetzt möchte ich z.B. alles ab Level3 in einem anderen Hash referenzieren.

    [...]

    So funzt es nicht, weil hier dereferenziert wird:

    Du kannst eine Referenz nur in einem Skalar speichern, d.h. eine Hashreferenz kannst du nicht einem Hash zuweisen. Du musst also genau überlegen, was du eigentlich willst.

    Hier das gleiche:

    my $I_ref=%{$I{'level1'}{'level2'}{'level3'}};

    Das ist eine Referenz, allerdings umständlich, da es das Gleiche ist wie:

    my $I_ref= $I{'level1'}{'level2'}{'level3'};

    Ich bevorzuge aber die Pfeilschreibweise (wenn ich solch vertrackten Strukturen verwenden würde), dann sieht man das auch.

    my $I_ref= $I{'level1'}->{'level2'}->{'level3'};

    (ich weiß den zweiten Pfeil könnte man sich auch sparen, aber wie gesagt mir geht es um's sehen der Referenz)

    Ich könnte zwar mit $I2->{...} die Referenz nutzen, müsste aber eben bei jeder Verwendung des Teilhashes den Pfeil mit verwenden.

    Ja musst du. Weil es keine verschachtelteten Hashes oder Arrays gibt, die zweite Ebene ist immer eine Referenz, wie es Beat schon sagte.

    Ist es nicht irgendwie möglich, dass die Referenz "unsichtbar" bleibt und ich eben nicht 11000 Zeilen Quelltext umschreiben muss?

    Den möchte ich nicht sehen, wenn er solch verschachtelten Strukturen in der Form nutzt.

    #/usr/bin/perl

    Besser:
    #/usr/bin/perl -w

    print "\nDEBUG:SOURCE_HASH:".$I{'level1'}{'level2'}{'level3'}{'value1'};

    Du kannst dir die ganze Struktur mit dem Standardmodul Data::Dumper ansehen,

    use Data::Dumper;  
    ...  
    print Dumper \%I;
    

    Struppi.