以下代码
#!/usr/bin/env perl
use strict;
use warnings;
my @foo = (0,1,2,3,4);
foreach my $i (@foo) {
sub printer {
my $blah = shift @_;
print "$blah-$i\n";
}
printer("test");
}
没有按照我的预期行事。
到底发生了什么? (我希望它打印出来“test-0 \ ntest-1 \ ntest-2 \ ntest-3 \ ntest-4 \ n”)
答案 0 :(得分:19)
问题是sub name {...}
构造不能像for
循环那样嵌套。
原因是因为sub name {...}
实际上意味着BEGIN {*name = sub {...}}
并且解析后立即执行开始块。所以子程序的编译和变量绑定发生在编译时,在for循环有机会运行之前。
你想要做的是创建一个匿名子程序,它将在运行时绑定它的变量:
#!/usr/bin/env perl
use strict;
use warnings;
my @foo = (0,1,2,3,4);
foreach my $i (@foo) {
my $printer = sub {
my $blah = shift @_;
print "$blah-$i\n";
};
$printer->("test");
}
打印
test-0
test-1
test-2
test-3
test-4
据推测,在您的实际用例中,这些闭包将被加载到数组或散列中,以便以后可以访问它们。
你仍然可以使用带有闭包的裸字标识符,但是你需要做一些额外的工作来确保名称在编译时可见:
BEGIN {
for my $color (qw(red blue green)) {
no strict 'refs';
*$color = sub {"<font color='$color'>@_</font>"}
}
}
print "Throw the ", red 'ball'; # "Throw the <font color='red'>ball</font>"
答案 1 :(得分:7)
关于词汇生命周期的简短说明:词汇在编译时创建,甚至在输入范围之前实际可用,如此示例所示:
my $i;
BEGIN { $i = 42 }
print $i;
此后,当它们超出范围时,它们将在下一次进入范围之前变得不可用:
print i();
{
my $i;
BEGIN { $i = 42 }
# in the scope of `my $i`, but doesn't actually
# refer to $i, so not a closure over it:
sub i { eval '$i' }
}
print i();
在您的代码中,闭包在编译时绑定到初始词法$i
。
然而,foreach循环有点奇怪;虽然my $i
实际上创建了一个词法,但foreach循环并没有使用它;相反,它在每次迭代时将其别名为循环值之一,然后在循环之后将其恢复到其原始状态。因此,你的闭包是唯一引用原始词汇$i
的东西。
略有变化表明更复杂:
foreach (@foo) {
my $i = $_;
sub printer {
my $blah = shift @_;
print "$blah-$i\n";
}
printer("test");
}
这里,原始$i
是在编译时创建的,闭包绑定到那个;循环的第一次迭代设置它,但循环的第二次迭代创建一个与闭包无关的新$i
。