Что самый безопасный способ для перебора ключей хеша Perl?

голоса
84

Если у меня есть хэш Perl с кучей пар (ключ, значение), что является предпочтительным методом перебора всех ключей? Я слышал , что использование eachможет , каким - то образом имеют непредсказуемые побочные эффекты. Таким образом, это правда, и это один из двух следующих методов лучших, или есть способ лучше?

# Method 1
while (my ($key, $value) = each(%hash)) {
    # Something
}

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}
Задан 06/08/2008 в 03:53
источник пользователем
На других языках...                            


9 ответов

голоса
170

Правило заключается в том, чтобы использовать функцию наиболее подходящую для ваших нужд.

Если вы просто хотите , ключи и не планируете когда - нибудь прочитать любой из значений, используйте клавиши ():

foreach my $key (keys %hash) { ... }

Если вы просто хотите, чтобы значения, используйте значение ():

foreach my $val (values %hash) { ... }

Если вам нужны ключи и значения, использовать каждый ():

keys %hash; # reset the internal iterator so a prior each() doesn't affect the loop
while(my($k, $v) = each %hash) { ... }

Если вы планируете изменить ключи хеша любым способом , за исключением для удаления текущего ключа во время итерации, то вы не должны использовать каждый (). Например, этот код , чтобы создать новый набор заглавных ключей с удвоенными значениями отлично работает с помощью клавиш ():

%h = (a => 1, b => 2);

foreach my $k (keys %h)
{
  $h{uc $k} = $h{$k} * 2;
}

получения ожидаемого в результате хэш:

(a => 1, A => 2, b => 2, B => 4)

Но, используя каждый (), чтобы сделать то же самое:

%h = (a => 1, b => 2);

keys %h;
while(my($k, $v) = each %h)
{
  $h{uc $k} = $h{$k} * 2; # BAD IDEA!
}

производит неправильные результаты в труднодоступных предсказать способы. Например:

(a => 1, A => 2, b => 2, B => 8)

Это, однако, является безопасным:

keys %h;
while(my($k, $v) = each %h)
{
  if(...)
  {
    delete $h{$k}; # This is safe
  }
}

Все это описано в документации PERL:

% perldoc -f keys
% perldoc -f each
Ответил 06/08/2008 в 14:22
источник пользователем

голоса
21

Одна вещь , которую вы должны знать при использовании в eachтом , что она имеет побочный эффект добавления «состояние» в ваш хэш (хэш должен помнить , что «следующий» ключ). При использовании кода , как в фрагментах , размещенные выше, которые итерацию по всей хэш в одном дыхании, это обычно не является проблемой. Тем не менее, вы столкнетесь с трудно отследить проблемы (я говорю из опыта;), при использовании eachвместе с операторами , как lastи returnдо выхода из while ... eachцикла , прежде чем вы обработали все ключи.

В этом случае, хэш будет помнить , какие ключи она уже вернулась, а также при использовании eachна нем в следующий раз (возможно , в несвязанной суммарно кусок кода), он будет продолжать в этом положении.

Пример:

my %hash = ( foo => 1, bar => 2, baz => 3, quux => 4 );

# find key 'baz'
while ( my ($k, $v) = each %hash ) {
    print "found key $k\n";
    last if $k eq 'baz'; # found it!
}

# later ...

print "the hash contains:\n";

# iterate over all keys:
while ( my ($k, $v) = each %hash ) {
    print "$k => $v\n";
}

Это печатает:

found key bar
found key baz
the hash contains:
quux => 4
foo => 1

Что случилось с ключами «бар» и БАЗ "? Они все еще там, но вторые eachстартами , где первый один левыми прочь, и останавливается , когда он достигает конец хэша, поэтому мы никогда не видим их во втором цикле.

Ответил 16/09/2008 в 00:37
источник пользователем

голоса
19

Место , где eachможет вызвать проблемы в том , что это правда, не Scoped итератора. В качестве примера:

while ( my ($key,$val) = each %a_hash ) {
    print "$key => $val\n";
    last if $val; #exits loop when $val is true
}

# but "each" hasn't reset!!
while ( my ($key,$val) = each %a_hash ) {
    # continues where the last loop left off
    print "$key => $val\n";
}

Если вам нужно , чтобы быть уверенным , что eachполучает все ключи и значения, вы должны убедиться , что вы используете keysили valuesпервый (как это сбрасывает итератор). Смотрите документацию для каждого .

Ответил 16/09/2008 в 15:35
источник пользователем

голоса
13

Использование каждого синтаксиса позволит предотвратить весь набор ключей от генерируется сразу. Это может быть важно, если вы используете галстук-й изд хэш в базу данных с миллионами строк. Вы не хотите, чтобы генерировать весь список ключей сразу и исчерпать свою физическую память. В этом случае каждая служит итератор, тогда как на самом деле ключами генерирует весь массив перед началом цикла.

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

Если память не является проблемой, как правило, карта или ключи парадигме является более prevelant и легче читать парадигму.

Ответил 11/09/2008 в 23:04
источник пользователем

голоса
5

Несколько разные мысли по этой теме:

  1. Там нет ничего небезопасных о каких - либо хэш самих итераторов. Что небезопасно является изменение ключи хеша пока вы итерацию над ним. (Это совершенно безопасно для изменения значений.) Единственный потенциальный побочный эффект , который я могу думать о том , что valuesвозвращает псевдонимы , что означает , что изменение их будет изменять содержимое хэш. Это дизайн , но не может быть то , что вы хотите в некоторых обстоятельствах.
  2. Джон принял ответ хорош с одним исключением: в документации ясно , что это не безопасно , чтобы добавить ключи в то время как итерация хэш. Это может работать для некоторых наборов данных , но не получится для других в зависимости от того , хэша.
  3. Как уже отмечалось, это безопасно удалить последний ключ , возвращаемый each. Это не верно для , keysкак eachэто итератор , а keysвозвращает список.
Ответил 15/09/2008 в 22:36
источник пользователем

голоса
4

Я всегда использую метод 2, а также. Единственная польза от использования каждого, если вы просто читать (а не переназначения) значение хэш-записи, вы постоянно не де-ссылки на хэш.

Ответил 06/08/2008 в 05:04
источник пользователем

голоса
4

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

Все, что сказал, я почти всегда использую клавиши (), потому что для меня это, как правило, более документировано для доступа к значению этого ключа с помощью самого хэша. Я иногда использовать значение (), когда значение является ссылкой на большую структуру и ключ к хэшу уже хранится в структуре, и в этот момент ключ является излишним и не нужен. Я думаю, что я использовал каждый () 2 раза в 10 лет программирования Perl, и это было, вероятно, неправильный выбор как раз =)

Ответил 06/08/2008 в 04:43
источник пользователем

голоса
1

Я обычно использую , keysи я не могу думать о последний раз я использовал или прочитать использование each.

Не забывайте о том map, в зависимости от того, что вы делаете в петлю!

map { print "$_ => $hash{$_}\n" } keys %hash;
Ответил 22/08/2008 в 16:31
источник пользователем

голоса
-2

Я woudl сказать:

  1. Используйте то, что проще всего прочитать / понять для большинства людей (так ключи, как правило, я бы поспорил)
  2. Используйте то, что вы решили последовательно Повсеместно всей кодовой базы.

Это дает 2 основных преимущества:

  1. Это легче обнаружить «общий» код, так что вы можете повторно фактор в функции / methiods.
  2. Это проще для будущих разработчиков поддерживать.

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

Ответил 20/12/2010 в 13:46
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more