Почему Perl каждый () перебирает весь хэш во второй раз? - PullRequest
6 голосов
/ 14 июля 2009

У меня есть простой скрипт, пытающийся узнать о хешах в Perl.

#!/usr/bin/perl

my %set = (
    -a => 'aaa',
    -b => 'bbb',
    -c => 'ccc',
    -d => 'ddd',
    -e => 'eee',
    -f => 'fff',
    -g => 'ggg'
);

print "Iterate up to ggg...\n";
while ( my ($key, $val) = each %set ) {
    print "$key -> $val \n";
    last if ($val eq 'ggg');
}
print "\n";

print "Iterate All...\n";
while ( my ($key, $val) = each %set ) {
    print "$key -> $val \n";
}
print "\n";

Я удивлен выводом: -

Iterate upto ggg...
-a -> aaa
-c -> ccc
-g -> ggg

Iterate All...
-f -> fff
-e -> eee
-d -> ddd
-b -> bbb

Я понимаю, что ключи хэшируются, поэтому первый вывод может быть 'n' элементами в зависимости от внутреннего порядка. Но почему я не могу просто потом зациклить массив? В чем дело ?

Спасибо

Ответы [ 2 ]

18 голосов
/ 14 июля 2009

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

Большинство людей избегают each по этой (и другим) причинам, вместо этого выбирая keys:

for my $key (keys %hash){
    say "$key => $hash{$key}";
}

Это также дает вам контроль над порядком итерации:

for my $key (sort keys %hash){
    say "$key => $hash{$key}";
}

В любом случае, если вы собираетесь закончить цикл раньше, избегайте each.

Кстати, сторонники функционального программирования должны воспользоваться этой возможностью, чтобы указать на недостатки скрытого состояния. То, что выглядит как операция без сохранения состояния («цикл по каждой паре в таблице»), на самом деле довольно полноразмерно.

10 голосов
/ 14 июля 2009

вы можете прочитать perldoc на каждом

perldoc -f each

Когда хеш полностью читается, в контексте списка возвращается нулевой массив (который при назначении выдает false (0) значение) и "undef" в скалярном контексте. Следующий вызов «каждому» после этого начнет повторяться снова. Есть грех gle итератор для каждого хеша, общий для всех вызовов функций «each», «keys» и «values» в программе; это может быть сброшено чтение всех элементов из хэша или вычисление «ключей HASH» или «значений HASH».

следовательно, вы можете использовать ключи%, установленные в вашем коде, для повторной итерации (благодаря вашему «последнему» утверждению)

print "Iterate upto ggg...\n";
while ( my ($key, $val) = each %set ) {
    print "$key -> $val \n";
    last if ($val eq 'ggg');
}
print "\n";
keys %set;
print "Iterate All...\n";
while ( my ($key, $val) = each %set ) {
    print "$key -> $val \n";
}
print "\n";
...