3

define_methodは、次の動作を示します。

class TestClass
  def exec_block(&block) ; yield ; end
end
TestClass.new.send(:exec_block) do ; puts self ; end
# -> main
TestClass.send(:define_method, :bing) do ; puts self ; end
TestClass.new.bing
# -> <TestClass:...>

私が理解していないのは、define_methodに渡されたブロックがクロージャであると想定されているということです。そのため、(少なくとも私の理解によれば)を呼び出すときに示されるように、asselfの値をキャプチャする必要があります。mainexec_block

ブロックがメソッドの本体になることは理解していますが、動作の理由はわかりません。さまざまな方法で使用したときに、ブロックがさまざまなものに評価されるのはなぜですか?

他の方法でブロックの動作を再現するにはどうすればよいdefine_methodですか?つまり、 `main´の代わりにexec_block出力するように書くにはどうすればよいですか?<TestClass:...>

4

3 に答える 3

5

self他の変数と同様に、クロージャによってキャプチャされます。Procさまざまなオブジェクトインスタンスを渡すことで、次のことを確認できます。

class A
  def exec_block(&block)
    block.call
  end
end

class B
  def exec_indirect(&block)
    A.new.exec_block(&block)
  end
end

block = proc { p self }
a = A.new; b = B.new

a.exec_block(&block)    # => main
b.exec_indirect(&block) # => main

ただし、同様に、変数を動的にBasicObject#instance_eval再バインドします。self

コンテキストを設定するために、コードの実行中に変数がobjselfに設定され、コードがobjのインスタンス変数にアクセスできるようになります。

Module#define_methodinstance_eval次に、関連するブロックを実行するために使用します。

ブロックを指定すると、メソッド本体として使用されます。このブロックはinstance_evalを使用して評価されます[...]

観察:

A.send(:define_method, :foo, &block)
a.foo                   # => #<A:0x00000001717040>
a.instance_eval(&block) # => #<A:0x00000001717040>

その知識があれば、次のように書き直すことができexec_blockますinstance_eval

class A
  def exec_block(&block)
    instance_eval(&block)
  end
end

block = proc { p self }
A.new.exec_block(&block)  # => #<A:0x00000001bb9828>

前述のように、コンテキストが変更されinstance_evalたインスタンスを実行するには、を使用することが唯一の方法のようです。Rubyで動的バインディングProcを実装するために使用できます。

于 2012-04-18T14:09:15.787 に答える
0

NiklasBのコメントに触発されました。:

class TestClass
  def exec_block(&block) ; yield ; end
end
s = self

TestClass.new.send(:exec_block) do ; puts s ; end
# -> main

TestClass.send(:define_method, :bing) do ; puts s ; end
TestClass.new.bing
# -> main
于 2012-04-18T12:51:47.707 に答える
0

まず、ブロックを明示的にメソッドに渡す場合は、yieldを使用できますblock.call。2番目のこと-メソッドの内部exec_blockをに置き換えるyieldinstance_eval(&block)、魔法が表示されます;)

もう少し明確にします-最初の例では、ブロックはオブジェクトselfを指している変数とともにローカルスコープをキャッチしmainます。

2番目の例(with define_method)では、ブロックは新しいメソッド本体として扱われ、を使用してオブジェクトのスコープ内で評価されますinstance_eval。詳細については、http://apidock.com/ruby/Module/define_methodを確認してください。

于 2012-04-18T10:20:22.617 に答える