Perl: while ($ key = каждый% хэш) не останавливается на key = 0 - PullRequest
10 голосов
/ 21 октября 2008

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

Ответы [ 3 ]

29 голосов
/ 21 октября 2008

Вы звоните 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.

См. Также: Когда делает while () проверять определенное против истины в PerlMonks. Я не могу найти, где это упоминается в документации по Perl (упоминается случай <FILE>, но я не вижу случай each).

6 голосов
/ 22 октября 2008

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
0 голосов
/ 21 октября 2008

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

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

...