perl goto&Name 对比 return

perl goto&Name vs return

我最近偶然发现了一段 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 退出当前函数并用调用堆栈中的新函数替换它。但是我在这里如何从中受益?

您可以通过跳过堆栈处理来节省一些时间和内存。您可以使用 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.

的增加而增加

不幸的是,尾巴调用神话只是一个神话。 goto 仍然会创建一个新的堆栈框架并且并不比递归快。我尝试用 cperl 消除这个成本,进行真正的尾递归,覆盖堆栈值,但我认为我还没有正确解决它。整个 ABI 是高度去优化的。而且它只适用于 cperl 签名,因为只有那些使用堆栈 ABI 作为 XS 的签名。使用纯 perl5,你和 python.

一样乱七八糟