{
sub a {
print 1;
}
}
a;
一个错误,是吗?
a
不应从外部获得。
它在 Perl 6* 中工作吗?
*对不起,我还没有安装它。
您是在问为什么 sub 在块外可见吗?如果是这样,那么它是因为编译时sub
关键字将 sub 放在main
命名空间中(除非您使用该package
关键字创建新的命名空间)。你可以尝试类似的东西
{
my $a = sub {
print 1;
};
$a->(); # works
}
$a->(); # fails
在这种情况下,sub
关键字不是创建一个 sub 并将其放入main
命名空间,而是创建一个匿名子例程并将其存储在词法范围的变量中。当变量超出范围时,它不再可用(通常)。
要了解更多信息,请查看perldoc perlsub
另外,您知道您可以检查 Perl 解析器查看您的代码的方式吗?使用标志运行 perl,-MO=Deparse
如perl -MO=Deparse yourscript.pl
. 您的原始代码解析为:
sub a {
print 1;
}
{;};
a ;
sub首先被编译,然后运行一个没有代码的块,然后a
被调用。
对于我在 Perl 6 中的示例,请参阅:Success、Failure。请注意,在 Perl 6 中,取消引用.
不是->
.
编辑:我添加了另一个关于 Perl 5.18 预期的词法子例程的新实验性支持的答案。
在 Perl 6 中,subs 确实是词法范围的,这就是代码抛出错误的原因(正如一些人已经指出的那样)。
这有几个有趣的含义:
子程序是包范围的,而不是块范围的。
#!/usr/bin/perl
use strict;
use warnings;
package A;
sub a {
print 1, "\n";
}
a();
1;
package B;
sub a {
print 2, "\n";
}
a();
1;
Perl 中的命名子例程被创建为全局名称。其他答案显示了如何通过将匿名子分配给词法变量来创建词法子例程。另一种选择是使用local
变量来创建动态范围的子。
两者之间的主要区别是调用样式和可见性。动态作用域的 sub 可以像命名的 sub 一样被调用,并且它也将是全局可见的,直到离开它定义的块。
use strict;
use warnings;
sub test_sub {
print "in test_sub\n";
temp_sub();
}
{
local *temp_sub = sub {
print "in temp_sub\n";
};
temp_sub();
test_sub();
}
test_sub();
这应该打印
in temp_sub
in test_sub
in temp_sub
in test_sub
Undefined subroutine &main::temp_sub called at ...
冒着@tchrist再次责骂的风险,我添加了另一个完整的答案。尚未发布的 Perl 5.18 预计将包含词法子例程作为实验特性。
这是相关文档的链接。同样,这是非常实验性的,它不应该用于生产代码,原因有两个:
所以如果你想玩这个新玩具,但你已经被警告了!
如果您看到代码编译、运行并打印“1”,那么您没有遇到错误。
您似乎期望子例程只能在定义它们的词法范围内调用。那会很糟糕,因为这意味着一个人将无法调用在其他文件中定义的子例程。也许您没有意识到每个文件都在其自己的词法范围内进行评估?这允许像
my $x = ...;
sub f { $x }
是的,我认为这是一个设计缺陷——更具体地说,最初选择使用动态范围而不是 Perl 中的词法范围,这自然会导致这种行为。但并非所有语言设计者和用户都会同意。所以你问的问题没有明确的答案。
Perl 5 中添加了词法作用域,但作为一个可选功能,您始终需要特别指出它。对于这种设计选择,我完全同意:向后兼容性很重要。