Perl subs不是词汇范围的设计缺陷吗?

new*_*erl 12 perl scope perl6 raku

{
  sub a {
    print 1;
  }
}
a;
Run Code Online (Sandbox Code Playgroud)

一个错误,是吗?

a 不应该从外面获得.

它在Perl 6*中有效吗?

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

Joe*_*ger 30

你问为什么sub在块外是可见的?如果是,那么因为compile time sub关键字将sub放在main命名空间中(除非使用package关键字创建新的命名空间).你可以尝试类似的东西

{
  my $a = sub {
    print 1;
  };
  $a->(); # works
}
$a->(); # fails
Run Code Online (Sandbox Code Playgroud)

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

阅读更多信息 perldoc perlsub

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

sub a {
    print 1;
}
{;};
a ;
Run Code Online (Sandbox Code Playgroud)

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

对于我在Perl 6中的例子,请参阅:成功,失败.请注意,在Perl 6中,取消引用.不是->.

编辑:我已经添加了另一个关于Perl 5.18预期的词汇子程序的实验性支持的答案.


mor*_*itz 17

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

这有几个有趣的含义:

  • 嵌套的命名子工作作为正确的闭包(参见:perl 5中的"不会保持共享"警告)
  • 从模块导入subs工作到词法范围
  • 内置函数在程序周围的外部词法范围("设置")中提供,因此覆盖就像声明或导入同名函数一样简单
  • 因为lexpads在运行时是不可变的,所以编译器可以在编译时检测对未知例程的调用(niecza已经这样做了,Rakudo仅在"optimizer"分支中).


Bil*_*ert 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;
Run Code Online (Sandbox Code Playgroud)


Ven*_*tsu 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();
Run Code Online (Sandbox Code Playgroud)

这应该打印

in temp_sub
in test_sub
in temp_sub
in test_sub
Undefined subroutine &main::temp_sub called at ...
Run Code Online (Sandbox Code Playgroud)

  • @JoelBerger我用这种方式解释:**包用于查找内容,范围用于隐藏内容.**Perl中的函数*名称*(以及格式名称和文件名称,实际上)*始终*绑定到包.但是,变量可能绑定到包或范围.名字是一个全球性的想法; 另见符号解除引用.一旦这个名称的模型变得清晰,混乱就会消失. (3认同)

ike*_*ami 7

如果您看到代码编译,运行并打印"1",那么您没有遇到错误.

您似乎期望子例程只能在定义它们的词法范围内调用.那将是不好的,因为这意味着将无法调用其他文件中定义的子例程.也许你没有意识到每个文件都是在自己的词法范围内进行评估的?这允许喜欢的

my $x = ...;
sub f { $x }
Run Code Online (Sandbox Code Playgroud)


Joe*_*ger 7

冒着被@tchrist再次责骂的风险,我正在为完整性添加另一个答案.尚未发布的Perl 5.18预计将包含词法子程序作为实验性功能.

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

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

如果你愿意,可以玩这款新玩具,但是你已经被警告了!

  • @downvoter,不要因为我不喜欢这个想法而贬低我,我的答案是真实的。 (2认同)

rei*_*ost 5

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

在Perl 5中添加了词法作用域,但作为一个可选功能,您需要特别指出它.有了这个设计选择,我完全同意:向后兼容性非常重要.