5

在 CoffeeScript 中:

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

f() # returns 5 as expected

在红宝石中:

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

好的,显然 JavaScript 函数被设置为在创建它们的范围内捕获变量,但 Ruby 的方法不是。有没有办法让 Ruby 方法在这方面表现得像 JavaScript 函数?

4

5 回答 5

9

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

def f
  v = 5
  define_method :g do
    v
  end
  g
end
f
# => 5

但是,请注意,这不会您认为的那样做(您的原始代码也不会)。它没有定义g嵌套在 method 中的方法f。Ruby 没有嵌套方法。方法总是属于模块(类就是模块),它们不能属于方法。

它的作用是定义一个方法f,当您运行它时,它定义一个方法g,然后调用该方法。

观察:

methods.include?(:g)
# => true

您现在已经定义了一个名为 的新顶级方法(实际上是 的私有实例方法Objectg,并且您将一遍又一遍地定义它,每次f都会被调用。

你可能想要的是一个 lambda:

def f
  v = 5
  g = -> { v }
  g.()
end
f
# => 5

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

好的,所以我在搞乱 lambdas,我注意到我对v里面所做的任何事情g都不会反映在v一次g返回中。有没有办法让我的改变v坚持下去?

def f
  v = 5
  g = -> { v = 'Hello' }
  g.()
  v
end
f
# => 'Hello'

如您所见,返回后更改为v 坚持” g

于 2013-08-03T01:08:49.057 回答
8

您并没有真正在 Ruby 中的方法中定义方法,但您可以使用lambda

def f
  v = 5

  g = lambda do
    v
  end

  g.call
end
于 2013-08-02T23:15:44.620 回答
2

简短的回答,不。Ruby 的函数与 Javascript 函数有不同的作用域规则。

更长的答案是您可以在 Ruby 中定义对象,以保留定义它们的范围(称为闭包)。这些在 Ruby 中称为 lambdas、Procs 和 blocks。在这里查看快速概览。

编辑:块不是 Ruby 对象,但它们可以转换为 Procs,它们是对象。

于 2013-08-02T23:20:40.320 回答
1

要添加到其他答案,为了与您的 CoffeeScript 代码保持最一致,您可能会写:

f = lambda do
  v = 5

  g = lambda do
    v
  end

  g[]
end
f[]
于 2013-08-05T05:17:20.640 回答
1

闭包是一个命名的代码块(某些语言中的函数,Ruby 中的 lambda 或 proc),具有以下特征:

  • 它会记住在其定义的范围内(外部范围)可用的所有变量的值,即使在另一个范围内调用或这些变量不再可用(超出范围)时也是如此。
  • 它是一个对象。因此,它可以分配给一个变量,传递,从不同的范围内调用,等等。

下面是一个使用 Lambda 的示例。使用 Proc 也可以做到这一点。

minutes = 40
def meditate minutes
    return lambda { minutes }
end

p = meditate minutes
minutes = nil
p.call

# Output:
=> 40

请注意,lambda 是在 meditate 方法中创建的,该方法需要几分钟作为参数。后来,当我们调用 lambda 时,冥想方法已经消失了。我们还将分钟的值设置为零。尽管如此,lambda 还是记住了“分钟”的值。

Ruby 有两种类型的闭包:Lambdas 和 Procs。

方法不是闭包,Method 对象也不是。正如 Yukihiro Matsumoto (Matz) 在他的《Ruby 编程语言》一书中所说:

Method 对象和 Proc 对象之间的一个重要区别是 Method 对象不是闭包。Ruby 的方法旨在完全自包含,并且它们永远无法访问其范围之外的局部变量。因此,Method 对象保留的唯一绑定是 self 的值 - 要在其上调用方法的对象。

在 Zen Ruby 博客中有关方法的这篇文章中查看更多信息。

于 2016-05-08T20:43:51.597 回答