Хэш Perl: цикл while-each переходит в бесконечный цикл

Следующий код Perl запускает бесконечный цикл. Похоже, что each сбрасывает себя после вызова подпрограммы. Почему это так?

#!/usr/bin/perl
use warnings;
use strict;

my %h = ( "a" => "b" );
while ( my ($x, $y) = each %h ) {
    &do_something( \%h );
}

sub do_something(){
    my %tmp  = %{$_[0]};
}

Интересно, это работает:

while ( my ($x, $y) = each %h ) {
    &do_something( \%h );
}

sub do_something(){
}

Пока это не так:

while ( my ($x, $y) = each %h ) {
    &do_something( %h );
}

sub do_something(){
}

person Klaus    schedule 18.09.2012    source источник
comment
В качестве примечания: избегайте использования прототипов. Хотя они выглядят так, будто контролируют то, что принимает подпрограмма, на самом деле это не так. Они контролируют контекст аргументов и имеют очень ограниченное применение. Если вы этого не понимаете, не связывайтесь с ними. Если вам нужны аргументы подпрограммы, посмотрите Method::Signatures.   -  person Schwern    schedule 18.09.2012
comment
Спасибо за подсказку по поводу прототипов. Я не знал, что сделал прототип своей подпрограммы, написав ()   -  person Klaus    schedule 18.09.2012
comment
Вызов подпрограмм как &foo указывает Perl игнорировать прототипы, поэтому вы, возможно, не заметили этого раньше. Нет необходимости в &.   -  person Schwern    schedule 18.09.2012
comment
См. Hash::SafeKeys, который я придумал, когда У меня была такая же проблема   -  person mob    schedule 18.09.2012


Ответы (1)


Получение содержимого хеша путем его оценки в контексте списка использует тот же итератор, что и each/keys/values, что приводит к его сбросу.

Минимальная демонстрация:

>perl -E"%h=(a=>4); while (($k) = each(%h)) { say $k; keys %h }"
a
a
a
...

>perl -E"%h=(a=>4); while (($k) = each(%h)) { say $k; %t=%h }"
a
a
a
...

Поскольку вы оцениваете хеш только в контексте списка в первом и третьем фрагментах, итератор сбрасывается только в этих двух фрагментах.


PS Почему вы используете неверный прототип (()), а затем говорите Perl игнорировать его (&)?

person ikegami    schedule 18.09.2012
comment
Спасибо за объяснение! Вопрос: Вы говорите, что each/keys/values все итераторы? Я бы предположил, что each — это итератор, а keys и values — функции, которые возвращают список и не сбрасывают никаких итераторов, кроме each. Что касается прототипирования - спасибо за подсказку, я не знал, что определяю прототипы. - person Klaus; 18.09.2012
comment
Как вы думаете, как keys получает все ключи? Он должен перебирать элементы. Он использует тот же итератор, что и each. - person ikegami; 18.09.2012
comment
Реакция @ikegami Клауса совершенно правильная. Следует удивиться тому, что keys сбрасывает итератор each. Даже если они используют один и тот же итератор, keys может его восстановить. Это неочевидно и плохой дизайн. - person Schwern; 18.09.2012
comment
@Schwern, делать вид, что его реакция отличается от критики там, где она не может помочь? Не круто. Если то, что вы говорите, возможно, это неплохая идея (хотя keys в контексте void должен продолжать сбрасывать итератор), так почему бы вам не отправить отчет об ошибке, если не патч. - person ikegami; 18.09.2012
comment
@ikegami Я прочитал ваш ответ Клаусу с явным намеком на то, как могло быть по-другому, и, конечно же, вы могли бы понять это. - person Schwern; 18.09.2012
comment
@Schwern, опять же, его вопрос был не в том, почему keys и each используют один и тот же итератор?, а в том, что keys - это итератор? Так что да, я сказал, как может быть по-другому. У вас просто неправильно это. - person ikegami; 18.09.2012
comment
@ikegami Думаю, я прав. Есть много других способов получить ключи хеша Perl (и реализовать хэш). Простой цикл for по внутреннему массиву C, содержащему хэш-элементы, выполняет свою работу и, вероятно, будет быстрее. Я не знаю, почему они используют итератор и почему в API нет функции hv_keys для инкапсуляции реализации хэша. На первый взгляд, интуиция Клауса о том, как keys должен работать, имеет смысл. Это нестандартный способ Perl. Многое в Perl реализовано неожиданным образом для поддержки малоизвестных функций. - person Schwern; 19.09.2012
comment
@Schwerm, Re ‹Думаю, я прав› Извините, вы не правы. Я на 100% уверен, что отвечал на keys это итератор?. Никакая настойчивость или мысли об обратном этого не изменят. - person ikegami; 19.09.2012