Perl: while ($ key = each% hash) не останавливается на ключе = 0

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

Ответ 1

Вы вызываете 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.

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

Ответ 2

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

Ответ 3

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

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