关闭如何为Ruby的define_singleton_method工作?

这个
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 bodies have a lexical scope like functions in Lisp and
JavaScript, meaning that when Ruby encounters a free variable inside a
proc body, its value is resolved within the context the proc was
defined. This is what makes closures possible. Methods are different,
however. A method’s context is the object to which they are bound.
When Ruby encounters a free variable inside a method body, it assumes
the variable refers to another method on the object, and this is what
saves us from having to prefix same-object methods with “this” or
“self”.

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

具有{msg}的所有这些各种块必须像Proc主体那样工作. define_singleton_method正在获取块并为其提供方法规则.

最佳答案 答案与自我和范围有关

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

闭包抓取的唯一本地绑定是’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

一个关于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的本地绑定值.

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

– – 编辑 – –
“为什么是”

def p
   Proc.new {msg}
结束

不同于

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

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

class Object
  def msg
     “再见”
  结束

点赞