在CoffeeScript中:
f = ->
v = 5
g = ->
v
g()
f() # returns 5 as expected
在Ruby中:
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函数一样?
答案 0 :(得分:8)
您并没有真正在Ruby中定义方法内部的方法,但您可以使用lambda
:
def f
v = 5
g = lambda do
v
end
g.call
end
答案 1 :(得分:7)
Ruby具有脚本范围,模块/类/方法定义范围和块范围。只有块创建嵌套范围。因此,您需要使用块来定义方法。值得庆幸的是,有一种方法可以定义采用块的方法:
def f
v = 5
define_method :g do
v
end
g
end
f
# => 5
但是,请注意,不执行您认为的操作(原始代码也不行)。它不定义嵌套在方法g
中的方法f
。 Ruby没有嵌套方法。方法总是属于模块(类是模块),它们不属于方法。
这样做是定义一个方法f
,当你运行它时定义一个方法g
,然后调用该方法。
观察:
methods.include?(:g)
# => true
您现在已经定义了一个名为Object
的新顶级方法(实际上是g
的私有实例方法),并且您将每次{{1}一次又一次地定义它。被调用。
你可能想要的是一个lambda:
f
在对另一个答案的评论中写道:
好的,所以我正在搞乱lambdas,我注意到
def f v = 5 g = -> { v } g.() end f # => 5
v
内g
v
所做的任何事情都没有反映在g
。有没有办法可以对v
粘贴进行更改?
def f
v = 5
g = -> { v = 'Hello' }
g.()
v
end
f
# => 'Hello'
正如您所看到的,v
返回后,对g
的更改“坚持”。
答案 2 :(得分:2)
简短的回答,没有。 Ruby的函数具有与Javascript函数不同的作用域规则。
更长的答案是您可以在Ruby中定义对象,以保留定义它们的范围(称为闭包)。这些在Ruby中称为lambdas,Procs和块。查看here以获得快速概述。
编辑:块不是Ruby对象,但可以将它们转换为Procs,它们是对象。
答案 3 :(得分:1)
要添加其他答案,与CoffeeScript代码最为一致,您可能会写:
f = lambda do
v = 5
g = lambda do
v
end
g[]
end
f[]
答案 4 :(得分: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)在他的书“The Ruby Programming Language:
”中所说了解详情Method对象和Proc对象之间的一个重要区别是Method对象不是闭包。 Ruby的方法旨在完全自包含,并且它们永远不能访问其范围之外的局部变量。因此,Method对象保留的唯一绑定是self的值 - 调用该方法的对象。