2010-12-30 6 views
8

Ich bin ziemlich neu in Perl und ich versuche, einen Hash rekursiv zu erstellen und nirgendwo zu bekommen. Ich habe versucht, nach Tutorials zu suchen, um Hashes dynamisch zu erstellen, aber alles, was ich finden konnte, waren einführende Artikel über Hashes. Ich wäre dankbar, wenn Sie mich in die richtige Richtung weisen oder einen schönen Artikel/Tutorial vorschlagen.Dynamisch/rekursiv Hashes in Perl erstellen?

Ich versuche, aus einer Datei zu lesen, die Pfade in Form von

one/two/three 
four 
five/six/seven/eight 

hat, und ich möchte einen Hash wie

VAR = { 
    one : { 
     two : { 
      three : "" 
     } 
    } 
    four : "" 
    five : { 
     six : { 
      seven : { 
       eight : "" 
      } 
     } 
    } 
} 

Das Skript zur Zeit verwende ich bauen :

+4

Ihre 'my ($ hashrf, $ line) = $ _;' Zeile sollte wahrscheinlich 'my ($ hashrf, $ line) = @_;' stattdessen lesen. –

+0

Fixed :) Aber es summiert sich immer noch nichts. Wenn ich den Wert von 'hashrf' in der Schleife ausdrücke, ist es immer' '' ' –

Antwort

6

Das ist ein bisschen weit hergeholt, aber es funktioniert:

sub insert { 
    my ($ref, $head, @tail) = @_; 
    if (@tail) { insert(\%{$ref->{$head}}, @tail) } 
    else   {   $ref->{$head} = ''  } 
} 

my %hash; 
chomp and insert \%hash, split('/', $_) while <>; 

Es auf autovivification beruht, die zwar ein bisschen ist für einen Anfänger fortgeschritten.

Was wahrscheinlich eine Antwort auf Ihre Frage ein wenig verdreht wäre, ist, dass Sie nach leeren Strings in den Blättern fragen, die einen anderen "Typ" als die Hashes der Knoten haben und eine andere Dereferenzierung erfordern.

+3

machen Sie einfach die 'while'-Schleife und' chomp' explizit in einer eigenen Zeile, und es wird für Anfänger_ weit lesbar sein. –

+0

Ich wünschte, ich könnte die "Chomp" einfach ganz wegschaffen. Es ist notwendig, keine hässlichen Zeilenumbrüche in den Blattschlüsseln zu haben, aber fügt dem Problem wirklich nichts hinzu. Was die 'while' angeht, sind die Meinungen unterschiedlich, aber Sie können mich gerne bearbeiten, wenn Sie wirklich tief denken. –

+0

++ weil deine Lösung keine leeren HashRefs wie meins übrig lässt:) – Hugmeir

3

Ich habe so etwas noch nie gemacht, also ist dieser Ansatz wahrscheinlich falsch, aber gut, hier ist mein Aufnahme:

use 5.013; 
use warnings; 
use Data::Dumper; 

