遍历Perl数组的最佳方法


94

在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可接受的答案?等等)
Max Lybbert,2012年

2
您发布的三分之二会让我走“ WTH ?!” 除非有其他周围环境使它们成为明智的选择。无论如何,这个问题都在“ 加两个数字的最佳方法是什么? ” 的级别上,大多数时候,只有一种方法。然后,在某些情况下,您需要使用其他方法。投票关闭。
锡南·努尔(SinanÜnür)2012年

4
@SinanÜnür我很赞同您的观点(只有一种方法可以将两个数字相加),但是这种类比还不够强,不能随意使用。显然,有不止一种方法,OP希望了解什么是好主意,什么不是。
CodeClown42 2012年

2
《 Perl Perl编程》第三版的第24章中有一个关于效率的部分,该部分很不错。它解决了效率的不同类型,例如时间,程序员,维护人员。本节以“请注意,优化时间有时会占用您的空间或程序员的效率(由下面的相互矛盾的提示表示)。这就是中断”。

1
一种1将两个数字相加的方法?如果你不考虑较低级别的呼叫/实现....想先行进位,进位保存加法器等
workwise

Answers:


76
  • 就速度而言:#1和#4,但在大多数情况下并没有太多。

    您可以编写一个基准进行确认,但是我怀疑您会发现#1和#4会稍快一些,因为迭代工作是在C而不是Perl中完成的,并且不会发生不必要的数组元素复制。($_混叠到元件在#1,但#2和#3上实际复制的标量从阵列)。

    #5可能相似。

  • 就内存使用而言:除了#5之外,它们都是相同的。

    for (@a)是特殊情况,以避免使数组变平。循环遍历数组的索引。

  • 在可读性方面:#1。

  • 在灵活性方面:#1 /#4和#5。

    #2不支持错误的元素。#2和#3具有破坏性。


3
哇,您用简短的句子添加了大量的信息。
jaypal singh 2014年

1
当您进行队列时(例如,广度优先搜索),#2很好:my @todo = $root; while (@todo) { my $node = shift; ...; push @todo, ...; ...; }
ikegami 2015年

实现4不会创建索引的中间数组,这可能会引入要使用的大量内存吗?如果是这样,听起来好像不应该使用这种方法。stackoverflow.com/questions/6440723/... rt.cpan.org/Public/Bug/Display.html?id=115863
托尔斯滕Schöning

@ikegami忠实于您的冠军风格-很棒的答案:)
skeetastax

26

如果您只关心的元素@Array,请使用:

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

要么

如果索引很重要,请使用:

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

或者,从perl5.12.1开始,您可以使用:

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

如果您在循环体中同时需要元素及其索引, 我希望 使用 each 成为最快的,但是您将放弃与5.12.1之前的兼容性perl

在某些情况下,除这些模式以外的其他模式可能更合适。


我希望这each是最慢的。它执行其他所有工作,减去一个别名,再加上一个列表分配,两个标量副本和两个标量清除。
ikegami

而且,就我的测量能力而言,您是正确的。约45%具有更快的for遍历的阵列的指数,以及20%的速度,当迭代数组引用(I做访问的索引$array->[$i]在体内),并且使用each与结合while
SinanÜnür'12

3

IMO,实施方案1是典型的,Perl简短而惯用,仅此一项就胜过其他。这三个选择的基准至少可以使您深入了解速度。


2

1与2和3实质上不同,因为它完好无损地保留了数组,而其他两个则使它为空。

我想说的是#3很古怪,可能效率不高,所以就算了。

剩下的就是#1和#2,而它们却做不到相同的事情,因此,一个不能比另一个更好。如果数组很大并且不需要保留它,则通常范围会处理它(但请参阅 NOTE),因此通常,#1仍然是最清晰,最简单的方法。关闭每个元素不会加快任何速度。即使有必要将数组从引用中释放出来,我也要去:

undef @Array;

完成时。

  • 注意:包含数组范围的子例程实际上保留了数组并在下次再次使用该空间。通常,应该没问题(请参阅评论)。

@Array = ();不释放基础数组。甚至超出范围都不会做到这一点。如果要释放基础数组,则可以使用undef @Array;
ikegami 2012年

2
演示 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
ikegami 2012年

什么??? 我以为GC的全部要点是引用计数== 0,所涉及的内存就可以回收了。
CodeClown42

@ikegami:我看到了()vs的问题undef,但是如果超出范围并没有释放该范围本地数组使用的内存,那是否会使perl成为泄漏的灾难?那不是真的
CodeClown42 2012年

他们也不泄漏。子程序仍然拥有它们,并且下次调用该子程序时将重用它们。优化速度。
ikegami,2012年

1

在单行中打印元素或数组。

打印$ _为(@array);

注意:请记住,$ _在循环中内部引用@array的元素。$ _中所做的任何更改将反映在@array中; 例如

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

输出:2 4 6


0

确定此类问题以对其进行基准测试的最佳方法:

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,
});

并在为x86_64-linux-gnu-thread-multi构建的perl 5,版本24,版本1(v5.24.1)上运行

我得到:

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还指出,除了速度以外,这些实现方式还有很多差异。


1
比较$index < $#array实际上应该是$index <= $#array因为$#array不是数组的长度,而是数组的最后一个索引。
josch
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.