遍历 Perl 数组的最佳方法
Posted
技术标签:
【中文标题】遍历 Perl 数组的最佳方法【英文标题】:Best way to iterate through a Perl array 【发布时间】:2012-05-16 06:28:39 【问题描述】:遍历 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
和可接受的答案?等等)
你发布的三个中有两个会让我去“WTH?!”除非有额外的周围环境使它们成为明智的选择。无论如何,这个问题属于“两个数字相加最好的方法是什么?”大多数时候,只有一种方法。然后,在某些情况下,您需要一种不同的方式。投票结束。
@SinanÜnür 我赞同你的观点(只有一种方法可以将两个数字相加),但是这个类比不够强,不能轻视。显然,方法不止一种,OP 想要了解什么是好主意,什么不是。
Programming Perl 第三版的第 24 章有一节关于效率的内容值得一读。它解决了不同类型的效率,例如时间、程序员、维护者。该部分以“请注意,优化时间有时可能会降低空间或程序员效率的成本(由下面的冲突提示表示)开始。他们是休息时间。”
两个数字相加的一种方法?如果您研究较低级别的调用/实现,则不会……考虑进行前瞻,进行保存加法器等。
【参考方案1】:
在速度方面:#1 和 #4,但在大多数情况下不会太多。
您可以编写一个基准来确认,但我怀疑您会发现 #1 和 #4 会稍微快一些,因为迭代工作是在 C 而不是 Perl 中完成的,并且不会发生不必要的数组元素复制。 ($_
被 aliased 到 #1 中的元素,但 #2 和 #3 实际上 复制 数组中的标量。)
#5 可能类似。
就内存使用而言:除了 #5 之外,它们都相同。
for (@a)
是特殊情况以避免数组变平。循环遍历数组的索引。
在可读性方面:#1。
在灵活性方面:#1/#4 和 #5。
#2 不支持错误的元素。 #2 和 #3 具有破坏性。
【讨论】:
哇,您用简短的句子添加了大量信息。 #2 在你做队列时很好(例如广度优先搜索):my @todo = $root; while (@todo) my $node = shift; ...; push @todo, ...; ...;
实现 4 是否不会创建一个中间索引数组,这可能会引入大量要使用的内存?如果是这样,听起来不应该使用这种方法。 ***.com/questions/6440723/…rt.cpan.org/Public/Bug/Display.html?id=115863
@ikegami 忠于你的冠军风格 - 很好的答案:)【参考方案2】:
如果你只关心@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 的兼容性。
在某些情况下,其他一些模式可能更合适。
【讨论】:
我希望each
是最慢的。它完成了其他人的所有工作,减去一个别名、一个列表分配、两个标量副本和两个标量清除。
而且,就我的测量能力而言,你是对的。使用for
迭代数组的索引大约快 45%,迭代数组引用的索引时快 20%(我在正文中访问 $array->[$i]
),超过使用 each
和 @987654332 @.【参考方案3】:
IMO,实现#1 是典型的,对于 Perl 来说,它的简短和惯用仅在这一点上就胜过其他实现。至少,这三种选择的基准可以让您深入了解速度。
【讨论】:
【参考方案4】:1 与 2 和 3 大不相同,因为它使数组保持完整,而其他两个则保持空。
我会说#3 很古怪,而且可能效率较低,所以算了吧。
这让你有了#1 和#2,它们做的事情不一样,所以一个不能比另一个“更好”。如果数组很大并且您不需要保留它,一般范围会处理它(但请参阅 注意),所以 一般来说,#1 仍然是最清晰和最简单的方法。关闭每个元素不会加快任何速度。即使需要从引用中释放数组,我也会去:
undef @Array;
完成后。
注意:包含数组范围的子程序实际上保留了数组并在下次重新使用空间。 一般,应该没问题(见 cmets)。【讨论】:
@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
WHAT??? 我原以为 GC 的重点是一旦 ref count == 0,所涉及的内存就可以回收了。
@ikegami:我看到了关于()
和undef
的事情,但是如果超出范围并不会释放该范围内的本地数组使用的内存,那不是perl泄漏的灾难?这不可能是真的。
它们也不会泄漏。 sub 仍然拥有它们,并将在下次调用 sub 时重用它们。针对速度进行了优化。【参考方案5】:
决定此类问题以对其进行基准测试的最佳方法:
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 还指出,除了速度之外,这些实现还有很多不同。
【讨论】:
比较$index < $#array
实际上应该是$index <= $#array
,因为$#array
不是数组的长度,而是它的最后一个索引。【参考方案6】:
单行打印元素或数组。
为 (@array) 打印 $_;
注意:请记住 $_ 在内部引用循环中的 @array 元素。 $_ 中所做的任何更改都将反映在@array 中; 前任。
my @array = qw( 1 2 3 );
for (@array)
$_ = $_ *2 ;
print "@array";
输出:2 4 6
【讨论】:
以上是关于遍历 Perl 数组的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章