2008-10-21 9 views
10

Мне это не нужно, очевидно; Мне просто интересно, что здесь происходит. Я пропустил что-то простое? Могу ли я рассчитывать на такое поведение во всех версиях 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"; 
} 

выходы

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

man perlfunc (каждый) не объясняет, почему в то время как цикл продолжается когда $k присваивается 0. Код ведет себя так, как будто условие на петле while было ($k = each %h, defined $k).

Если условие цикла фактически изменяется на ($k = each %h, $k) то он делает действительно остановки в $k = 0, как и ожидалось.

Он также останавливается на $k = 0 для следующего переопределения 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"; 
} 

выходов всего:

deleted one; remaining: zero two 

ответ

29

Вы звоните each в скалярном контексте, поэтому он не работает из-за возвращаемое значение.

Так же, как

while ($line = <FILE>) 

специальна-обсаженный добавить неявную defined, так

while ($key = each %hash) 

В 5.8.8, что происходит в op.c, строки 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; 

Я не уверен, что это относится ко всем версиям Perl 5.

См. Также: When does while() test for defined vs truth на PerlMonks. Я не могу найти, где это упоминается в документах Perl (упоминается случай <FILE>, но я не вижу случая each).

0

Спасибо, cjm. Было ясно, что какое-то неявное добавление defined происходило так же, как и для glob, но не там, где было документально. Теперь, по крайней мере, я знаю ограниченные случаи, в которых применяется специальная обработка.

Но информация должна быть в документации perlfunc, не только исходным кодом Perl!

6

cjm является правильным. Я просто хочу добавить, что, когда вы сталкиваетесь с такими странными вещами, это часто помогает запустить ваш код через B::Deparse, чтобы узнать, как Perl понял ваш код. Мне нравится использовать -p-переключатель, чтобы отображать ошибки приоритета.

$ 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