问题描述
我是Ruby的新手,有些闭包逻辑让我感到困惑。考虑以下代码:
I'm kind of new to Ruby and some of the closure logic has me a confused. Consider this code:
array = [] for i in (1..5) array << lambda {i} end array.map{|f| f.call} # => [5, 5, 5, 5, 5]
这对我来说很有意义,因为我被约束在外面循环,因此每次循环都捕获相同的变量。对我来说,使用每个块也可以解决此问题:
This makes sense to me because i is bound outside the loop, so the same variable is captured by each trip through the loop. It also makes sense to me that using an each block can fix this:
array = [] (1..5).each{|i| array << lambda {i}} array.map{|f| f.call} # => [1, 2, 3, 4, 5]
...因为我现在分别声明了对于每次通过。但是现在我迷路了:为什么我还不能通过引入中间变量来解决它?
...because i is now being declared separately for each time through. But now I get lost: why can't I also fix it by introducing an intermediate variable?
array = [] for i in 1..5 j = i array << lambda {j} end array.map{|f| f.call} # => [5, 5, 5, 5, 5]
由于j每次在循环中都是新的,我认为每次通过都会捕获一个不同的变量。例如,这绝对是C#的工作方式,而且-我认为-Lisp的举止行为。但是在Ruby中并没有那么多。
Because j is new each time through the loop, I'd think a different variable would be captured on each pass. For example, this is definitely how C# works, and how -- I think-- Lisp behaves with a let. But in Ruby not so much. What's really happening?
编辑:查看答案中的注释;否则,将发生什么?问题似乎是j仍然在循环之外。
See comments in the answers; the problem seems to be that j is still in scope outside the loop. How does scope in loops really work?
编辑:我想我还是不太明白。如果循环不创建新的作用域,为什么这样做:
I guess I still don't understand; if loops don't create new scopes, why this:
for i in 1..5 puts j if i > 1 #undefined local variable or method `j' for main:Object (NameError) j = i end
推荐答案
好的,这太荒谬了。每次我尝试回答有关 for 循环如何在Ruby中工作的问题时,我都会弄错。
Okay, this is getting ridiculous. Every time I try to answer a question about how for loops work in Ruby, I get it wrong.
原因当然是我在Ruby中不使用 循环,其他人也没有,所以对我来说真的没有关系:-)
The reason for this is, of course, that I don't use for loops in Ruby, neither does anybody else, so it really doesn't matter to me :-)
无论如何,要一劳永逸地解决问题,我直接去了,即IPA Ruby语言规范(旨在成为ISO Ruby语言规范)的2009年12月1日初步草案:
Anyway, to settle the matter once and for all, I went straight to the ultimate source, the December 1, 2009 preliminary Draft of the IPA Ruby Language Specification (destined to become the ISO Ruby Language Specification):
for-expression的 expression 不能是表达式。
A for-expression 的评估如下:
- 评估表达式。假设 O 为结果值。
-
让 E 为 primary-expression [此处没有行终止符] block-body ,其中的值primary-expression 是 O , block-formal-argument-list 是 for-variable ,块体是子句的复合语句。
- Evaluate the expression. Let O be the resulting value.
Let E be the primary-method-invocation of the form primary-expression [no line-terminator here] block-formal-argument-list block-body , where the value of the primary-expression is O,the block-formal-argument-list is the for-variable, the block-body is the compound-statement of the do-clause.
评估 E ,但跳过第11.2.2节的步骤c。
Evaluate E, but skip Step c of §11.2.2.
for-expression 是调用的结果值。
好的,所以基本上这意味着
Okay, so basically this means that
for for_variable in expression do_clause end
被翻译为
O = expression O.each do |for_variable| do_clause end
或者,在您的情况下:
for i in 1..5 puts j if i > 1 #undefined local variable or method `j' (NameError) j = i end
被翻译为
(1..5).each do |i| puts j if i > 1 #no excpetion here, works just fine ??!!?? j = i end
啊哈!但是我们忘记了一些东西!这个不祥的跳过第11.2.2节的步骤c。事情!那么,它怎么说呢?
Aha! But we forgot something! There's this ominous "skip Step c of §11.2.2." thing! So, what does it say?
请注意,步骤b
不跳过 。
因此,据我所知, for 循环具有其自己的执行上下文,该上下文以当前执行上下文的副本,但是它没有获得自己的局部变量绑定集。 IOW:它有自己的动态执行上下文,但没有它自己的词法范围。
So, as far as I can see, a for loop gets its own execution context, which starts out as a copy of the current execution context, but it does not get its own set of local variable bindings. IOW: it gets its own dynamic execution context, but not its own lexical scope.
我必须承认,我仍然不确定我是否完全理解它,但是它没有没有比这更精确的了。
I must admit, I'm still not sure I fully understand it, but it doesn't get any more precise than this.
这篇关于Ruby中的闭包和for循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!