closures = []
vals = ('a'..'z').to_a
until vals.empty?
val = vals.shift()
closures << lambda { puts val }
end
closures.each { |l| l.call() }
这个Ruby代码打印&#39; z&#39;每次通话都有点令人惊讶
def closure(val)
lambda {puts val}
end
closures = []
vals = ('a'..'z').to_a
until vals.empty?
val = vals.shift()
closures << closure(val)
end
closures.each { |l| l.call() }
这打印&#39; a&#39;到&#39; z&#39;正如预期的那样。
所以我在这里看到的是Ruby lambdas在创建时捕获参数的某些不当行为
可以通过引用Ruby规范来解释这个效果吗? (我的Ruby是2.2.5p319 / Cygwin)
这应该被报告为Ruby bug跟踪器中的错误吗?
或者这是预期的行为?
或者它已经在Ruby的另一个版本中得到修复?
提前感谢您的回复
更新。这是移植到Perl的相同代码。令人惊讶的是,它按预期工作:
use strict;
use warnings;
my @vals = 'a'..'z';
my @subs = ();
while (@vals) {
my $val = shift @vals;
push @subs, sub { print "$val\n"; };
}
$_->() for @subs;
答案 0 :(得分:1)
我相信这里发生的事情是第一种情况
closures = []
vals = ('a'..'z').to_a
until vals.empty?
val = vals.shift()
closures << lambda { puts val }
end
closures.each { |l| l.call() }
每次将lambda { puts val }
推送到closures
时,您只是推送一种不记得val
的当前值的方法。因此,如果我们在puts val
循环until
的末尾添加行val = 'z'
,那么当您在闭包中调用每个lambda时,您正在调用puts val
, val
的当前值。
在第二种情况下,
def closure(val)
lambda {puts val}
end
closures = []
vals = ('a'..'z').to_a
until vals.empty?
val = vals.shift()
closures << closure(val)
end
closures.each { |l| l.call() }
当您将closure(val)
推入闭包时,ruby能够记住参数的当前值,因此您正在推送closure('a')
,closure('b')
等。现在,当您调用每个时l
中的closures
,您可以打印出一个到z。
答案 1 :(得分:1)
变量是通过引用在Ruby中捕获的,而不是通过值捕获的(在Python,JavaScript和许多其他语言中也是如此)。此外,val
的范围是函数范围,而不是作用于循环内部,因此在循环的每次迭代中都不会得到新的变量val
- 它&# 39; s变量val
;你只是在每次迭代中为它分配另一个值。
在循环的每次迭代中,都会创建一个引用变量val
的闭包 - 完全相同的变量val
。因此,当稍后评估闭包时,它们都读取相同的值 - 此时(单个)变量val
的值。
当你将它传递给方法并在方法中创建闭包时,它会有所不同,因为闭包捕获的变量是方法val
主体中的closure
,作用于该方法。每次调用方法closure
时,都会得到一个新变量val
,其值是传入的值,此后它永远不会被更改(closure
中没有任何内容赋值给它)。因此,当稍后闭包读取该值时,它仍然是创建闭包时传递给closure
的值的值。