我用perl 5.8(codepad)和perl 5.16测试了以下代码。可能有一些更深层次的原则我不知道,我很好奇这种行为背后的逻辑是什么。感谢。
以下简单的递归子例程引用
use strict;
use warnings;
my $fact = sub {
my $n = shift;
if ($n == 0) {
return 1;
}
return $n * $fact->($n-1);
};
print $fact->(100);
导致错误
Global symbol "$fact" requires explicit package name at line 9.
Execution aborted due to compilation errors.
在定义变量之前声明变量不会产生此错误。
use strict;
use warnings;
my $fact;
$fact = sub {
my $n = shift;
if ($n == 0) {
return 1;
}
return $n * $fact->($n-1);
};
print $fact->(100);
答案 0 :(得分:5)
my
返回新的词法,因此它可以用于在声明它的同一语句中将分配给。但是,对 name 的任何进一步引用仅从下一个语句开始解析为该词汇。
将声明分开:
my $fact;
$fact = sub { ... $fact ... }
这条规则实际上有时是有用的;你可以有一个外部词汇和一个内部词汇,并在两者之间分配:
my $foo = 42;
{
my $foo = $foo;
$foo += 42;
print "foo is $foo\n";
}
print "foo is $foo\n";
如果您有最新版本的Perl,则实际上不需要在子例程中访问$fact
,因为__SUB__
伪常量提供对当前子例程的引用。
use 5.016;
my $fact = sub {
my $n = shift;
if ($n == 0) {
return 1;
}
return $n * __SUB__->($n-1);
};
请注意,在第一个示例中(在sub中使用$fact
变量),会创建一个引用周期,这可能会导致Perl随着时间的推移泄漏内存。 __SUB__
是一种解决该问题的相当简洁的方法。 (该问题的其他解决方案包括the Y-combinator和reference weakening。)