12
{
  sub a {
    print 1;
  }
}
a;

一个错误,是吗?

a不应从外部获得。

它在 Perl 6* 中工作吗?

*对不起,我还没有安装它。

4

7 回答 7

30

您是在问为什么 sub 在块外可见吗?如果是这样,那么它是因为编译时sub关键字将 sub 放在main命名空间中(除非您使用该package关键字创建新的命名空间)。你可以尝试类似的东西

{
  my $a = sub {
    print 1;
  };
  $a->(); # works
}
$a->(); # fails

在这种情况下,sub关键字不是创建一个 sub 并将其放入main命名空间,而是创建一个匿名子例程并将其存储在词法范围的变量中。当变量超出范围时,它不再可用(通常)。

要了解更多信息,请查看perldoc perlsub

另外,您知道您可以检查 Perl 解析器查看您的代码的方式吗?使用标志运行 perl,-MO=Deparseperl -MO=Deparse yourscript.pl. 您的原始代码解析为:

sub a {
    print 1;
}
{;};
a ;

sub首先被编译,然后运行一个没有代码的块,然后a被调用。

对于我在 Perl 6 中的示例,请参阅:SuccessFailure。请注意,在 Perl 6 中,取消引用.不是->.

编辑:我添加了另一个关于 Perl 5.18 预期的词法子例程的新实验性支持的答案。

于 2011-09-23T02:54:22.213 回答
17

在 Perl 6 中,subs 确实是词法范围的,这就是代码抛出错误的原因(正如一些人已经指出的那样)。

这有几个有趣的含义:

  • 嵌套命名的 subs 用作适当的闭包(另请参阅:perl 5 中的“将不保持共享”警告)
  • 从模块导入 subs 进入词法范围
  • 内置函数在程序周围的外部词法范围(“设置”)中提供,因此覆盖就像声明或导入同名函数一样简单
  • 由于 lexpad 在运行时是不可变的,编译器可以在编译时检测对未知例程的调用(niecza 已经这样做了,Rakudo 只在“优化器”分支中)。
于 2011-09-23T06:19:18.643 回答
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;
于 2011-09-23T03:02:10.597 回答
13

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 ...
于 2011-09-23T20:10:13.243 回答
8

冒着@tchrist再次责骂的风险,我添加了另一个完整的答案。尚未发布的 Perl 5.18 预计将包含词法子例程作为实验特性。

这是相关文档的链接。同样,这是非常实验性的,它不应该用于生产代码,原因有两个:

  1. 它可能还没有很好地实施
  2. 它可能会被删除,恕不另行通知

所以如果你想玩这个新玩具,但你已经被警告了!

于 2012-10-12T20:51:51.053 回答
7

如果您看到代码编译、运行并打印“1”,那么您没有遇到错误。

您似乎期望子例程只能在定义它们的词法范围内调用。那会很糟糕,因为这意味着一个人将无法调用在其他文件中定义的子例程。也许您没有意识到每个文件都在其自己的词法范围内进行评估?这允许像

my $x = ...;
sub f { $x }
于 2011-09-23T03:32:11.190 回答
6

是的,我认为这是一个设计缺陷——更具体地说,最初选择使用动态范围而不是 Perl 中的词法范围,这自然会导致这种行为。但并非所有语言设计者和用户都会同意。所以你问的问题没有明确的答案。

Perl 5 中添加了词法作用域,但作为一个可选功能,您始终需要特别指出它。对于这种设计选择,我完全同意:向后兼容性很重要。

于 2011-09-26T13:04:40.383 回答