Я использую each
для итерации через хеш Perl:
while (my ($key,$val) = each %hash) {
...
}
Затем происходит что-то интересное, и я хочу распечатать хэш. Сначала я считаю что-то вроде:
while (my ($key,$val) = each %hash) {
if (something_interesting_happens()) {
foreach my $k (keys %hash) { print "$k => $hash{$k}\n" }
}
}
Но это не сработает, потому что все знают, что вызов keys
(или values
) хэша сбрасывает внутренний итератор, используемый для each
, и мы можем получить бесконечный цикл. Например, эти скрипты будут работать вечно:
perl -e '%a=(foo=>1); while(each %a){keys %a}'
perl -e '%a=(foo=>1); while(each %a){values %a}'
Нет проблем, подумал я. Я мог бы сделать копию хэша и распечатать копию.
if (something_interesting_happens()) {
%hash2 = %hash;
foreach my $k (keys %hash2) { print "$k => $hash2{$k}\n" }
}
Но это тоже не работает. Это также сбрасывает итератор each
. Фактически любое использование %hash
в контексте списка кажется reset его итератором each
. Так что они тоже бегут:
perl -e '%a=(foo=>1); while(each %a){%b = %a}'
perl -e '%a=(foo=>1); while(each %a){@b = %a}'
perl -e '%a=(foo=>1); while(each %a){print %a}'
Насколько это документировано? Имеет смысл, что perl, возможно, потребуется использовать один и тот же внутренний итератор, чтобы направить содержимое хэша в стек возврата, но я также могу представить хэш-реализации, которые не нуждались в этом.
Что еще более важно, есть ли способ сделать то, что я хочу? Чтобы получить все элементы хэша без сброса итератора each
?
Это также говорит о том, что вы не можете отлаживать хэш внутри итерации each
. Рассмотрите возможность запуска отладчика:
%a = (foo => 123, bar => 456);
while ( ($k,$v) = each %a ) {
$DB::single = 1;
$o .= "$k,$v;";
}
print $o;
Просто просмотрев хэш, где останавливается отладчик (например, набрав p %a
или x %a
), вы измените вывод программы.
Обновление: Я загрузил Hash::SafeKeys
как общее решение этой проблемы. Спасибо @gpojd за то, что указали мне в правильном направлении и @cjm за предложение, которое сделало решение намного проще.