2008-10-21 5 views
10

Oczywiście tego nie potrzebuję; Po prostu ciekawi mnie, co tu się dzieje. Czy mi brakuje czegoś prostego? Czy mogę liczyć na to zachowanie we wszystkich wersjach Perl)

Perl v5.8.8:

%h = (0=>'zero', 1=>'one', 2=>'two'); 
while ($k = each %h) { 
    $v = delete $h{$k}; 
    print "deleted $v; remaining: @h{0..2}\n"; 
} 

wyjścia

deleted one; remaining: zero two 
deleted zero; remaining: two 
deleted two; remaining: 

man perlfunc (każdy) nie wyjaśnia, dlaczego natomiast pętla kontynuuje gdy $k jest przypisany 0. Kod zachowuje się tak, jakby stan na while pętli był ($k = each %h, defined $k).

Jeśli warunek pętli jest faktycznie zmieniony ($k = each %h, $k) następnie robi rzeczywiście przystanku przy $k = 0 jak oczekiwano.

zatrzymuje się również na $k = 0 na następny reimplementacja each:

%h = (0=>'zero', 1=>'one', 2=>'two'); 
sub each2 { 
    return each %{$_[0]}; 
} 
while ($k = each2 \%h) { 
    $v = delete $h{$k}; 
    print "deleted $v; remaining: @h{0..2}\n"; 
} 

wyjść tylko:

deleted one; remaining: zero two 

Odpowiedz

29

Dzwonisz each w kontekście skalarnym, więc to nie działa ze względu na wyświetl wartość zwracaną.

Podobnie jak

while ($line = <FILE>) 

jest specjalnym bocznym dodać niejawny defined, więc jest

while ($key = each %hash) 

W 5.8.8, co dzieje się w op.c, linie 3760-3766:

case OP_SASSIGN: 
    if (k1->op_type == OP_READDIR 
     || k1->op_type == OP_GLOB 
     || (k1->op_type == OP_NULL && k1->op_targ == OP_GLOB) 
     || k1->op_type == OP_EACH) 
    expr = newUNOP(OP_DEFINED, 0, expr); 
    break; 

Nie jestem pewien, czy dotyczy to wszystkich wersji Perla 5.

Zobacz także: When does while() test for defined vs truth na PerlMonks. Nie mogę znaleźć, gdzie jest to wspomniane w dokumentach Perla (wspomniany jest przypadek <FILE>, ale nie widzę przypadku each).

0

Dzięki, Cjm. Było jasne, że pewne implicite dodawanie defined działało w ten sposób dla globu, ale nie w miejscu, w którym zostało udokumentowane . Teraz przynajmniej znam ograniczone przypadki, w których zastosowanie ma specjalna obsługa.

Ale informacje powinny znajdować się w dokumentacji perlfunc, nie tylko w kodzie źródłowym Perla!

6

cjm ma rację. Chcę tylko dodać, że w przypadku takich dziwnych rzeczy często pomocne jest uruchamianie kodu przez B::Deparse, aby zobaczyć, jak Perl zrozumiał twój kod. Chciałbym użyć przełącznika -p, aby pokazać błędy pierwszeństwa.

$ perl -MO=Deparse,p your_example.plx 
(%h) = (0, 'zero', 1, 'one', 2, 'two'); 
while (defined($k = each %h)) { 
    $v = delete $h{$k}; 
    print "deleted $v; remaining: @h{0..2}\n"; 
} 
your_example.plx syntax OK