sub construct { 
    my $hash = shift; 
    return unless @_; 

    return construct($hash->{shift()} //= {}, @_); 
} 

my %hash; 

while (<DATA>) { 
    chomp; 
    construct(\%hash, split m!/!); 
} 

say Dumper \%hash; 

__DATA__ 
one/two/three 
four 
five/six/seven/eight 

BEARBEITEN: Korrigiert!

EDIT2: Eine (ich glaube) Tail-Call-optimierte Version, weil!

sub construct { 
    my $hash = shift; 
    return unless @_; 
    unshift @_, $hash->{shift()} //= @_ ? {} : ''; 

    goto &construct; 
} 
+0

Ah, das funktioniert! : D aber es hat ein kleines Problem, angenommen, ich hätte 'eins/zwei/drei' und' eins/zwei/vier', es überschreibt 'eins/zwei/drei' mit' eins/zwei/vier' anstelle von beiden. Ich hoffe das macht Sinn. –

+0

Da gehen Sie, behoben! Es verwendet den Defined-or-Operator, der nur auf einem Perl 5.10+ funktioniert - in älteren Perls müsste man so etwas machen: my $ temp = $ hash -> {shift()}; Konstrukt ((definiert ($ temp)? $ temp: {}), @_) http://perldoc.perl.org/perlop.html#C-style-Logical-Defined-Or – Hugmeir

+1

Wenn Sie sich damit befassen müssen doppelte Schlüssel ('eins' für' eins/zwei/drei' und auch für 'eins/zwei/vier') Sie können keine Hashes verwenden, wie Sie es versuchen. Sie müssen ein Array oder einen anderen Schlüssel verwenden, wie die ganze Zeile selbst. – Nathan

3

lief ich Ihren Code und fand ein paar Probleme:

  • Sie nicht @elements richtig scoped haben.
  • mit dieser Rekursion erstellen Sie einen Hash, der sich selbst referenziert, das ist nicht was Sie wollen.
  • in Ihrem äußersten Anruf, der zweite ARG constructHash() ist eine Zeichenfolge, aber auf dem rekursiven Aufruf nach innen, übergeben Sie ein Array von @elements

bereits.

use Data::Dumper; 

my $finalhash = {}; 
my @input = split "\n", <<INPUT; 
one/two/three 
four 
five/six/seven/eight 
INPUT 

sub constructHash { 
    my $line = shift; 
    my ($first, $remainder) = split(/\//, $line,2); 

    if ($remainder) { 
     return { $first => constructHash($remainder) } ; 
    } else { 
     return { $first , "" }; 
    } 
} 

foreach $lines (@input) { 
    my $linehash = constructHash($lines); 
    my $firstkey = (keys %$linehash)[0]; 
# print Dumper $linehash; 
    $finalhash->{$firstkey} = $linehash->{$firstkey}; 
} 


print Dumper $finalhash; 

Es produziert

$VAR1 = { 
      'five' => { 
         'six' => { 
           'seven' => { 
               'eight' => '' 
              } 
           } 
        }, 
      'one' => { 
        'two' => { 
           'three' => '' 
           } 
        }, 
      'four' => '' 
     }; 

Denken Sie daran, sind Perl-Hashes nicht bestellt.

+0

Vielen Dank für die Korrekturen: 1) Yup, Anfänger Fehler, denke ich. 2) Nachdem ich die Frage gepostet habe, habe ich 'Join ('/', @elements)' für den rekursiven Aufruf versucht, aber es schien nicht zu funktionieren. 3) Ich denke, das war das Hauptproblem. Ich verstehe immer noch nicht, warum es nicht funktioniert hat. Kannst du mich auf einen Artikel/Tutorial über Hash-Referenzen verweisen, die das deutlicher machen würden? Nochmals vielen Dank. :) –

+0

Für 3 ist das Problem nicht wirklich Hash-Referenzen, sondern Parameter übergeben. Momentan ist Ihr Sub eine Funktion von zwei Argumenten (ref und line), und das ist die Art und Weise, wie Sie es vom Toplevel aus aufrufen. Aber der rekursive Aufruf ruft ihn als eine Funktion vieler Argumente auf: das ref und mehrere Elemente. In der Tat würden Sie alle Elemente außer dem ersten jedes Mal verlieren, wenn Sie rekursiv waren. –

+2

@Guarav, lernte ich dieses Zeug, indem ich auf ['perldoc perlref'] (http://perldoc.perl.org/perlref.html) und seine Verwandten (perldsc, lol, toot) und [Effektive Perl-Programmierung] (http : //en.wikipedia.org/wiki/Effective_Perl_Programming) war auch instrumental. – Nathan

1
+0

Keiner dieser Links beschäftigt sich mit dem rekursiven/dynamischen Erzeugen einer Datenstruktur (gibt es einen besseren Namen dafür?). Trotzdem, ich muss Liebe perldsc. – Hugmeir

+2

Ich hatte einfach keine Lust auf Wikipedia für Rekursion zu verlinken. perldsc erklärt, aus welchen Hashes Hashes bestehen (und in Perl ist es nicht trivial). Das ist der schwierige Teil davon. Nicht Rekursion, nicht der dynamische Aspekt. Meiner Meinung nach jedenfalls :-) –

+0

Gut genug für mich :) Hab auch vergessen, danke für den perlglossary link! Ich wusste nicht, dass es existiert. – Hugmeir

7

Data::Diver deckt diese Nische so gut, dass die Menschen nicht das Rad neu erfinden sollte.

use strict; 
use warnings; 
use Data::Diver 'DiveVal'; 
use Data::Dumper; 

my $root = {}; 
while (my $line = <DATA>) { 
    chomp($line); 
    DiveVal($root, split m!/!, $line) = ''; 
} 
print Dumper $root; 
__DATA__ 
one/two/three 
four 
five/six/seven/eight 
+0

Vielen Dank, dass Sie dieses Modul vorgeschlagen haben. Ich habe versucht, nach CPAN zu suchen, konnte es aber nicht finden (vielleicht war das "Hash" in der Suche auch keine gute Idee von mir). Ich muss allerdings sagen, dass ich viel gelernt habe, als ich das Rad neu erfand (tatsächlich sah ich das Rad von den Experten neu erfunden) :) –

Verwandte Themen