Какая реализация (с точки зрения скорости и использования памяти) для перебора массива 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 ;
map
приемлемый ответ? И т. Д.)Ответы:
По скорости: №1 и №4, но в большинстве случаев не намного.
Вы можете написать тест для подтверждения, но я подозреваю, что вы обнаружите, что №1 и №4 будут немного быстрее, потому что итерационная работа выполняется на C вместо Perl, и не происходит ненужного копирования элементов массива. (
$_
имеет псевдоним элемента в №1, но №2 и №3 фактически копируют скаляры из массива.)№5 может быть похожим.
Что касается использования памяти: все они одинаковы, за исключением №5.
for (@a)
имеет специальный корпус, чтобы избежать сплющивания массива. Цикл перебирает индексы массива.По удобочитаемости: №1.
По гибкости: №1 / №4 и №5.
# 2 не поддерживает ложные элементы. №2 и №3 деструктивны.
источник
my @todo = $root; while (@todo) { my $node = shift; ...; push @todo, ...; ...; }
Если вас интересуют только элементы
@Array
, используйте:for my $el (@Array) { # ... }
или
Если индексы имеют значение, используйте:
for my $i (0 .. $#Array) { # ... }
Или,
perl
начиная с 5.12.1, вы можете использовать:while (my ($i, $el) = each @Array) { # ... }
Если вам нужен и элемент, и его индекс в теле цикла,
Я ожидалс помощьюeach
быть самым быстрым, но тогдавы откажетесь от совместимости с версией до 5.12.1perl
.При определенных обстоятельствах может быть уместен другой шаблон, отличный от указанного.
источник
each
будет самым медленным. Он выполняет всю работу остальных за вычетом псевдонима, плюс назначение списка, две скалярные копии и две скалярные очистки.for
итерации по индексам массива и на 20% быстрее при итерации по индексам ссылки на массив (я выполняю доступ$array->[$i]
в теле), по сравнению с использованиемeach
в сочетании сwhile
.ИМО, реализация №1 типична, короткая и идиоматическая для Perl превосходит другие только в этом. По крайней мере, эталонный тест из трех вариантов может дать вам представление о скорости.
источник
1 существенно отличается от 2 и 3, поскольку оставляет массив неизменным, а два других оставляют его пустым.
Я бы сказал, что №3 довольно странный и, вероятно, менее эффективный, так что забудьте об этом.
В результате вы остаетесь с №1 и №2, а они не делают одно и то же, поэтому одно не может быть «лучше» другого. Если массив большой и вам не нужно его хранить, обычно с ним будет работать область видимости ( но см. ПРИМЕЧАНИЕ ), поэтому, как правило , №1 по-прежнему является самым ясным и простым методом. Отключение каждого элемента ничего не ускорит. Даже если есть необходимость освободить массив от ссылки, я бы просто пошел:
undef @Array;
когда сделано.
источник
@Array = ();
не освобождает базовый массив. Даже выход за рамки этого не сделает. Если вы хотите освободить базовый массив, вы должны использоватьundef @Array;
.perl -MDevel::Peek -e'my @a; Dump(\@a,1); @a=qw( a b c ); Dump(\@a,1); @a=(); Dump(\@a,1); undef @a; Dump(\@a,1);' 2>&1 | grep ARRAY
()
vsundef
, но если выход за пределы области действия не освобождает память, используемую локальным для этой области массивом, разве это не превращает Perl в утечку? Это не может быть правдой.В единственной строке вывести элемент или массив.
напечатать $ _ for (@array);
ПРИМЕЧАНИЕ: помните, что $ _ внутренне ссылается на элемент @array в цикле. Любые изменения, внесенные в $ _, будут отражены в @array; напр.
my @array = qw( 1 2 3 ); for (@array) { $_ = $_ *2 ; } print "@array";
вывод: 2 4 6
источник
Лучший способ решить подобные вопросы, чтобы сравнить их:
use strict; use warnings; use Benchmark qw(:all); our @input_array = (0..1000); my $a = sub { my @array = @{[ @input_array ]}; my $index = 0; foreach my $element (@array) { die unless $index == $element; $index++; } }; my $b = sub { my @array = @{[ @input_array ]}; my $index = 0; while (defined(my $element = shift @array)) { die unless $index == $element; $index++; } }; my $c = sub { my @array = @{[ @input_array ]}; my $index = 0; while (scalar(@array) !=0) { my $element = shift(@array); die unless $index == $element; $index++; } }; my $d = sub { my @array = @{[ @input_array ]}; foreach my $index (0.. $#array) { my $element = $array[$index]; die unless $index == $element; } }; my $e = sub { my @array = @{[ @input_array ]}; for (my $index = 0; $index <= $#array; $index++) { my $element = $array[$index]; die unless $index == $element; } }; my $f = sub { my @array = @{[ @input_array ]}; while (my ($index, $element) = each @array) { die unless $index == $element; } }; my $count; timethese($count, { '1' => $a, '2' => $b, '3' => $c, '4' => $d, '5' => $e, '6' => $f, });
И запускаем это на perl 5, версия 24, subversion 1 (v5.24.1), созданная для x86_64-linux-gnu-thread-multi
Я получил:
Benchmark: running 1, 2, 3, 4, 5, 6 for at least 3 CPU seconds... 1: 3 wallclock secs ( 3.16 usr + 0.00 sys = 3.16 CPU) @ 12560.13/s (n=39690) 2: 3 wallclock secs ( 3.18 usr + 0.00 sys = 3.18 CPU) @ 7828.30/s (n=24894) 3: 3 wallclock secs ( 3.23 usr + 0.00 sys = 3.23 CPU) @ 6763.47/s (n=21846) 4: 4 wallclock secs ( 3.15 usr + 0.00 sys = 3.15 CPU) @ 9596.83/s (n=30230) 5: 4 wallclock secs ( 3.20 usr + 0.00 sys = 3.20 CPU) @ 6826.88/s (n=21846) 6: 3 wallclock secs ( 3.12 usr + 0.00 sys = 3.12 CPU) @ 5653.53/s (n=17639)
Таким образом, «foreach (@Array)» примерно в два раза быстрее, чем другие. Все остальные очень похожи.
@ikegami также отмечает, что помимо скорости есть немало различий в этих имплементациях.
источник
$index < $#array
самом деле сравнение должно происходить,$index <= $#array
потому что$#array
это не длина массива, а его последний индекс.