Ruby中的闭包和for循环

5 ruby closures

我是Ruby的新手,一些关闭逻辑让我很困惑.考虑以下代码:

array = []
for i in (1..5)
  array << lambda {i}
end
array.map{|f| f.call} # => [5, 5, 5, 5, 5]
Run Code Online (Sandbox Code Playgroud)

这对我来说很有意义,因为我被绑定在循环之外,因此每次循环都会捕获相同的变量.我也觉得使用每个块可以解决这个问题:

array = []
(1..5).each{|i|  array << lambda {i}}
array.map{|f| f.call} # => [1, 2, 3, 4, 5]
Run Code Online (Sandbox Code Playgroud)

...因为我现在每次都被单独宣布.但现在我迷路了:为什么我不能通过引入一个中间变量来修复它?

array = []
for i in 1..5
  j = i
  array << lambda {j}
end
array.map{|f| f.call} # => [5, 5, 5, 5, 5]
Run Code Online (Sandbox Code Playgroud)

因为j每次循环都是新的,我认为每次传递都会捕获一个不同的变量.例如,这绝对是C#的工作原理,以及我认为 - Lisp的行为与let有关.但在Ruby中并没有那么多.真的发生了什么?

编辑:查看答案中的评论; 问题似乎是j仍然在循环之外的范围内.循环中的范围如何真正起作用?

编辑:我想我还是不明白; if循环不创建新范围,为什么:

for i in 1..5
  puts j if i > 1 #undefined local variable or method `j' for main:Object (NameError)
  j = i
end
Run Code Online (Sandbox Code Playgroud)

Jör*_*tag 9

好的,这太荒谬了.每当我尝试回答有关for循环如何在Ruby中工作的问题时,我就错了.

这样做的原因当然是我不在forRuby中使用循环,其他人也不使用循环,所以对我来说真的没关系:-)

无论如何,为了一劳永逸地解决这个问题,我直接找到了最终资源,即2009年12月1日的IPA Ruby语言规范草案(注定要成为ISO Ruby语言规范):

§11.4.1.2.3 for表达式

句法

  • for-expression for for-variable in 表达式 do子句 end
  • for-variable 左侧 | 多左手侧

表达的表达不应是跳的表达.

语义

用于表达如下评价:

  1. 评估表达式.让我们O因此而产生的价值.
  2. E初级方法调用形式的基本表达式 [无线-终止这里] .each do | 块正规参数列表 | 块体 end,其中所述的值基本表达式O,则块正规参数列表为变量,所述块体是所述化合物语句的的do-子句.

    评估E,但跳过§11.2.2的步骤c.

  3. 所述的值对于表达是所得到的调用的值.

好吧,基本上这意味着

for for_variable in expression
  do_clause
end
Run Code Online (Sandbox Code Playgroud)

被翻译成

O = expression
O.each do |for_variable|
  do_clause
end
Run Code Online (Sandbox Code Playgroud)

或者,在您的情况下:

for i in 1..5
  puts j if i > 1 #undefined local variable or method `j' (NameError)
  j = i
end
Run Code Online (Sandbox Code Playgroud)

被翻译成

(1..5).each do |i|
  puts j if i > 1 #no excpetion here, works just fine ??!!??
  j = i
end
Run Code Online (Sandbox Code Playgroud)

啊哈!但我们忘记了什么!这是一个不祥的"跳过§11.2.2的步骤c".事情!那么,它说什么呢?

  • 将一组空的局部变量绑定推送到⟦local-variable-bindings⟧.

注意步骤b

  • 将执行上下文设置为Eb.

不是跳过.

因此,据我所知,for循环获得自己的执行上下文,它以当前执行上下文的副本开头,但它没有获得自己的一组局部变量绑定.IOW:它获得自己的动态执行上下文,但不是它自己的词法范围.

我必须承认,我仍然不确定我是否完全理解它,但它没有比这更精确.