Лучший способ перебрать массив Perl

80

Какая наилучшая реализация (с точки зрения скорости и использования памяти) для итерации через массив Perl? Есть ли лучший способ? (@Array не нужно сохранять).

Реализация 1

foreach (@Array)
{
      SubRoutine($_);
}

Реализация 2

while($Element=shift(@Array))
{
      SubRoutine($Element);
}

Реализация 3

while(scalar(@Array) !=0)
{
      $Element=shift(@Array);
      SubRoutine($Element);
}

Реализация 4

for my $i (0 .. $#Array)
{
      SubRoutine($Array[$i]);
}

Реализация 5

map { SubRoutine($_) } @Array ;
  • 2
    Почему бы быть «лучшим»? Особенно если учесть, что мы понятия не имеем, как бы вы сравнивали одно с другим (важнее ли скорость, чем использование памяти? map и приемлемый ответ? И т. Д.)
  • 2
    Два из трех, которые вы разместили, заставят меня сказать "WTH ?!" разве что в качестве дополнительного окружающего контекста, чтобы сделать их разумными альтернативами. В любом случае, этот вопрос находится на уровне « Как лучше всего добавить два числа? » В большинстве случаев есть только один способ. Тогда есть те обстоятельства, когда вам нужен другой путь. Голосование, чтобы закрыть.
Показать ещё 3 комментария
Теги:
arrays
iteration

5 ответов

58
Лучший ответ
  • С точки зрения скорости: # 1 и # 4, но не в большинстве случаев.

    Вы можете написать контрольный пример для подтверждения, но я подозреваю, что вы найдете # 1 и # 4 немного быстрее, потому что работа итерации выполняется в C вместо Perl, и не происходит ненужного копирования элементов массива. ($_ является псевдонимом элемента в # 1, но # 2 и # 3 фактически копируют скаляры из массива.)

    # 5 может быть аналогичным.

  • В терминах использования памяти: они все одинаковые, кроме # 5.

    for (@a) имеет специальную форму, чтобы избежать сглаживания массива. Цикл выполняет итерацию по индексам массива.

  • С точки зрения читаемости: # 1.

  • С точки зрения гибкости: # 1/# 4 и # 5.

    # 2 не поддерживает элементы, которые являются ложными. # 2 и # 3 являются разрушительными.

  • 3
    Вау, ты добавил кучу информации в короткие и простые предложения.
  • 1
    # 2 хорошо, когда вы делаете очереди (например, поиск в ширину): my @todo = $root; while (@todo) { my $node = shift; ...; push @todo, ...; ...; }
Показать ещё 1 комментарий
17

Если вам нужны только элементы @Array, используйте:

for my $el (@Array) {
# ...
}

или

Если значения имеют значение, используйте:

for my $i (0 .. $#Array) {
# ...
}

Или, начиная с perl 5.12.1, вы можете использовать:

while (my ($i, $el) = each @Array) {
# ...
}

Если вам нужен как элемент, так и его индекс в теле цикла, я бы ожидал, что, используя each , будет самым быстрым, но затем отказаться от совместимости с pre-5.12.1 perl s.

В некоторых случаях может быть уместен какой-то другой шаблон, чем они.

  • 0
    Я ожидаю, что each будет самым медленным. Он выполняет всю работу других, за исключением псевдонима, а также списка назначений, двух скалярных копий и двух скалярных очисток.
  • 0
    И, насколько мне известно, вы правы. Приблизительно на 45% быстрее for итерации по индексам массива и на 20% быстрее при итерации по индексам ссылки на массив (у меня есть доступ к $array->[$i] в теле), при использовании each в сочетании с while ,
3

IMO, реализация № 1 типична и короткая и идиоматическая для Perl превосходит остальных только для этого. Тест на три варианта может дать вам представление о скорости, по крайней мере.

2

1 существенно отличается от 2 и 3, так как он оставляет массив в такте, тогда как остальные два оставляют его пустым.

Я бы сказал, что №3 довольно дурацкая и, вероятно, менее эффективная, поэтому забудьте об этом.

Что оставляет вас С# 1 и # 2, и они не делают то же самое, поэтому нельзя быть "лучше", чем другой. Если массив большой, и вам не нужно его хранить, обычно область действия будет иметь дело с ним (но см. ПРИМЕЧАНИЕ), поэтому, как правило, # 1 по-прежнему является самым ясным и простым методом. Сдвиг каждого элемента не ускорит его. Даже если есть необходимость освободить массив из ссылки, я бы просто пошел:

undef @Array;

когда это сделано.

  • ПРИМЕЧАНИЕ. Подпрограмма, содержащая область массива, фактически сохраняет массив и повторно использует пробел в следующий раз. Как правило, это должно быть хорошо (см. Комментарии).
  • 0
    @Array = (); не освобождает базовый массив. Даже выход за рамки этого не сделает. Если бы вы хотели освободить базовый массив, вы бы использовали undef @Array; ,
  • 2
    Демо; perl -MDevel::Peek -e'my @a; Dump(\@a,1); @a=qw( abc ); Dump(\@a,1); @a=(); Dump(\@a,1); undef @a; Dump(\@a,1);' 2>&1 | grep ARRAY
Показать ещё 12 комментариев
0

В одной строке для печати элемента или массива.

print $_ for (@array);

ПРИМЕЧАНИЕ: помните, что $_ внутренне ссылается на элемент @array в цикле. Любые изменения, внесенные в $_, будут отражены в @array; ех.

my @array = qw( 1 2 3 );
for (@array) {
        $_ = $_ *2 ;
}
print "@array";

вывод: 2 4 6

Ещё вопросы

Сообщество Overcoder
Наверх
Меню