关闭如何为Ruby的define_singleton_method工作?

时间:2016-04-12 20:22:40

标签: ruby closures

这个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

生成此输出:

hello
hello
hello and hello and hello
caught: undefined local variable or method `msg' for #<Object:0x00000001a20638>
hello

d1和d2有什么区别?为什么所有的块都看到了msg,除了传递给define_singleton_method的那个?

更新

我认为归结为:

  

Proc主体有一个像Lisp和。中的函数一样的词法范围   JavaScript,意思是当Ruby遇到一个自由变量时   proc体,它的值在proc的上下文中被解析   定义。这就是使封闭成为可能的原因。方法不同,   然而。方法的上下文是它们绑定的对象。   当Ruby遇到方法体内的自由变量时,它会假定   变量引用对象的另一个方法,这就是什么   使我们不必使用&#34;这个&#34;前缀同一对象方法。要么   &#34;自&#34;

我在这里找到:Of closures, methods, procs, scope, and Ruby

{ msg }所有这些不同的块必须像Proc主体那样工作。 define_singleton_method正在采取措施并为其提供方法规则。

1 个答案:

答案 0 :(得分:1)

答案与self和范围

有关

在ruby中创建新范围有三种方法。 类,模块和方法。

您的类创建了一个范围,您的每个方法都创建一个包含特定于它们的绑定的范围。闭包很特别。当您定义块时,闭包将捕获周围的绑定,并且块结束后块特定绑定将消失。例如:

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" }

什么时候编写代码

obj.define_singleton_method(:say1) { msg }

闭包抓取的唯一本地绑定是&#39; obj&#39; 这可以通过修改代码来证明:

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

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

简而言之,原因在于范围。每当您声明bound = msg时,您正在使msg的内容为方法本地,然后闭包然后可以获取msg的本地绑定值。

如果你想了解更多关于它是如何工作的,我强烈推荐&#34;实用程序员 - 元编程Ruby&#34;你将学到很多关于自我和闭包的知识。 http://www.amazon.com/Metaprogramming-Ruby-Program-Like-Facets/dp/1941222129

---- ---- EDIT &#34;为什么&#34;

def p Proc.new { msg } end

不同

def d2(obj) obj.define_singleton_method(:say2) { msg } end

这是不同的,因为块内的自我是不同的。在方法定义&#34; p&#34;中,块可以访问实例变量和方法,而方法&#34; d2&#34;有一个只能访问Object的块。我们可以通过一点点monkeypatching来证明这一点。添加以下代码:

class Object def msg "GoodBye" end