【问题标题】:perl goto&Name vs returnperl goto&Name vs return
【发布时间】:2015-03-12 14:32:25
【问题描述】:

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

【问题讨论】:

    标签: perl function return goto callstack


    【解决方案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 的增加而增长。

    【讨论】:

    • @ikegami:在我的机器上,它似乎快了一点。对于更多的迭代,我看到了恒定的差异(100_000 为 0.1 秒),这仍然是微不足道的。
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多