Perl Goatse 'Secret Operator' 高效吗?
Posted
技术标签:
【中文标题】Perl Goatse \'Secret Operator\' 高效吗?【英文标题】:Is the Perl Goatse 'Secret Operator' efficient?Perl Goatse 'Secret Operator' 高效吗? 【发布时间】:2011-04-28 20:26:54 【问题描述】:Perl 中的“goatse 运算符”或=()=
习惯用法导致表达式在列表上下文中进行计算。
一个例子是:
my $str = "5 and 4 and a 3 and 2 1 BLAST OFF!!!";
my $count =()= $str =~ /\d/g; # 5 matches...
print "There are $count numbers in your countdown...\n\n";
当我解释使用时,会发生以下情况:
$str =~ /\d/g
匹配所有数字。 g
开关和列表上下文会生成这些匹配项的列表。让这成为“列表生产者”示例,在 Perl 中这可能是很多东西。
=()=
导致分配给一个空列表,因此所有实际匹配项都被复制到一个空列表中。
在标量上下文中对 2 中生成的列表的 $count 赋值。给出列表的计数或 5 的结果。
在标量赋值后,空列表=()=
的引用计数变为零。然后列表元素的副本被 Perl 删除。
关于效率的问题如下:
-
我的解析方式错了吗?
如果您有一些 List Producer 并且您只对计数感兴趣,是否有更有效的方法来执行此操作?
这个简单的列表很好用,但如果列表有数十万个匹配项怎么办?使用这种方法,您将生成每个匹配项的完整副本,然后将其删除以计算它们。
【问题讨论】:
它不如设计一个定制的微芯片来实现你的程序那么高效。 为什么投反对票?这不是一个关于效率和习语之间权衡的公平问题吗? 抱歉,我忍不住成为[goatse]
的标签创建者。 :D
列表没有引用计数;它们总是短暂的
@Ether:你要写标签 wiki 条目吗?
【参考方案1】:
Perl 5 在复制列表方面很聪明。它只复制与左侧一样多的项目。它之所以有效,是因为标量上下文中的列表分配会产生右侧的项目数。因此,n
项目将由正则表达式创建,但它们不会被复制和丢弃,只是被丢弃。您可以在下面的基准测试中看到额外副本在幼稚情况下的不同。
至于效率,迭代解决方案在内存和 CPU 使用方面通常更容易,但这必须与 goatse 秘密运算符的简洁性进行权衡。以下是对各种解决方案进行基准测试的结果:
naive: 10
iterative: 10
goatse: 10
for 0 items:
Rate iterative goatse naive
iterative 4365983/s -- -7% -12%
goatse 4711803/s 8% -- -5%
naive 4962920/s 14% 5% --
for 1 items:
Rate naive goatse iterative
naive 749594/s -- -32% -69%
goatse 1103081/s 47% -- -55%
iterative 2457599/s 228% 123% --
for 10 items:
Rate naive goatse iterative
naive 85418/s -- -33% -82%
goatse 127999/s 50% -- -74%
iterative 486652/s 470% 280% --
for 100 items:
Rate naive goatse iterative
naive 9309/s -- -31% -83%
goatse 13524/s 45% -- -76%
iterative 55854/s 500% 313% --
for 1000 items:
Rate naive goatse iterative
naive 1018/s -- -31% -82%
goatse 1478/s 45% -- -75%
iterative 5802/s 470% 293% --
for 10000 items:
Rate naive goatse iterative
naive 101/s -- -31% -82%
goatse 146/s 45% -- -75%
iterative 575/s 470% 293% --
这是生成它的代码:
#!/usr/bin/perl
use strict;
use warnings;
use Benchmark;
my $s = "a" x 10;
my %subs = (
naive => sub
my @matches = $s =~ /a/g;
return scalar @matches;
,
goatse => sub
my $count =()= $s =~ /a/g;
return $count;
,
iterative => sub
my $count = 0;
$count++ while $s =~ /a/g;
return $count;
,
);
for my $sub (keys %subs)
print "$sub: @[$subs$sub()]\n";
for my $n (0, 1, 10, 100, 1_000, 10_000)
$s = "a" x $n;
print "\nfor $n items:\n";
Benchmark::cmpthese -1, \%subs;
【讨论】:
+1:谢谢。我真的很欣赏你如何处理这个逻辑并且你抓住了我想象的情况:你拥有的越多,迭代越好。但是,如果 Perl 能够“聪明”地复制左侧所需的数字,那么=()=
不就是全部吗?
不,左边没有目标,所以没有数据被复制(但正则表达式仍然需要生成右边的目标)。
同意如果您有类似($i, $j, $k)=/a/g;
的内容,即使有 10 个匹配项,也会复制 3 个项目。但是如果你有()=/a/g;
,那么 Perl 是否足够聪明,可以看到零赋值副本 0?
@drewk 是的,就是这么聪明。
请原谅我公然插入我自己的软件,但对于基准测试,请查看我的Dumbbench
工具或Benchmark::Dumb
兼容性包装器,它与 Benchmark.pm 几乎相同,只是更好。文档试图解释原因。【参考方案2】:
在您的特定示例中,基准测试很有用:
my $str = "5 and 4 and a 3 and 2 1 BLAST OFF!!!";
use Benchmark 'cmpthese';
cmpthese -2 =>
goatse => sub
my $count =()= $str =~ /\d/g;
$count == 5 or die
,
while => sub
my $count;
$count++ while $str =~ /\d/g;
$count == 5 or die
,
;
返回:
Rate goatse while
goatse 285288/s -- -57%
while 661659/s 132% --
列表上下文中的$str =~ /\d/g
正在捕获匹配的子字符串,即使它不是必需的。 while
示例在标量(布尔)上下文中包含正则表达式,因此正则表达式引擎只需返回 true 或 false,而不是实际匹配项。
一般来说,如果你有一个列表生成函数并且只关心项目的数量,那么编写一个简短的count
函数会更快:
sub make_list map $_**2 0 .. 1000
sub count scalar @_
use Benchmark 'cmpthese';
cmpthese -2 =>
goatse => sub my $count =()= make_list; $count == 1001 or die,
count => sub my $count = count make_list; $count == 1001 or die,
;
给出:
Rate goatse count
goatse 3889/s -- -26%
count 5276/s 36% --
我对为什么 sub 更快的猜测是因为子例程调用经过优化,可以在不复制列表的情况下传递列表(作为别名传递)。
【讨论】:
+1:基准总是比空闲假设好。谢谢!【参考方案3】:如果您需要在列表上下文中运行某些内容,则必须在列表上下文中运行它。在某些情况下,就像您介绍的那样,您也许可以使用另一种技术来解决它,但在大多数情况下您不会。
然而,在您进行基准测试之前,最重要的问题是“这是否重要?”。在进行基准测试之前进行分析,并且只有在没有真正需要解决的问题时才担心这些事情。 :)
但是,如果您正在寻找极致的效率,Perl 的水平有点太高了。 :)
【讨论】:
“这是否重要”是一个公平的问题。 我 很重要,原因有两个:1)我很好奇!如果我使用一个成语与另一个成语,我喜欢在脑后思考我为什么这样做。 2)如果我使用捷径,我喜欢了解它的具体细节。我可以像$count =()= $s =~ /a/g;
一样轻松地养成输入$count++ while $s =~/a/g
的习惯。如果一个往往比另一个快很多,我会倾向于支持它而不说另一个是“错误的”。
你准备为这个“操作员”创建一个标签维基吗? ***.com/tags/goatse/info以上是关于Perl Goatse 'Secret Operator' 高效吗?的主要内容,如果未能解决你的问题,请参考以下文章