2010-12-30 10 views
8

Jestem całkiem nowy w Perlu i staram się skompilować rekursywnie rekursywnie i nigdzie nie docierając. Próbowałem szukać tutoriali, aby dynamicznie budować hasze, ale wszystko, co mogłem znaleźć, to wstępne artykuły o hashe. Byłbym wdzięczny, gdybyś skierował mnie w dobrym kierunku lub zaproponował fajny artykuł/samouczek.Dynamicznie/rekursywnie buduje hasze w Perlu?

Próbuję odczytać z pliku, który ma ścieżek w postaci

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

i chcę zbudować hash jak

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

Skrypt używam obecnie jest :

my $finalhash = {}; 
my @input = <>; 

sub constructHash { 
    my ($hashrf, $line) = @_; 
    @elements = split(/\//, $line); 
    if(@elements > 1) { 
     $hashrf->{shift @elements} = constructHash($hashrf->{$elements[0]}, @elements); 
    } else { 
     $hashrf->{shift @elements} = ""; 
    } 
    return $hashrf; 
} 

foreach $lines (@input) { 
    $finalhash = constructHash($finalhash, $lines); 
} 
+4

Twoja linia 'my ($ hashrf, $ line) = $ _;' powinna raczej odczytać 'my ($ hashrf, $ line) = @_;' zamiast tego. –

+0

Naprawiono :) Ale wciąż nic nie daje. Jeśli wydrukuję wartość 'hashrf' w pętli, zawsze jest to' '' ' –

Odpowiedz

6

To trochę naciągane, ale to działa:

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

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

Opiera się on autovivification, który jest wprawdzie nieco wyprzedza dla początkujących.

To, co prawdopodobnie spowodowałoby, że jakakolwiek odpowiedź na twoje pytanie jest nieco zakręcona, to prośby o puste struny w liściach, które mają inny "typ" niż skróty węzłów i wymagają innej operacji dereferencyjnej.

+3

wystarczy, że pętla 'while' i' chomp' będą jawne we własnym wierszu i będą czytelne dla początkującego. –

+0

Chciałbym móc całkowicie pozbyć się 'chomp'. Wymagane jest, aby nie mieć brzydkich znaków nowej linii w kluczach liści, ale tak naprawdę nie dodaje nic do problemu. Jeśli chodzi o 'while', opinie są różne, ale możesz mnie edytować, jeśli naprawdę tak myślisz. –

+0

++ ponieważ twoje rozwiązanie nie pozostawia pustych makr podobnych do moich:) – Hugmeir

3

Nigdy nie zrobiłem czegoś takiego, więc to podejście prawdopodobnie będzie złe, ale cóż, oto mój shot:

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 

EDYCJA: Naprawiono!

EDIT2: A (jak sądzę) zoptymalizowana wersja z ogonem, ponieważ!

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

    goto &construct; 
} 
+0

Ah, to działa! : D jednak ma mały problem, przypuśćmy, że mam 'jeden/dwa/trzy' i' jeden/dwa/cztery' to nadpisze 'jeden/dwa/trzy' z' jeden/dwa/cztery' zamiast mieć oba. Mam nadzieję, że to ma sens. –

+0

Idź, naprawiony! Korzysta ze zdefiniowanego operatora, który działa tylko w 5.10+ Perlu - w starszych Perlach, musiałbyś zrobić coś takiego: my $ temp = $ hash -> {shift()}; skonstruuj ((zdefiniowane ($ temp)? $ temp: {}), @_) http://perldoc.perl.org/perlop.html#C-style-Logical- Defined-Or – Hugmeir

+1

Jeśli masz do czynienia z zduplikowane klucze ('jeden' ​​dla' jeden/dwa/trzy' oraz także 'jeden/dwa/cztery') nie możesz używać skrótów takich jak próbujesz. Będziesz musiał użyć tablicy lub innego klucza, jak cała linia. – Nathan

3

Pobiegłem swój kod i znalazłem kilka problemów:

  • jeszcze nie scoped @elements prawidłowo.
  • z tą rekursją tworzysz hasz, który się odwołuje, czyli , a nie czego chcesz.
  • w swoim najdalszym rozmowy, drugi arg do constructHash() jest ciągiem znaków, ale na rekurencyjnego wywołania wewnątrz, mijamy tablicę @elements

Spróbuj tego.

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; 

Produkuje

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

Pamiętaj, mieszań Perl nie są sortowane.

+0

Dziękuję za poprawki: 1) Tak, chyba błąd początkujących. 2) Po tym, jak napisałem pytanie, spróbowałem 'join ('/', @elements)' w rekurencyjnym wywołaniu, ale nie działało. 3) Sądzę, że to był główny problem. Nadal nie rozumiem, dlaczego to nie zadziałało. Czy możesz wskazać mi artykuł/tutorial o hash referencjach, które czynią to jaśniejszym? Jeszcze raz bardzo dziękuję. :) –

+0

Dla 3 problem nie jest tak naprawdę odwołaniem hash, ale przekazaniem parametrów. Obecnie twój okręt podwodny jest funkcją dwóch argumentów (ref i line) i to jest sposób, w jaki wywołujesz go z poziomu tween. Jednak wywołanie rekursywne wywołuje je jako funkcję wielu argumentów: ref i wielu elementów. W efekcie stracisz wszystkie elementy oprócz pierwszego za każdym razem, gdy się powtórzysz. –

+2

@Guarav, Nauczyłem się tego, wpatrując się w ['perldoc perlref'] (http://perldoc.perl.org/perlref.html) i jego krewnych (perldsc, lol, toot) i [Effective Perl Programming] (http : //en.wikipedia.org/wiki/Effective_Perl_Programming) również było instrumentalne. – Nathan

1
+0

Żadne z tych łączy nie zajmuje się rekursywnie/dynamicznie generowaniem struktury danych (czy jest na to lepsza nazwa?). Wciąż, kocham perldsc. – Hugmeir

+2

Po prostu nie miałem ochoty na linkowanie do wikipedii dla rekursji. perldsc wyjaśnia, z jakiego skórki pochodzą hasze (a w Perlu to nie jest trywialne). To najtrudniejsza część. Nie rekurencja, a nie aspekt dynamiczny. Moim zdaniem, tak czy inaczej :-) –

+0

Dla mnie wystarczająco dobrze:) Zapomniałem, dzięki za link perlglossary! Nie wiedziałem, że to istnieje. – Hugmeir

7

Data::Diver obejmuje tę niszę tak dobrze, że ludzie nie powinni wyważać otwartych drzwi.

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

Dziękujemy za sugestię tego modułu. Próbowałem przeszukać CPAN, ale nie mogłem tego znaleźć (może w tym przypadku "hash" w wyszukiwaniu nie był dobrym pomysłem z mojej strony). Muszę jednak powiedzieć, że wiele nauczyłem się od ponownego wynalezienia koła (tak naprawdę, widząc wynalezienie koła przez ekspertów) :) –