【问题标题】:Nested function call scoping and syntax嵌套函数调用范围和语法
【发布时间】:2012-03-08 20:01:17
【问题描述】:

我正在尝试调用嵌套递归 perl 函数,但我无法使用正确的语法。

问题:对嵌套函数执行递归调用的正确语法是什么(如果嵌套函数应该被递归调用)?

答案:参考已接受答案中建议的伪代码。

这是一个伪代码sn-p:

use Scalar::Util;
sub outerfunction {
  my $innerfunction = sub {
    # Do something
    innerfunction(); 
    # Do other things
  }; 
  Scalar::Util::weaken($innerfunction); 
  &$innerfunction(@_);
}; 

我尝试按以下方式调用内部函数(带有相应的错误消息):

innerfunction

未定义的子程序 &main::innerfunction

&innerfunction

未定义的子程序 &main::innerfunction

&$innerfunction

全局符号“$innerfunction”需要明确的包名

我也尝试将内部函数声明为本地函数,但收到以下内容:

全局符号“$innerfunction”需要明确的包名

我在解释语言方面没有太多经验,因此任何与内存/堆栈泄漏/损坏或上述伪代码的其他危险(递归系统限制除外)相关的附带评论也将不胜感激。

谢谢! 在 Linux 2.6.34.7-61.fc13.x86_64 上运行的 perl v5.10.1

【问题讨论】:

  • 在引用上使用weaken 的目的是什么?
  • 遇到了一些帖子,这些帖子建议使用它来防止过时的引用导致内存泄漏,当函数引用超出范围时,perl 解释器可能会忽略这些引用。不过,它似乎不会影响潜在的问题。如果不建议使用weak(),请纠正我的理解。
  • 听起来像是过早的优化。你有内存泄漏的问题吗?我认为像子程序这样基本的东西可以很好地防止泄漏。
  • @TLP,weaken 在创建递归闭包时是必需的。如果闭包持有对自身的强引用,那么它永远不会被垃圾收集(直到解释器关闭)。这可能不是严重的泄漏(这取决于调用outerfunction 的频率),但它会是内存泄漏。
  • @TLP, my $foo; sub foo { $foo } $foo = \&foo; 确实遇到了同样的问题(即 $foo&foo 在全球破坏之前不会被释放),但是 1)你永远不会这样做,并且2) foo 在全局销毁之前不会超出范围,所以它没有实际意义。

标签: perl syntax recursion scope


【解决方案1】:

innerfunction() 语法仅适用于已安装到符号表中的子例程(例如 sub NAME {...} 语法)。您需要将您的内部函数称为$innerfunction->()&$innerfunction(),但您遇到的问题是$innerfunction 词法的范围。

当您使用my 声明变量时,该变量在该语句结束后 的范围内。所以你需要拆分你的声明:

 my $innerfunction;
    $innerfunction = sub {
        ...
        $innerfunction->();
        ...
    };

要使用weaken 打破循环引用,通常的模式是:

use Scalar::Util;
sub outer_function {
    my $weak_ref;
    $weak_ref = my $strong_ref = sub {
        # Do something
        $weak_ref->(); 
        # Do other things
    };
    Scalar::Util::weaken($weak_ref); 
    return $strong_ref;
};

所以现在,一旦$strong_ref 超出范围,子例程就会被垃圾回收。

【讨论】:

  • 你玩过 blead 中的__SUB__ 新功能了吗?
【解决方案2】:
sub outer_function {
    local *inner_function = sub {
        # Do something
        inner_function(); 
        # Do other things
    };
    inner_function();
};

几乎与以下内容一样好,但更清晰:

use Scalar::Util qw( weaken );
sub outer_function {
    my $weak_ref;
    my $strong_ref = sub {
        # Do something
        $weak_ref->(); 
        # Do other things
    };
    weaken($weak_ref = $strong_ref);  # Avoid memory leak.
    $strong_ref->();
};

【讨论】:

  • 需要明确的是,顶级版本不会遭受内存泄漏,即使它没有经历所有这些扭曲。 local 提供与weaken 相同的服务。 (@Eric Strom 可能会觉得这很有趣。)
  • 我同意使用local 更干净,但实用性大大降低。词法闭包的优势在于它可以被返回(如我的回答所示)。如果您尝试从第一个示例返回 coderef 然后调用它,充其量您会在调用未定义的子例程时遇到致命错误,最坏的情况是您会调用一个不相关但类似命名的子例程,从而导致各种意外行为.我在回答中假设 OP 只是展示了一个简短的示例,并希望在外部范围内使用 coderef。如果不是,那么我同意local 运作良好。
  • Re“但效用大大降低了”,我已经用这种方式完成了“数百万”个递归函数,但没有一个弱化,所以它没有减少太多。重新“正如我的回答所示”,是的,我注意到你在某种程度上偏离了这个问题。大多数递归函数都可以从非递归包装器中受益,在这种情况下,递归部分本身最好是私有的。这不是创建闭包的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多