我最近遇到了一段perl代码。代码如下所示
package CODE;
sub _doJob
{
$result = JOB->new(@_)->doIt();
return $result;
}
sub _doFirstJob
{
unshift @_, '1';
goto &_doJob;
}
sub _doSecondJob
{
unshift @_, '2';
goto &_doJob;
}
sub _doThirdJob
{
unshift @_, '3';
goto &_doJob;
}
然后调用类似
的函数$result = CODE::_doFirstJob($a);
$result = CODE::_doSecondJob($a);
$result = CODE::_doThirdJob($a);
问题是goto
在这里有什么意义?如果我用return
编写类似的函数调用,如下所示,那会是什么问题?
package CODE;
sub _doJob
{
$result = JOB->new(@_)->doIt();
return $result;
}
sub _doFirstJob
{
unshift @_, '1';
return &_doJob(@_);
}
sub _doSecondJob
{
unshift @_, '2';
return &_doJob(@_)
}
sub _doThirdJob
{
unshift @_, '3';
return &_doJob(@_)
}
我仍然可以用同样的方式调用这些函数
$result = CODE::_doFirstJob($a);
$result = CODE::_doSecondJob($a);
$result = CODE::_doThirdJob($a);
我知道goto &NAME
实际上使perl退出当前函数并将其替换为调用堆栈中的新函数。但是我如何从中受益呢?
答案 0 :(得分:1)
您可以通过跳过堆栈处理来节省一些时间和内存。您可以使用Benchmark来测试您的情况中差异是否显着。
阅读" Tail Call"也可能会提供一些信息。例如,请参阅PerlMonks处的旧帖子。
我使用以下脚本进行了测试:
sub rec {
$_[0]++;
sleep 5, return if $_[0] > 1000;
return rec(@_); # Comment this line.
goto &rec
}
rec(1 .. 200);
时间差是不可测量的,内存消耗完全不同:return
情况下的字节数几乎是goto
的两倍。迭代次数越多,goto
的内存消耗保持不变,但随return
增长。
答案 1 :(得分:1)
不幸的是,尾部召唤神话只是一个神话。 goto仍会创建一个新的堆栈帧,并不比递归更快。我尝试用cperl消除这个成本,做真正的尾递归,覆盖堆栈值,但我认为我还没有正确解决它。整个ABI被高度去优化。它只适用于cperl签名,因为只有那些使用堆栈ABI作为XS。使用纯perl5,你和python一样混乱。