2011-07-31 25 views
14

Mam zagnieżdżonej tabeli mieszania, który wygląda tak:Czy "istnieje" Perla modyfikuje wartości struktury danych?

my %myhash = (
    "val1" => { 
     "A/B.c" => { 
      "funct1" => 1 
     } 
    }, 
    "val2" => { 
     "C/D.c" => { 
      "funct2" => 1 
     } 
    } 
) 

Moim celem w tej strukturze danych ma produkować różne wartości w zależności od tego, czy istnieją pewne tabele hash. Na przykład:

sub mysub 
{ 
    my $val = shift; 
    my $file = shift; 
    my $funct = shift; 

    if (exists $myhash{$val}{$file}{$funct}) { 
     return "return1"; 
    } 
    if (exists $myhash{$val}{$file}) { 
     return "return2"; 
    } 
    return "return3"; 
} 

Zachowanie, które napotykam, jest następujące. Mam instancji w czasie, gdy my $ val = "val1"; my $ file = "C/D.c"; my $ funct = "funct3";

W tym momencie zwracana jest wartość "return2". To są moje obserwacje z debuggera Perl:

  1. Przerwa w pierwszych "jeśli" w mysub
  2. Drukuj p $ proxToBugs { "val1"} { "C/D.C."} ==> Zwraca pusty wiersz. W porządku. Kontynuuj, a to "jeśli" jest pomijane.
  3. Kontynuuj i rozbij na sekundę "if" w mysub
  4. Drukuj p $ proxToBugs {"val1"} {"C/D.c"} ==> Zwraca "HASH (0x ...)". WTF moment. Funkcja zwraca "return2".

To mówi mi, że po uruchomieniu pierwszej, jeśli zmodyfikowano strukturę danych, która pozwala na drugą operację, gdy w rzeczywistości nie powinna. Funkcja, którą wykonuję, jest identyczna z funkcją pokazaną powyżej; ten jest po prostu zdezynfekowany. Czy ktoś ma dla mnie wyjaśnienie? :)

Odpowiedz

21

Tak. Wynika to z autovivification. Na dole dokumentacji exists:

Chociaż w większości głęboko zagnieżdżonej tablicy lub mieszania nie sprężyście do życia tylko dlatego, że jej obecność została zbadana, wszystkie te uczestniczące [autovivified tablic lub skróty] będzie [sprężystego do istniejący]. Tym samym $ ref -> {"A"} i $ ref -> {"A"} -> {"B"} powstanie dzięki testowi istnienia dla elementu $ key powyżej. Dzieje się nigdzie operator strzałka służy ...

Gdzie "... test kluczowego elementu $ powyżej ..." odnosi się do:

if (exists $ref->{A}->{B}->{$key}) { } 
if (exists $hash{A}{B}{$key})  { } # same idea, implicit arrow 

Szczęśliwy kodowania.

+2

Potwierdzona ... Nadal mam dużo do nauczenia się w Perlu, który robię. Ta sprawa doprowadzała mnie do szału przez ponad pół godziny. Dzięki! – danns87

9

Jak słusznie zauważa pst, jest to autoworyzacja. Istnieją co najmniej dwa sposoby, aby tego uniknąć.Pierwszy (i najczęściej w moim doświadczeniu) jest testowanie na każdym poziomie:

if (
    exists $h{a}  and 
    exists $h{a}{b} and 
    exists $h{a}{b}{c} 
) { 
    ... 
} 

Zwarcie charakter and powoduje, że drugi i trzeci wywołań exists aby nie zostać zrealizowane, jeżeli wcześniejsze poziomy nie robić istnieć.

Nowszym rozwiązaniem jest autovivification pragmy (dostępny z CPAN)

#!/usr/bin/perl 

use strict; 
use warnings; 

use Data::Dumper; 

$Data::Dumper::Useqq = 1; 

{ 
    my %h; 

    if (exists $h{a}{b}{c}) { 
     print "impossible, it is empty\n"; 
    } 

    print Dumper \%h; 
} 

{ 
    no autovivification; 

    my %h; 

    if (exists $h{a}{b}{c}) { 
     print "impossible, it is empty\n"; 
    } 

    print Dumper \%h; 
} 

Trzecia metoda ysth wymienia w komentarzach ma zalety są w rdzeniu (tak jak w pierwszym przykładzie) i nie powtarzanie wywołania funkcji exists; Wierzę jednak, że robi to kosztem czytelności:

if (exists ${ ${ $h{a} || {} }{b} || {} }{c}) { 
    ... 
} 

Działa poprzez zastąpienie jakiegokolwiek poziomu, który nie istnieje w hashref wziąć autovivification. Obiekty te zostaną odrzucone po wykonaniu instrukcji if. Ponownie widzimy wartość logiki zwarciowej.

Oczywiście wszystkie trzy metody przyjmują założenie o danych, których oczekuje się od skrótu, bardziej niezawodna metoda obejmuje wywołania ref lub reftype w zależności od tego, jak chcesz traktować obiekty (jest trzecia opcja, bierze pod uwagę, że zajęcia obciążać hash operatora indeksowania, ale nie mogę sobie przypomnieć jego nazwy):

if (
    exists $h{a}   and 
    ref $h{a} eq ref {} and 
    exists $h{a}   and 
    ref $h{a}{b} eq ref {} and 
    exists $h{a}{b}{c} 
) { 
    ... 
} 

w komentarzach, PST pytanie, czy istnieje coś takiego jak myExists($ref,"a","b","c"). Jestem pewien, że w CPAN jest moduł, który robi coś takiego, ale nie jestem tego świadomy. Jest zbyt wiele przypadków, które mogłyby mi się przydać, ale prosta implementacja to:

#!/usr/bin/perl 

use strict; 
use warnings; 

use Data::Dumper; 

sub safe_exists { 
    my ($ref, @keys) = @_; 

    for my $k (@keys) { 
     return 0 unless ref $ref eq ref {} and exists $ref->{$k}; 
     $ref = $ref->{$k}; 
    } 
    return 1; 
} 

my %h = (
    a => { 
     b => { 
      c => 5, 
     }, 
    }, 
); 

unless (safe_exists \%h, qw/x y z/) { 
    print "x/y/z doesn't exist\n"; 
} 

unless (safe_exists \%h, qw/a b c d/) { 
    print "a/b/c/d doesn't exist\n"; 
} 

if (safe_exists \%h, qw/a b c/) { 
    print "a/b/c does exist\n"; 
} 

print Dumper \%h; 
+1

trzeci sposób: 'istnieje $ {$ {$ myhash {$ val} || {}} {$ plik} || {}} {$ funct} ' – ysth

+0

I sposób' myExists ($ ref, "a", "b", "c") '? :) –

Powiązane problemy