嵌套子例程和Perl中的作用域

sno*_*kin 17 perl scope nested subroutine

我现在写Perl很长一段时间了,总是发现新的东西,我只是碰到了一些有趣的东西,我没有解释它,也没有在网上找到它.

sub a {
   sub b {
     print "In B\n";
   }
}
b();
Run Code Online (Sandbox Code Playgroud)

为什么我可以b()从外部调用它并且它有效?

我知道这是一个不好的做法,我不这样做,我使用闭合等这些情况,但只是看到了.

Ven*_*tsu 19

子例程在编译时存储在全局命名空间中.在你的例子中b();是简短的main::b();.要限制函数对范围的可见性,需要将匿名子例程分配给变量.

命名子程序和匿名子程序都可以形成闭包,但由于命名子程序只有嵌套它们才会被编译一次,因此它们的行为并不像人们期望的那样.

use warnings;
sub one {
    my $var = shift;
    sub two {
        print "var: $var\n";
    }
}
one("test");
two();
one("fail");
two();
__END__
output:
Variable "$var" will not stay shared at -e line 5.
var: test
var: test
Run Code Online (Sandbox Code Playgroud)

在Perl中允许嵌套命名子例程,但几乎可以肯定这是代码执行错误的一个信号.

  • @TLP,`sub two {}`是`BEGIN {*two = sub {}}`,因此它捕获了编译时存在的`$ var`.`my`基本上是在范围出口处出现的`new`,因此在第一次运行`$ one`之后,`two`的`$ var`变得与`one`的`$ var`不同. (5认同)

ike*_*ami 7

以下打印123.

sub a {
    $b = 123;
}

a();
print $b, "\n";
Run Code Online (Sandbox Code Playgroud)

那么为什么你对以下内容感到惊讶呢?

sub a {
    sub b { return 123; }
}

a();
print b(), "\n";
Run Code Online (Sandbox Code Playgroud)

没有任何要求$b&b词汇.事实上,你不能要求&b成为词汇(还).

sub b { ... }
Run Code Online (Sandbox Code Playgroud)

基本上是

BEGIN { *b = sub { ... }; }
Run Code Online (Sandbox Code Playgroud)

这里*b是符号表项$b,@b...,当然&b.这意味着subs属于包,因此可以从包内的任何地方调用,或者如果使用完全限定名称则可以在任何地方调用(MyPackage::b()).


Ter*_*emi 7

在perl中创建嵌套子例程的"官方"方法是使用local关键字.例如:

sub a {
    local *b = sub {
        return 123;
    };
    return b();  # Works as expected
}

b();  # Error: "Undefined subroutine &main::b called at ..."
Run Code Online (Sandbox Code Playgroud)

perldoc页面perlref有这个例子:

sub outer {
    my $x = $_[0] + 35;
    local *inner = sub { return $x * 19 };
    return $x + inner();
}
Run Code Online (Sandbox Code Playgroud)

"这具有创建另一个函数本地函数的有趣效果,这在Perl中通常不受支持."