perl goto& Name vs return

时间:2015-01-14 11:57:56

标签: perl function return goto callstack

我最近遇到了一段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退出当前函数并将其替换为调用堆栈中的新函数。但是我如何从中受益呢?

2 个答案:

答案 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一样混乱。