{
sub a {
print 1;
}
}
a;
一个错误,是吗?
a
不应该在外面提供。
它在Perl 6 *中有效吗?
*抱歉,我还没有安装它。
答案 0 :(得分:30)
你问为什么sub在块外是可见的?如果是,那么因为编译时sub
关键字将sub放在main
命名空间中(除非使用package
关键字创建新的命名空间)。你可以试试像
{
my $a = sub {
print 1;
};
$a->(); # works
}
$a->(); # fails
在这种情况下,sub
关键字不是创建子并将其放在main
命名空间中,而是创建一个匿名子例程并将其存储在词法范围的变量中。当变量超出范围时,它将不再可用(通常)。
要了解详情,请查看perldoc perlsub
另外,您是否知道可以检查Perl解析器查看代码的方式?使用-MO=Deparse
中的标记perl -MO=Deparse yourscript.pl
运行perl。您的原始代码解析为:
sub a {
print 1;
}
{;};
a ;
首先编译sub,然后运行一个没有代码的块,然后调用a
。
对于我在Perl 6中的示例,请参阅:Success,Failure。请注意,在Perl 6中,取消引用是.
而不是->
。
编辑:我已添加another answer有关Perl 5.18预期的词汇子程序的新实验支持。
答案 1 :(得分:17)
在Perl 6中,subs确实是词法范围的,这就是代码抛出错误的原因(正如几个人已经指出的那样)。
这有几个有趣的含义:
答案 2 :(得分:14)
子例程是包作用域,而不是块作用域。
#!/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;
答案 3 :(得分:11)
Perl中的命名子例程被创建为全局名称。其他答案已经展示了如何通过为词法变量分配匿名子来创建词法子例程。另一种选择是使用local
变量来创建动态范围的子。
两者之间的主要区别在于通话风格和可见性。动态范围的子类可以像命名子一样被调用,并且它也将是全局可见的,直到它被定义的块为止。
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 ...
答案 4 :(得分:7)
如果您看到代码编译,运行并打印“1”,那么您没有遇到错误。
您似乎期望子例程只能在定义它们的词法范围内调用。那将是不好的,因为这意味着将无法调用其他文件中定义的子例程。也许你没有意识到每个文件都是在自己的词法范围内进行评估的?这允许喜欢
my $x = ...;
sub f { $x }
答案 5 :(得分:7)
冒着被@tchrist再次责骂的风险,我正在为完整性添加另一个答案。尚未发布的Perl 5.18预计将包含词法子程序作为实验性功能。
Here is a link to the relevant documentation.同样,这是非常实验性的,它不应该用于生产代码,原因有两个:
如果你愿意,可以玩这款新玩具,但是你已经被警告了!
答案 6 :(得分:5)
是的,我认为这是一个设计缺陷 - 更具体地说,最初选择使用动态范围而不是Perl中的词法范围,这自然会导致这种行为。但并非所有语言设计师和用户都同意。所以你问的问题没有明确的答案。
在Perl 5中添加了词法作用域,但作为一项可选功能,您需要特别指出它。有了这个设计选择,我完全同意:向后兼容性非常重要。