本文介绍了Ruby中的闭包和for循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是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 的评估如下:


  1. 评估表达式。假设 O 为结果值。

  2. 让 E 为 primary-expression [此处没有行终止符] block-body ,其中的值primary-expression 是 O , block-formal-argument-list for-variable 块体子句复合语句

  1. Evaluate the expression. Let O be the resulting value.
  2. 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循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-27 06:56