2016-04-12 18 views
3

このルビー(2.2.3p173)コード:Rubyのdefine_singleton_methodのクロージャはどのように機能しますか?

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に違いは何ですか? define_singleton_methodに渡されたものを除いて、すべてのブロックがmsgを参照するのはなぜですか?

更新:

私はそれがこれに尽きると思う:

PROC本体

Rubyは procの内部で自由変数に遭遇したときにことを意味し、Lispや のJavaScriptの関数のようなレキシカルスコープを持っていますその値は、procが と定義されたコンテキスト内で解決されます。これがクロージャーを可能にするものです。しかしながら、方法は異なっている、 しかし。メソッドのコンテキストは、それらがバインドされているオブジェクトです。 Rubyでメソッド本体の空き変数が見つかると、 変数がオブジェクトの別のメソッドを参照しているとみなします。これは が "this"または "self"で同じオブジェクトメソッドの接頭辞を取らないようにします。

これは私がここに見つけた:Of closures, methods, procs, scope, and Rubyです。

{ msg }のこれらのさまざまなブロックはすべて、Proc本体として機能する必要があります。 define_singleton_methodはブロックを取得し、メソッドのルールを与えています。

答えて

1

答えはselfに関係しているとルビーの新しいスコープを作成するには、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" } 

あなたがコード

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​​の最も重要な部分の簡単なprint文は、あなたがさまざまな範囲で操作していることを示します。以下のコードをチェックしてください:

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を受け取ることができます。

この動作の詳細については、「Pragmatic Programmers-Metaprogramming Ruby」を読むことをお勧めします。自分自身とクローズについてたくさん学ぶでしょう。 http://www.amazon.com/Metaprogramming-Ruby-Program-Like-Facets/dp/1941222129

---- EDIT ---- ブロック内部の自己が異なるため

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

とは異なる

def p Proc.new { msg } end

"なぜ" それは違います。メソッド定義 "p"の内部では、ブロックはインスタンス変数とメソッドにアクセスできますが、メソッド "d2"にはObjectにしかアクセスできないブロックがあります。私たちは少しのモノスケッチでこれを証明することができます。このコードを追加します。

class Object def msg "GoodBye" end

+0

私はバウンド= MSGは見るために閉鎖のためのバインディングを作成することを理解しています。しかし、これはProc.new {msg}とどう違うのですか?このブロック{msg}はmsgをどのように見ますか?このブロック{msg}はありません。 –

+0

私は答えを推測するつもりです:マップに渡されたProc、lambda、block内のこのブロック{msg}はself#msgに解決されなければならず、define_instance_methodのselfが明らかにobjであるインスタンスに設定されていなければなりません。私はこれをさらに調べるためにあなた自身の考えを試してみましょう。ありがとう。 –

+0

私は私の答えがあると思う。私の質問に私の編集を参照してください。 –

関連する問題