嵌套闭包和捕获变量
Nested closures and captured variables
我有这个带有嵌套闭包的示例,它演示了内存泄漏
use v5.10;
use strict;
package Awesome;
sub new {
bless {steps => [], surprise => undef}, shift;
}
sub say {
print "awesome: ", $_[1], "\n";
}
sub prepare {
my ($self, @steps) = @_;
for my $s (@steps) {
push @{$self->{steps}}, sub {
$self->say($s);
if ($s eq 'pony') {
$self->{surprise} = sub {
$s;
}
}
};
}
}
sub make {
my $self = shift;
while (my $step = shift @{$self->{steps}}) {
$step->();
}
if ($self->{surprise}) {
printf("And you have surprise: %s\n", $self->{surprise}->());
}
}
sub DESTROY {
warn "destroying";
}
package main;
my $a = Awesome->new;
$a->prepare('barbie','pony','flash');
$a->make();
我的 perl 的输出是
awesome: barbie
awesome: pony
awesome: flash
And you have surprise: pony
destroying at /tmp/t.pl line 43 during global destruction.
而这个"during global destruction"意味着这个对象不能以正常的方式销毁,因为它有一些循环引用。
然而,唯一的循环引用是由
创建的
push @{$self->{steps}}, sub {
$self->say($s);
我们在第一个闭包中使用 $self。稍后在 make() 中,我们将删除这些步骤和循环引用。但是看起来这个带有 "suprise" 的嵌套闭包会产生问题。例如,如果我们不将 "pony" 传递给 prepare() 输出将如预期的那样好:
awesome: barbie
awesome: flash
destroying at /tmp/t.pl line 43.
那么,即使我们不使用它们,perl 中的嵌套闭包是否捕获了与已经捕获的上层闭包相同的变量?
Perl 以前在嵌套闭包中过度捕获,但从 5.18 开始不再这样做。
$ tail -n 9 a.pl # Modified to make clearer when the object is destroyed.
package main;
{
my $a = Awesome->new;
$a->prepare('barbie','pony','flash');
$a->make();
}
print "done.\n";
$ 5.16.3t/bin/perl a.pl
awesome: barbie
awesome: pony
awesome: flash
And you have surprise: pony
done.
destroying at a.pl line 43 during global destruction.
$ 5.18.2t/bin/perl a.pl
awesome: barbie
awesome: pony
awesome: flash
And you have surprise: pony
destroying at a.pl line 43.
done.
我有这个带有嵌套闭包的示例,它演示了内存泄漏
use v5.10;
use strict;
package Awesome;
sub new {
bless {steps => [], surprise => undef}, shift;
}
sub say {
print "awesome: ", $_[1], "\n";
}
sub prepare {
my ($self, @steps) = @_;
for my $s (@steps) {
push @{$self->{steps}}, sub {
$self->say($s);
if ($s eq 'pony') {
$self->{surprise} = sub {
$s;
}
}
};
}
}
sub make {
my $self = shift;
while (my $step = shift @{$self->{steps}}) {
$step->();
}
if ($self->{surprise}) {
printf("And you have surprise: %s\n", $self->{surprise}->());
}
}
sub DESTROY {
warn "destroying";
}
package main;
my $a = Awesome->new;
$a->prepare('barbie','pony','flash');
$a->make();
我的 perl 的输出是
awesome: barbie
awesome: pony
awesome: flash
And you have surprise: pony
destroying at /tmp/t.pl line 43 during global destruction.
而这个"during global destruction"意味着这个对象不能以正常的方式销毁,因为它有一些循环引用。
然而,唯一的循环引用是由
创建的push @{$self->{steps}}, sub {
$self->say($s);
我们在第一个闭包中使用 $self。稍后在 make() 中,我们将删除这些步骤和循环引用。但是看起来这个带有 "suprise" 的嵌套闭包会产生问题。例如,如果我们不将 "pony" 传递给 prepare() 输出将如预期的那样好:
awesome: barbie
awesome: flash
destroying at /tmp/t.pl line 43.
那么,即使我们不使用它们,perl 中的嵌套闭包是否捕获了与已经捕获的上层闭包相同的变量?
Perl 以前在嵌套闭包中过度捕获,但从 5.18 开始不再这样做。
$ tail -n 9 a.pl # Modified to make clearer when the object is destroyed.
package main;
{
my $a = Awesome->new;
$a->prepare('barbie','pony','flash');
$a->make();
}
print "done.\n";
$ 5.16.3t/bin/perl a.pl
awesome: barbie
awesome: pony
awesome: flash
And you have surprise: pony
done.
destroying at a.pl line 43 during global destruction.
$ 5.18.2t/bin/perl a.pl
awesome: barbie
awesome: pony
awesome: flash
And you have surprise: pony
destroying at a.pl line 43.
done.