这个 Ruby (2.2.3p173) 代码:
class A
def msg
"hello"
end
def p
Proc.new { msg }
end
def lam
lambda { msg }
end
def blk
(1..3).map { msg }.join(" and ")
end
def d1(obj)
obj.define_singleton_method(:say1) { msg }
end
def d2(obj)
bound = msg # <== Why is this needed?
obj.define_singleton_method(:say2) { bound }
end
end
a = A.new
puts a.p.call
puts a.lam.call
puts a.blk
obj = Object.new
a.d1(obj)
begin
# Why does this fail?
puts obj.say1
rescue
puts "caught: #{$!}"
end
a.d2(obj)
puts obj.say2
Run Code Online (Sandbox Code Playgroud)
产生这个输出:
hello
hello
hello and hello and hello
caught: undefined local variable or method `msg' for #<Object:0x00000001a20638>
hello
Run Code Online (Sandbox Code Playgroud)
d1 和 d2 有何不同?为什么除了传递给define_singleton_method的块之外,所有块都看到msg?
更新:
我认为归结为:
Proc 主体具有与 Lisp 和 JavaScript 中的函数类似的词法作用域,这意味着当 Ruby 在 proc 主体中遇到自由变量时,它的值将在 proc 定义的上下文中解析。这就是使闭包成为可能的原因。然而,方法是不同的。方法的上下文是它们绑定到的对象。当 Ruby 在方法体内遇到自由变量时,它假定该变量引用对象上的另一个方法,这使我们不必在相同对象的方法前加上“this”或“self”前缀。
我在这里找到的:关于闭包、方法、过程、范围和 Ruby。
所有这些不同的块都{ msg }必须像 Proc 主体一样工作。define_singleton_method正在获取块并给它方法的规则。
self答案与范围有关
在 ruby 中创建新作用域的方法有3种。类、模块和方法。
您的类创建一个作用域,并且每个方法都创建一个包含特定于它们的绑定的作用域。不过,关闭很特殊。闭包将抓取定义块时周围的绑定,并且块特定的绑定在块结束后消失。例如:
def my_method
#Method scope
x = "Goodbye"
yield("Cruel")
end
x = "Hello"
#Closure says "I am going to grab the local bindings from my scope
my_method {|y| "#{x}, #{y} world" }
Run Code Online (Sandbox Code Playgroud)
当你写代码的时候
obj.define_singleton_method(:say1) { msg }
Run Code Online (Sandbox Code Playgroud)
闭包获取的唯一本地绑定是“obj”,这可以通过修改代码来演示,如下所示:
def d2(obj)
puts "in the scope of method :d2, I have acces to the :msg method: #{methods.include?(:msg)}"
puts "---"
obj.define_singleton_method(:say2) do
puts "in the scope of this closure, I have acces to the :msg method: #{methods.include?(:msg)}"
puts "Things I do have access to: "
puts methods
puts local_variables
end
end
Run Code Online (Sandbox Code Playgroud)
ruby 最重要的部分 self 的简单打印语句将向您展示您正在不同的范围内进行操作。查看下面的代码:
def d2(obj)
puts "in the scope of method :d2, I am operating as #{self}"
puts "---"
obj.define_singleton_method(:say2) do
puts "in the scope of this closure, I am operating as #{self}"
end
end
Run Code Online (Sandbox Code Playgroud)
简而言之,原因是范围。每当你声明bound = msg你正在将内容设置msg为方法的本地内容,然后闭包就可以获取本地绑定值msg。
如果您想了解更多有关其工作原理的信息,我强烈推荐“The Pragmatic Programmers - Metaprogramming Ruby”,您将学到很多关于 self 和闭包的知识。 http://www.amazon.com/Metaprogramming-Ruby-Program-Like-Facets/dp/1941222129
----编辑----“为什么是”
def p
Proc.new { msg }
end
不同于
def d2(obj)
obj.define_singleton_method(:say2) { msg }
end
它是不同的,因为块内的 self 是不同的。在方法定义“p”内,该块可以访问实例变量和方法,而方法“d2”有一个只能访问对象的块。我们可以通过一点猴子补丁来证明这一点。添加此代码:
class Object
def msg
"GoodBye"
end
| 归档时间: |
|
| 查看次数: |
548 次 |
| 最近记录: |