在Ruby方法中捕获变量

Mic*_*rst 5 javascript ruby closures coffeescript

在CoffeeScript中:

f = ->
  v = 5
  g = ->
    v
  g()

f() # returns 5 as expected
Run Code Online (Sandbox Code Playgroud)

在Ruby中:

def f
  v = 5
  def g
    v # undefined local variable or method `v' for main:Object (NameError)
  end
  g
end
f
Run Code Online (Sandbox Code Playgroud)

好吧,显然JavaScript函数被设置为捕获它们创建范围内的变量,但Ruby的方法却没有.有没有办法让Ruby方法在这方面像JavaScript函数一样?

Jör*_*tag 9

Ruby具有脚本范围,模块/类/方法定义范围和块范围.只有块创建嵌套范围.因此,您需要使用块来定义方法.值得庆幸的是,有一种方法可以定义采用块的方法:

def f
  v = 5
  define_method :g do
    v
  end
  g
end
f
# => 5
Run Code Online (Sandbox Code Playgroud)

但是,请注意,这并不会做你认为它(而且也没有你原来的代码).它没有定义g嵌套在方法中的方法f.Ruby没有嵌套方法.方法总是属于模块(类是模块),它们不属于方法.

这样做是定义一个方法f,当你运行它时定义一个方法g,然后调用该方法.

注意:

methods.include?(:g)
# => true
Run Code Online (Sandbox Code Playgroud)

您现在已经定义了一个新的顶级方法(实际上是一个私有实例方法Object)g,并且您将在每次f调用时反复定义它.

你可能想要的是一个lambda:

def f
  v = 5
  g = -> { v }
  g.()
end
f
# => 5
Run Code Online (Sandbox Code Playgroud)

在对另一个答案的评论中写道:

好吧,所以我正在搞乱lambdas,我注意到我v内心所做的任何事情g都没有反映在v一次g回归中.有没有办法让我的变化v坚持下去?

def f
  v = 5
  g = -> { v = 'Hello' }
  g.()
  v
end
f
# => 'Hello'
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,变化v 确实 "大棒"之后g的回报.


tad*_*man 8

你没有在Ruby中定义方法内部的方法,但你可以使用lambda:

def f
  v = 5

  g = lambda do
    v
  end

  g.call
end
Run Code Online (Sandbox Code Playgroud)