Лучший способ перебрать массив Perl
Какова лучшая реализация (с точки зрения скорости и использования памяти) для перебора массива 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 ;
6 ответов
С точки зрения скорости: № 1 и № 4, но не намного в большинстве случаев.
Вы можете написать тест для подтверждения, но я подозреваю, что вы найдете # 1 и # 4 немного быстрее, потому что итерационная работа выполняется в C вместо Perl, и не происходит ненужного копирования элементов массива. (
$_
связан с элементом в #1, но #2 и #3 фактически копируют скаляры из массива.)# 5 может быть похожим.
С точки зрения использования памяти: они все одинаковые, кроме #5.
for (@a)
имеет специальный корпус, чтобы избежать сплющивания массива. Цикл перебирает индексы массива.С точки зрения читабельности: #1.
С точки зрения гибкости: № 1/ № 4 и № 5.
#2 не поддерживает ложные элементы. № 2 и № 3 разрушительны.
Если вы заботитесь только об элементах @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.
Какой-то другой шаблон, кроме этого, может быть подходящим при определенных обстоятельствах.
IMO, реализация #1 типична и коротка и идиоматична для Perl, и она опережает остальных. Тест из трех вариантов может предложить вам, по крайней мере, понимание скорости.
Лучший способ решить подобные вопросы, чтобы сравнить их:
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 также указывает на то, что существует не так много различий в этих значениях, кроме скорости.
В одну строку вывести элемент или массив.
выведите $_ for (@array);
ПРИМЕЧАНИЕ: помните, что $ _ внутренне ссылается на элемент @array в цикле. Любые изменения, сделанные в $ _, будут отражены в @array; ех.
my @array = qw( 1 2 3 );
for (@array) {
$_ = $_ *2 ;
}
print "@array";
выход: 2 4 6
1 существенно отличается от 2 и 3, поскольку он оставляет массив в такте, тогда как два других оставляют его пустым.
Я бы сказал, что #3 довольно дурацкий и, вероятно, менее эффективный, так что забудьте об этом.
Что оставляет вас с № 1 и № 2, и они не делают одно и то же, поэтому один не может быть "лучше", чем другой. Если массив большой, и вам не нужно его хранить, обычно с ним справится область действия (но см. ПРИМЕЧАНИЕ), поэтому, как правило, #1 по-прежнему является наиболее простым и понятным методом. Отключение каждого элемента ничего не ускорит. Даже если есть необходимость освободить массив от ссылки, я бы просто пошел:
undef @Array;
когда сделано.
- ПРИМЕЧАНИЕ. Подпрограмма, содержащая область действия массива, фактически сохраняет массив и повторно использует пространство в следующий раз. Как правило, это должно быть хорошо (см. Комментарии).