Могу ли я скопировать хеш, не сбрасывая его итератор «каждый»?

Я использую 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в контексте списка приводит к сбросу его 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?


Это также говорит о том, что вы не можете отлаживать хэш внутри каждойитерации.Рассмотрите возможность запуска отладчика на:

%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 за предложение, которое значительно упростило решение.

15
задан mob 7 June 2012 в 22:47
поделиться