了解Ruby闭包

wmo*_*ock 3 ruby closures

我试图更好地理解Ruby闭包,我遇到了这个我不太明白的示例代码:

def make_counter
  n = 0
  return Proc.new { n = n + 1 }
end

c = make_counter
puts c.call # => this outputs 1
puts c.call # => this outputs 2
Run Code Online (Sandbox Code Playgroud)

当有人打电话时,有人可以帮我理解上面代码中究竟发生了什么c = make_counter吗?在我看来,这就是我的想法:

Ruby调用该make_counter方法并返回一个Proc对象,其中与Proc关联的代码块将在其中{ n = 1 }.c.call执行第一个时,Proc对象将执行与之关联的块,然后返回n = 1.但是,当第二个c.call执行时,Proc对象是否仍然执行与之关联的块,这仍然是{ n = 1 }?我不明白为什么输出会变为2.

也许我根本就不理解这一点,如果你能对Ruby中实际发生的事情做一些澄清会有所帮助.

Pin*_*nyM 8

make_counter调用时不会计算块.当您调用Proc via时,将评估并运行该块c.call.因此,每次运行时c.call,n = n + 1都将评估并运行表达式.Proc的绑定将导致n变量保留在范围内,因为它(局部n变量)首先在Proc闭包之外声明.因此,n将在每次迭代时保持递增.

进一步澄清:

  • 定义Proc(或lambda)的块在初始化时不会被评估 - 其中的代码与您看到的完全一样.
  • 好的,代码实际上是"评估的",但不是为了更改冻结的代码.而是检查当前在范围内的任何变量,这些变量在Proc的代码块的上下文中使用.由于n是一个局部变量(因为它之前已定义了该行),并且它在Proc中使用,因此它会在绑定中捕获并随之出现.
  • callProc上调用该方法时,它将在已捕获的绑定的上下文中执行"冻结"代码.因此n,最初被指定为0 的那个增加到1.再次调用时,同样n会再次增加到2.依此类推...