Ответ 1
Вы пробовали Storable dclone
, чтобы скопировать его? Вероятно, это будет примерно так:
use Storable qw(dclone);
my %hash_copy = %{ dclone( \%hash ) };
Я использую 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 за предложение, которое сделало решение намного проще.
Вы пробовали Storable dclone
, чтобы скопировать его? Вероятно, это будет примерно так:
use Storable qw(dclone);
my %hash_copy = %{ dclone( \%hash ) };
Насколько велика эта хэш? Сколько времени требуется для прохождения через него, чтобы вы заботились о сроках доступа?
Просто установите флаг и выполните действие после окончания итерации:
my $print_it;
while (my ($key,$val) = each %hash) {
$print_it = 1 if something_interesting_happens();
...
}
if ($print_it) {
foreach my $k (keys %hash) { print "$k => $hash{$k}\n" }
}
Хотя нет причин не использовать each
в коде распечатки, если вы не планируете сортировку по ключу или что-то в этом роде.
Не забывайте, что keys %hash
уже определен при вводе цикла while
. Можно было просто сохранить ключи в массив для последующего использования:
my @keys = keys %hash;
while (my ($key,$val) = each %hash) {
if (something_interesting_happens()) {
print "$_ => $hash{$_}\n" for @keys;
}
}
Даунсайд:
%hash
будет изменен (но тогда зачем использовать each
в первую очередь?)Потенциал роста:
Не совсем. each
невероятно хрупкий. Он сохраняет итерационное состояние на итерированном хеше, состояние, которое повторно используется другими частями perl, когда оно в этом нуждается. Гораздо безопаснее забыть, что он существует, и всегда перебирает свой собственный список из результата keys %hash
, потому что состояние итерации по списку хранится лексически как часть самого цикла for
, поэтому оно не защищено от другие вещи.