在Perl中,如何将数组的每个成员乘以一个标量?

9

以下是代码...

use strict;
use warnings;

my @array= (1,2,3,4,5);
my $scalar= 5;

@array= $scalar*@array;

print @array;

需要一些能够用少量代码实现类似功能的东西。谢谢!

1
如果你需要操作更大的数组,你可能需要看一下PDL http://pdl.perl.org - MkV
6个回答

10

使用 foreach 循环。

foreach my $x (@array) { $x = $x * $scalar; }

1
+1. 对于大型列表,foreach方法比map方法更快。 - Alex Reynolds
@Alex - 在数组上下文中这可能是正确的,但在空上下文(例如我的第二个版本)中则是无休止辩论的话题。这显然与平台和版本有关。此外,系统可以并行化地执行map操作(原则上),而foreach通常不能,因此map的执行速度至少有可能比foreach更快。 - Ted Hopp
1
@Ted Hopp,map 的并行处理是 Perl 6 可能会采用的一项功能,但它很可能永远不会出现在 Perl 5 中。太多的代码依赖于 map 语句的有序迭代。Perl 5 的副作用过重,使得 perl 无法识别哪些 map 函数体是完全没有副作用的。 - Ven'Tatsu
1
map 函数的速度取决于 Perl 版本。较新版本的 Perl 会识别 map 函数在 void 上下文中的调用,并且不会存储 map 的结果,然后在结束时丢弃结果,而旧版本的 Perl 则会这样做。 - MkV
1
@SoloBold - 只需在 Google 上搜索“perl map vs foreach”即可获取大量良好讨论。其中 perlmonks.org 上的讨论通常都是见解充分且具体明了的。 - Ted Hopp
显示剩余4条评论

10

你可以尝试这样做:

@array = map { $_ * $scalar } @array;
更简单地说:
map { $_ *= $scalar } @array;

3
在 map 块中,不应修改 $_。 - MkV
1
@MkV - 为什么不呢?从perldoc page for map中可以看到:“请注意,$_是列表值的别名,因此它可以用于修改LIST的元素。”这是一种常见的将数组的所有元素转换的习惯用法。 - Ted Hopp
虽然这很有用且被支持,但如果LIST的元素不是变量,则可能导致奇怪的结果。在大多数情况下,使用常规的foreach循环会更清晰。 - MkV
@MkV,但是列表的元素是变量。也就是说,我会自己使用$_ *= $scalar for @array; - ikegami
@ikegami - 我也会选择你的建议,毕竟一切都说完了。不是出于性能原因,而是因为它让我觉得比其他任何东西都更易读。 :) - Ted Hopp
显示剩余2条评论

7
这个怎么样:

如何实现:

foreach(@array)
{ $_ *= $scalar }

正如您所看到的,您可以在遍历数组时就地修改它。


5
如果您打算依赖隐式变量$,那么最好对@array中的每个元素执行$ *= $scalar。 - MkV

6

我不知道你的需求范围。如果你正在进行数字数据处理,那么Perl数据语言(PDL)可以将数字数据数组转换为“piddle”对象,并重载数学运算符以“向量化”它们的操作。这是进行数字处理非常高效的系统。以下是一个示例:

#!/usr/bin/perl

use strict;
use warnings;

use PDL;

my $pdl_array = pdl([1,1,2,3,5,8]);
print 2*$pdl_array;

__END__
gives:
[2 2 4 6 10 16]

有趣。我有大约1000个元素的数据集,我必须反复对它们进行处理。我想知道PDL在这方面是否比perl快得多? - Matthew Lock
没问题,我试一下。 - Matthew Lock

5
这则评论是给SoloBold的。
这里是使用map方法的测试:
#!/usr/bin/perl                                                                                                                                                                                                          

use strict;
use warnings;
use Benchmark;

my @array = ();
push(@array, (1) x 1000000);
my $scalar = 5;

my $startTime = new Benchmark();

@array = map { $_ * $scalar } @array;

my $stopTime = new Benchmark();

print STDOUT "runtime: ".timestr(timediff($stopTime, $startTime), 'all')." sec\n";

以下是使用foreach方法的测试:

#!/usr/bin/perl                                                                                                                                                                                                          

