closures = []
vals = ('a'..'z').to_a
until vals.empty?
val = vals.shift()
closures << lambda { puts val }
end
closures.each { |l| l.call() }这段Ruby代码为每个调用打印'z‘,这有点令人惊讶
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() }这会按照预期将'a‘打印到'z’。
因此,我在这里看到的是Ruby lambdas中的某些错误行为,它们在创建时捕获了它们的参数
有谁可以引用Ruby规范来解释这种效果吗?(我的Ruby是2.2.5p319/Cygwin)
这应该在Ruby错误跟踪器中被报告为错误吗?
或者这是一种预期的行为?
或者它已经在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;发布于 2016-10-06 08:24:32
在Ruby语言中,变量是通过引用捕获的,而不是通过值(在Python、JavaScript和许多其他语言中也是如此)。此外,val的作用域是函数作用域,而不是循环内部的作用域,因此您不会在每次循环迭代中都获得一个新的变量val --它是同一个变量val;您只是在每次迭代中为它赋值。
在循环的每次迭代中,都会创建一个引用变量val的闭包--完全相同的变量val。因此,当稍后计算闭包时,它们都读取相同的值--当时(单个)变量val的值。
当您将其传递给方法并在方法中创建闭包时,情况就不同了,因为闭包捕获的变量是方法closure主体中的val,作用域为该方法。每次调用closure方法时,都会得到一个新的变量val,它的值就是传入的值,此后它就再也不会改变了( closure中的任何内容都不会赋值给它)。因此,当该值稍后被闭包读取时,它仍然是创建闭包时传递给closure调用的值。
发布于 2016-10-06 03:38:31
我相信这里发生的事情是在第一种情况下
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的当前值的方法。因此,如果我们在until循环val = 'z'的末尾添加puts val行,那么当您调用闭包中的每个lambda时,您将使用val的当前值调用puts 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')等。现在,当你在closures中调用每个闭包时,你可以打印出a到z。
https://stackoverflow.com/questions/39882131
复制相似问题