我在irc.perl.org的#p5p频道发了这个问题,得到了有趣的交流,解释了正在发生的事情。
[15:13:35] <simbabque> 有人能解释一下在
Perl作用域中访问子程序中的变量发生了什么吗?我试图阅读该程序的B :: Concise输出,但我的理解力还不够强。我们在那里看到的行为可能是一个错误吗?
[15:15:35] <rjbs> &foo是对@aTest的闭包。
[15:16:33] <haarg> 但只有第一个@aTest,因为&foo只在编译时创建一次。
[15:17:01] <rjbs> 对的。
[15:18:09] <rjbs> 在除包或裸块之外的任何东西中声明命名子程序,在我看来,都会引发未来的烦恼。
[15:18:23] <alh> 如果你有my $foo = sub { };$foo->();它将按预期工作。
[15:18:34] <alh> 或者在更新的Perl版本中,使用功能use feature qw(lexical_subs); my sub foo {}foo()也可以工作。
[15:18:35] <simbabque> 好吧,那个家伙说他在打高尔夫球时遇到了这个问题。
[15:19:13] <simbabque> alh:对于这两个,我也希望它是一个闭包,但由于子程序foo {}在编译时完成,我感到困惑。
[15:19:38] <rjbs> 词法子程序“做正确的事情”与绑定有关。
[15:19:45] <alh> 仍然是闭包,只是每次重新评估
[15:20:56] <haarg> 它们共享op树,但绑定到不同的变量
[15:23:29] <simbabque> 我在函数内部和外部添加了
say "foo: ".\@aTest;
和
say "out ".\@aTest;
。那很奇怪。第一轮都是相同的,然后foo保持相同,而循环中的地址得到新地址并保留在随后的迭代中。
[15:26:48] <alh> 当然,在循环中第一次运行时,子程序封闭的变量与循环看到的变量是相同的。
[15:27:01] <alh> 然后我们循环,并获得全新的变量,但子程序不会(因为它没有再次编译)。
[15:27:46] <simbabque> alh:那很有道理,但为什么所有后续迭代都重用相同的变量,但在循环中重置它会让你感到惊讶?这只是Perl对其内存的智能运用吗?
[15:28:48] <alh> 不,您的子程序已封闭了一个变量并保持对其的引用-因此它永远不会消失,并且其值在子调用之间保持不变。
[15:29:07] <alh> 子程序中没有“my @aTest”来“重置”变量
[15:29:19] <alh> 因此,它只保留其值-这就是闭包的目的
我所指的输出来自于这个修改:
for my $i(0..3) {
my @aTest = (1);
sub foo {
push @aTest, 2;
my $unused = 0;
print " foo: ".\@aTest;
}
print " out: ".\@aTest;
foo();
}
因此,本质上是在编译时在 @aTest
上建立闭包。在第一次迭代中,循环中的变量与子程序中的变量相同。在所有后续迭代中,它会创建一个新的循环变量,因此我们每次看到一个新的 (1)
。但子程序不会再次编译,因此其中的 @aTest
变量保持不变并增长。
foo()
之前直接输出@aTest
时会发生什么。它总是( 1 )
。就好像在子程序中有一个新的@aTest
词法变量,但从未声明过。为什么这不会失败呢? - undefinedfor
循环之外声明my @aTest
,它就能正常工作。 - undefined