Могу ли я скопировать хеш без сброса его «каждого» итератора?
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 за предложение, которое сделало решение намного проще.
Ответы (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
код распечатки, если только вы не планировали сортировку по ключу или чему-то еще.
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
в первую очередь?)
Потенциал роста:
- Он использует меньше памяти, избегая хэш-копирования
1 плюс
На самом деле, нет. each
невероятно хрупкий Он хранит состояние итерации в самом итерированном хэше, состояние, которое используется другими частями perl, когда им это необходимо. Гораздо безопаснее забыть о том, что он существует, и всегда итерировать свой собственный список из результата keys %hash
вместо этого, потому что состояние итерации по списку хранится лексически как часть самого for
цикла, поэтому он защищен от повреждения другими вещами.
Вопросы из категории :
- perl Как определить, имеет ли переменная число в Perl?
- perl Как я могу проверить STDIN без блокировки в Perl?
- perl Как выполнить подстановку Perl для строки, сохранив оригинал?
- perl Какой самый простой способ установить отсутствующий модуль Perl?
- perl Что происходит с Perl 6?
- perl Как запустить интерактивную консоль для Perl?
- perl Как я могу создать XML из Perl?
- perl Какой лучший движок XSLT для Perl?
- perl Что "select ((select (s), $ | = 1) [0])" делает в Perl?
- perl Как я могу получить длину строки в Perl?
- perl Как получить список стеков вызовов в Perl?
- perl How can I check if I have a Perl module before using it?
- perl Как я могу использовать новый модуль Perl без разрешения на установку?
- perl Есть ли в Perl PHP-подобные динамические переменные?
- perl Что делает shift () в Perl?
- perl Почему прототипы функций Perl 5 плохие?
- perl Как объединить хэши в Perl?
- perl Как я могу получить букву диска в Perl?
- perl Как использовать переменную на стороне замены оператора подстановки Perl?
- perl Вы предпочитаете "if (var)" или "if (var! = 0)"?