use strict;
use warnings;
use Benchmark;

my @array = ();
push(@array, (1) x 1000000);
my $scalar = 5;

my $startTime = new Benchmark();

foreach my $x (@array) { $x = $x * $scalar; }

my $stopTime = new Benchmark();

print STDOUT "runtime: ".timestr(timediff($stopTime, $startTime), 'all')." sec\n";

以下是我的系统信息:

bash-3.2$ perl --version
This is perl, v5.8.8 built for darwin-2level
...
bash-3.2$ uname -a
Darwin Sounder.local 10.7.0 Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386 i386

以下是一次测试的结果:

bash-3.2$ ./test.map.pl
runtime:  4 wallclock secs ( 0.41 usr  0.70 sys +  0.00 cusr  0.00 csys =  1.11 CPU) sec
bash-3.2$ ./test.foreach.pl
runtime:  0 wallclock secs ( 0.13 usr  0.00 sys +  0.00 cusr  0.00 csys =  0.13 CPU) sec

这些时间在同一台机器上是相对可重复的,并且在双核Linux机器上的结果有些可重复:

[areynolds@fiddlehead ~]$ perl --version
This is perl, v5.8.8 built for x86_64-linux-thread-multi
...
[areynolds@fiddlehead ~]$ uname -a
Linux fiddlehead.example.com 2.6.18-194.17.1.el5 #1 SMP Mon Sep 20 07:12:06 EDT 2010 x86_64 GNU/Linux
[areynolds@fiddlehead ~]$ ./test.map.pl
runtime:  0 wallclock secs ( 0.28 usr  0.05 sys +  0.00 cusr  0.00 csys =  0.33 CPU) sec
[areynolds@fiddlehead ~]$ ./test.foreach.pl
runtime:  0 wallclock secs ( 0.09 usr  0.00 sys +  0.00 cusr  0.00 csys =  0.09 CPU) sec

在OS X系统上,mapforeach的性能慢了8.53倍。在Linux系统上,同样的操作,速度慢了3.67倍。
我的Linux系统是双核的,而且每个核心的速度比我的单核OS X笔记本略快。 编辑 我在我的OS X系统上将Perl从v5.8.8升级到v5.12.3,获得了显着的速度提升,但是map仍然比foreach表现差。
sounder:~ alexreynolds$ perl --version
This is perl 5, version 12, subversion 3 (v5.12.3) built for darwin-multi-2level
...
sounder:~ alexreynolds$ ./test.map.pl
runtime:  0 wallclock secs ( 0.45 usr  0.08 sys +  0.00 cusr  0.00 csys =  0.53 CPU) sec
sounder:~ alexreynolds$ ./test.foreach.pl
runtime:  1 wallclock secs ( 0.18 usr  0.00 sys +  0.00 cusr  0.00 csys =  0.18 CPU) sec

这个数值从变差了8.53倍到变差了2.94倍,改善相当显著。

将Perl安装升级到v5.12.2后,Linux系统的表现略微变差

[areynolds@basquiat bin]$ perl --version    
This is perl 5, version 12, subversion 2 (v5.12.2) built for x86_64-linux-thread-multi
...
[areynolds@basquiat bin]$ /home/areynolds/test.map.pl
runtime:  1 wallclock secs ( 0.29 usr  0.07 sys +  0.00 cusr  0.00 csys =  0.36 CPU) sec
[areynolds@basquiat bin]$ /home/areynolds/test.foreach.pl
runtime:  0 wallclock secs ( 0.08 usr  0.00 sys +  0.00 cusr  0.00 csys =  0.08 CPU) sec

这将恶化3.67倍到4.5倍,情况不太好!仅仅为了玩而升级可能并不划算。

1

对我来说,很不幸的是Larry没有允许

$scalar operator (list)

或者

(list) operator $scalar

当然,map或循环也可以实现,但以上语法更加简洁。

如果两个列表长度相等,(list) operator (list) 也是有意义的。

惊讶于Larry没有允许这些,只是说一下...我猜在这种情况下还有(n-1)种方法可以实现。

比如

my @a = 'n' . (1..5); my @a = 2 * (1..5);

甚至

my @a = 2 * @b;


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接