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

perl

433 просмотра

4 ответа

Я использую, 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итератора?


Это также предполагает, что вы не можете отлаживать хеш внутри 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 за предложение, которое сделало решение намного проще.

Автор: mob Источник Размещён: 12.11.2019 09:10

Ответы (4)


9 плюса

Решение

Вы пробовали Storable's dclone скопировать его? Вероятно, будет что-то вроде этого:

use Storable qw(dclone);
my %hash_copy = %{ dclone( \%hash ) };
Автор: gpojd Размещён: 06.06.2012 08:16

2 плюса

Насколько велик этот хэш? Сколько времени нужно, чтобы пройти через это, так что вы заботитесь о времени доступа?

Просто установите флаг и выполните действие после окончания итерации:

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код распечатки, если только вы не планировали сортировку по ключу или чему-то еще.

Автор: Mark Reed Размещён: 06.06.2012 08:07

1 плюс

Давайте не будем забывать, что keys %hashэто уже определено при входе в whileцикл. Можно было просто сохранить ключи в массив для дальнейшего использования:

my @keys = keys %hash;

while (my ($key,$val) = each %hash) {

    if (something_interesting_happens()) {

        print "$_ => $hash{$_}\n" for @keys;
    }
}

Даунсайд:

  • Это менее элегантно (субъективно)
  • Это не будет работать, если %hashбудет изменено (но тогда зачем использовать eachв первую очередь?)

Потенциал роста:

  • Он использует меньше памяти, избегая хэш-копирования
Автор: Zaid Размещён: 07.06.2012 06:13

1 плюс

На самом деле, нет. eachневероятно хрупкий Он хранит состояние итерации в самом итерированном хэше, состояние, которое используется другими частями perl, когда им это необходимо. Гораздо безопаснее забыть о том, что он существует, и всегда итерировать свой собственный список из результата keys %hashвместо этого, потому что состояние итерации по списку хранится лексически как часть самого forцикла, поэтому он защищен от повреждения другими вещами.

Автор: LeoNerd Размещён: 17.06.2012 03:03
Вопросы из категории :
32x32