106

哪个是遍历 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 ;
4

6 回答 6

83
  • 在速度方面:#1 和#4,但在大多数情况下并没有太多。

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

    #5 可能类似。

  • 在内存使用方面:除了#5之外,它们都相同。

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

  • 在可读性方面:#1。

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

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

于 2012-05-07T20:00:05.837 回答
31

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

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

或者

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

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

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

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

如果你需要循环体中的元素和它的索引,我希望使用each 成为最快的,但随后您将放弃与 pre-5.12.1perl的兼容性。

在某些情况下,其他一些模式可能是合适的。

于 2012-05-07T23:19:56.110 回答
3

IMO,实现#1 是典型的,并且对于 Perl 来说,它的简短和惯用仅凭这一点就胜过其他实现。至少,这三种选择的基准可以让您深入了解速度。

于 2012-05-07T19:03:48.650 回答
2

1 与 2 和 3 大不相同,因为它使数组保持完整,而其他两个则使数组为空。

我会说#3 很古怪而且可能效率较低,所以忘记这一点。

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

undef @Array;

完成后。

  • 注意:包含数组范围的子程序实际上保留了数组并在下次重新使用空间。一般来说,这应该没问题(见评论)。
于 2012-05-07T19:01:03.847 回答
2

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

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

于 2018-02-01T11:53:58.793 回答
1

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

打印 $_ for (@array);

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

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

输出:2 4 6

于 2017-03-30T09:09:59.870 回答