Perl 子程序没有词法作用域是一个设计缺陷吗?

发布于 2024-12-06 05:40:05 字数 155 浏览 2 评论 0 原文

{
  sub a {
    print 1;
  }
}
a;

一个错误,是吗?

a 不应该从外部获得。

它在 Perl 6* 中工作吗?

* 抱歉我还没有安装。

{
  sub a {
    print 1;
  }
}
a;

A bug,is it?

a should not be available from outside.

Does it work in Perl 6*?

* Sorry I don't have installed it yet.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

南城追梦 2024-12-13 05:40:05

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

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

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

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

另外,您知道吗你可以检查 Perl 解析器如何看待你的代码吗?使用标志 -MO=Deparse 运行 perl,如 perl -MO=Deparse yourscript.pl 中所示。您的原始代码解析为:

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

首先编译子程序,然后运行一个没有代码的块,然后调用 a

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

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

Are you asking why the sub is visible outside the block? If so then its because the compile time sub keyword puts the sub in the main namespace (unless you use the package keyword to create a new namespace). You can try something like

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

In this case the sub keyword is not creating a sub and putting it in the main namespace, but instead creating an anonymous subroutine and storing it in the lexically scoped variable. When the variable goes out of scope, it is no longer available (usually).

To read more check out perldoc perlsub

Also, did you know that you can inspect the way the Perl parser sees your code? Run perl with the flag -MO=Deparse as in perl -MO=Deparse yourscript.pl. Your original code parses as:

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

The sub is compiled first, then a block is run with no code in it, then a is called.

For my example in Perl 6 see: Success, Failure. Note that in Perl 6, dereference is . not ->.

Edit: I have added another answer about new experimental support for lexical subroutines expected for Perl 5.18.

冰雪梦之恋 2024-12-13 05:40:05

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

这有几个有趣的含义:

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

In Perl 6, subs are indeed lexically scoped, which is why the code throws an error (as several people have pointed out already).

This has several interesting implications:

  • nested named subs work as proper closures (see also: the "will not stay shared" warning in perl 5)
  • importing of subs from modules works into lexical scopes
  • built-in functions are provided in an outer lexical scope (the "setting") around the program, so overriding is as easy as declaring or importing a function of the same name
  • since lexpads are immutable at run time, the compiler can detect calls to unknown routines at compile time (niecza does that already, Rakudo only in the "optimizer" branch).
心舞飞扬 2024-12-13 05:40:05

子例程是包作用域的,而不是块作用域的。

#!/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;

Subroutines are package scoped, not block scoped.

#!/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;
ゝ偶尔ゞ 2024-12-13 05:40:05

Perl 中的命名子例程被创建为全局名称。其他答案已经展示了如何通过将匿名子分配给词法变量来创建词法子例程。另一种选择是使用本地变量来创建动态范围的子变量。

两者之间的主要区别在于调用方式和可见性。动态范围的子程序可以像命名子程序一样被调用,并且它也将是全局可见的,直到它定义的块离开为止。

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 ...

Named subroutines in Perl are created as global names. Other answers have shown how to create a lexical subroutines by assigning an anonymous sub to a lexical variable. Another option is to use a local variable to create a dynamically scoped sub.

The primary differences between the two are call style and visibility. The dynamically scoped sub can be called like a named sub, and it will also be globally visible until the block it is defined in is left.

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();

This should print

in temp_sub
in test_sub
in temp_sub
in test_sub
Undefined subroutine &main::temp_sub called at ...
素染倾城色 2024-12-13 05:40:05

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

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

  1. 它可能还没有很好地实现
  2. 它可能会在没有通知的情况下被删除

所以如果你愿意的话可以玩这个新玩具,但你已经被警告了!

At the risk of another scolding by @tchrist, I am adding another answer for completeness. The as yet to be released Perl 5.18 is expected to include lexical subroutines as an experimental feature.

Here is a link to the relevant documentation. Again, this is very experimental, it should not be used for production code for two reasons:

  1. It might not be well implemented yet
  2. It might be removed without notice

So play with this new toy if you want, but you have been warned!

一花一树开 2024-12-13 05:40:05

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

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

my $x = ...;
sub f { $x }

If you see the code compile, run and print "1", then you are not experiencing a bug.

You seem to be expecting subroutines to only be callable inside the lexical scope in which they are defined. That would be bad, because that would mean that one wouldn't be able to call subroutines defined in other files. Maybe you didn't realise that each file is evaluated in its own lexical scope? That allows the likes of

my $x = ...;
sub f { $x }
岁月无声 2024-12-13 05:40:05

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

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

Yes, I think it is a design flaw - more specifically, the initial choice of using dynamic scoping rather than lexical scoping made in Perl, which naturally leads to this behavior. But not all language designers and users would agree. So the question you ask doesn't have a clear answer.

Lexical scoping was added in Perl 5, but as an optional feature, you always need to indicate it specifically. With that design choice I fully agree: backward compatibility is important.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文