Стоит каждая функция perl использования?

От perldoc-f каждый мы читаем:

Существует единственный итератор для каждого хеша, совместно использованного всеми each, keys, и values вызовы функции в программе; это может быть сброшено путем чтения всех элементов из хеша, или путем оценки keys HASH или values HASH.

Итератор не сбрасывается при отъезде объема, содержащего each(), и это может привести к ошибкам:

my %h = map { $_, 1 } qw(1 2 3);
while (my $k = each %h) { print "1: $k\n"; last }
while (my $k = each %h) { print "2: $k\n"       }

Вывод:

1: 1
2: 3
2: 2

Каковы общие обходные решения для этого поведения? И действительно ли это стоит использовать each в целом?

12
задан Eugene Yarmash 7 March 2010 в 22:52
поделиться

8 ответов

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

while (my ($k,$v) = each %h) {
    say "$k = $v";
}

В вашем примере вы можете сбросить итератор, добавив ключей% h; следующим образом:

my %h = map { $_ => 1 } qw/1 2 3/;
while (my $k = each %h) { print "1: $k\n"; last }
keys %h;  # reset %h
while (my $k = each %h) { print "2: $k\n" }

Из Perl 5.12 каждый также разрешит итерацию по массиву.

10
ответ дан 2 December 2019 в 03:48
поделиться

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

Рассмотрим этот пример, в котором мы хотим сгруппировать наши пары k / v (да, я знаю, что printf подойдет лучше):

#!perl

use strict;
use warnings;

use Test::More 'no_plan';

{   my %foo = map { ($_) x 2 } (1..15);

    is( one( \%foo ), one( \%foo ), 'Calling one twice works with 15 keys' );
    is( two( \%foo ), two( \%foo ), 'Calling two twice works with 15 keys' );
}

{   my %foo = map { ($_) x 2 } (1..105);

    is( one( \%foo ), one( \%foo ), 'Calling one twice works with 105 keys' );
    is( two( \%foo ), two( \%foo ), 'Calling two twice works with 105 keys' );
}


sub one {
    my $foo = shift;

    my $r = '';

    for( 1..9 ) {
        last unless my ($k, $v) = each %$foo;

        $r .= "  $_: $k -> $v\n";
    }
    for( 10..99 ) {
        last unless my ($k, $v) = each %$foo;

        $r .= " $_: $k -> $v\n";
    }

    return $r;
}

sub two {
    my $foo = shift;

    my $r = '';

    my @k = keys %$foo;

    for( 1..9 ) {
        last unless @k;
        my $k = shift @k;

        $r .= "  $_: $k -> $foo->{$k}\n";
    }
    for( 10..99 ) {
        last unless @k;
        my $k = shift @k;

        $r .= "  $_: $k -> $foo->{$k}\n";
    }

    return $r;
}

Отладка ошибки, показанной в приведенных выше тестах, в реальном приложении могла бы быть ужасно болезненным. (Для лучшего вывода используйте Test :: Differences eq_or_diff вместо равно .)

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

Не знаю, как вы, но я просто буду использовать ключи и значения .

2
ответ дан 2 December 2019 в 03:48
поделиться

Лучше всего использовать его по названию: каждый. Возможно, это неправильное использование, если вы имеете в виду "дайте мне первую пару ключ-значение", или "дайте мне первые две пары", или что-то еще. Просто имейте в виду, что идея достаточно гибкая, чтобы каждый раз, когда вы ее вызываете, вы получали следующую пару (или ключ в скалярном контексте).

1
ответ дан 2 December 2019 в 03:48
поделиться

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

1
ответ дан 2 December 2019 в 03:48
поделиться

используйте функцию keys() для сброса итератора. Дополнительную информацию см. в faq

2
ответ дан 2 December 2019 в 03:48
поделиться

каждый слишком опасен для использования, и многие руководства по стилю полностью запрещают его использование. . Опасность заключается в том, что если цикл каждый прерывается до окончания хеширования, следующий цикл начнется там. Это может вызвать очень трудно воспроизводимые ошибки; поведение одной части программы будет зависеть от совершенно не связанной с ней другой части программы. Вы можете правильно использовать каждый , но как насчет каждого когда-либо написанного модуля, который может использовать ваш хэш (или hashref; это одно и то же)?

ключи и значения всегда безопасны, поэтому просто используйте их. keys в любом случае упрощают обход хеша в детерминированном порядке, что почти всегда более полезно. ( для моего $ key (sort keys% hash) {...} )

8
ответ дан 2 December 2019 в 03:48
поделиться

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

Ключи пустого контекста () (или значения, но согласованность - это хорошо) перед началом цикла - единственное необходимое «обходное решение»; есть ли причина, по которой вы ищете другое решение?

7
ответ дан 2 December 2019 в 03:48
поделиться

Я нахожу each очень удобным для таких идиом, как эта:

my $hashref = some_really_complicated_method_that_builds_a_large_and_deep_structure();
while (my ($key, $value) = each %$hashref)
{
    # code that does stuff with both $key and $value
}

Противопоставьте этот код этому:

my $hashref = ...same call as above
foreach my $key (keys %$hashref)
{
    my $value = $hashref->{$key};
    # more code here...
}

В первом случае и $key, и $value сразу доступны в теле цикла. Во втором случае сначала нужно получить $value. Кроме того, список ключей $hashref может быть очень большим, что занимает память. Это иногда является проблемой. each не несет таких накладных расходов.

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

8
ответ дан 2 December 2019 в 03:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: