4

Ruby メタプログラミングの初心者です。irb でコードを練習しているときに、この問題に遭遇しました。

class A; end
a = A.new
b = class << a; self; end

b.instance_eval { def foo; puts 'foo'; end }
# => works for b.foo

b.instance_eval { define_method :bar do; puts 'bar'; end }
# => WHY this one works for a.bar rather than b.bar

最後のコード片は私を混乱させました。


具体的な回答をありがとうございますが、混乱を明確に説明していない可能性があります。私が本当に理解しようとしているのはdefine_method、これらの場合になぜ動作が異なるのかということです。

class A
  def foo1
    p 'foo1 from A'
  end 

  define_method :bar1 do
    p 'bar1 from A'
  end 
end

a = A.new
a.foo1 # => 'foo1 from A'
a.bar1 # => 'bar1 from A'


a.instance_eval { def foo2; p 'foo2 from a.metaclass'; end }
a.foo2 # => 'foo2 from a.metaclass'
a.instance_eval { define_method :bar2 do; p 'bar2 from a.metaclass'; end }
# => NoMethodError: undefined method `define_method' for #<A:0x000000016a2e70>

aa = class << a; self; end
aa.instance_eval { def foo3; p 'foo3 from a.metaclass.metaclass'; end }
aa.foo3 # => 'foo3 from a.metaclass.metaclass'
aa.instance_eval { define_method :bar3 do; p 'bar3 from a.metaclass.metaclss'; end }
aa.bar3 # => NoMethodError: undefined method `bar3' for #<Class:#<A:0x000000016a2e70>>
a.bar3 # => 'bar3 from a.metaclass.metaclss'

これは日常のコーディングでは出てこないことはわかっていますが、はっきりさせておきたいと思います。


結論を出す:

aa = class << a; self; end 

aa.instance_eval { def foo; puts 'foo..'; end }

# defines a singleton-method for aa
aa.foo # => 'foo...'


aa.instance_eval { define_method :bar do; puts 'bar..'; end }
# equals
aa.class_eval { def bar; puts 'bar..'; end }

# both define a singleton-method for a,
# as define_method and class_eval both define instance_method
a.bar # => 'bar...'
4

3 に答える 3

2

他のすべてのコメントに加えて:

[つるはしから] Object#instance_eval メソッドを使用すると、self を任意のオブジェクトに設定し、ブロック内のコードを [self] で評価してから、self をリセットできます。
Module#define_method : レシーバーでインスタンス メソッドを定義します [自己、(匿名の) クラスまたはモジュールである必要があります]。

singleton_class_of_object_a = aa = class << a; self; end
aa.instance_eval { def foo3; puts "foo3 from singleton class of a, self=#{self}"; end }
aa.foo3 # => foo3 from singleton class of a, self=#<Class:#<A:0x007fc2e4049e68>>
aa.instance_eval do
    puts "about to define_method :bar3 in self=#{self}"
    define_method :bar3 do; puts "bar3 from singleton class of a, self=#{self}"; end
end # => about to define_method :bar3 in self=#<Class:#<A:0x007fc2e4049e68>>
a.bar3 # => bar3 from singleton class of a, self=#<A:0x007fc2e4049e68>

define_method :bar3singleton_class_of_object_a (匿名クラス、以下を参照) のコンテキストで実行されるため、そのクラスのインスタンス メソッドが定義されるため、bar3 は a のシングルトン メソッドになります。以前の回答で既に述べたように、 object で直接定義するのと同じです:

def a.bar4; puts 'bar4 from singleton class of a' end
a.bar4 # => bar4 from singleton class of a

p a.singleton_methods.sort # => [:bar3, :bar4, :foo2]
p a.methods(false).sort # => [:bar3, :bar4, :foo2]  


の後、インスタンスaa = A.newのフィールド クラスはクラスAを指します。orを 使用すると、Ruby は無名クラスを作成し、インスタンスaのフィールド クラスはこの無名クラスを指し、そこからAを指します。 このコンテキストで定義されたメソッドは、無名クラスのメソッド テーブルを使用するか、メソッド テーブルに移動します。
class << adef a.bar4
defdefine_method

于 2012-12-09T19:36:55.673 に答える
1
b.instance_eval { def foo; puts 'foo'; end }
b.instance_eval { puts "self in b.instance_eval block=#{self}" }
#=> self in b.instance_eval block=#<Class:#<A:0x007fe3c204d000>>
b.foo #=> foo

A の単一インスタンスのシングルトン クラスでメソッドを定義しています。かなり複雑に見えます。代わりに、インスタンスでシングルトン メソッドを直接定義します。

cat = String.new("cat")
def cat.speak
    'miaow'
end
cat.speak #=> "miaow" 
cat.singleton_methods #=> ["speak"] 

などのclass <<表記

singleton_class_of_A = eigenclass_of_A = class << A; self; end

通常、定義された「クラス メソッド」(実際には A のシングルトン メソッド)、またはクラスのインスタンス変数に使用されます。

class B
    class << self
        def my_class_method_of_B
            puts "my_class_method_of_B"
        end
        @my_first_variable_of_class_B = 123
        attr_accessor :my_second_variable_of_class_B
    end
end

B.my_class_method_of_B
print 'singleton methods of B : '
p B.singleton_methods
print 'instance variables of B : '
p B.instance_variables
print 'class variables of B : '
p B.class_variables
print '"singleton variables" of B : '
class << B; p instance_variables end

出力:

my_class_method_of_B
singleton methods of B : [:my_class_method_of_B, :my_second_variable_of_class_B, :my_second_variable_of_class_B=]
instance variables of B : []
class variables of B : []
"singleton variables" of B : [:@my_first_variable_of_class_B]

ご覧のとおり、この問題は簡単ではありません。http://pragprog.com/book/ppmetr/metaprogramming-rubyを参照してください。

于 2012-12-08T15:09:23.740 に答える
1

あなたの場合def fooは として機能しdef self.foo
define_method :barとして機能するためdef barです。

これが発生するとinstance_eval、クラス メソッドが作成されます

あなたのコードは以下と同じです:

class << a
  def self.foo
    puts 'foo'
  end

  def bar
    puts 'bar'
  end
end

したがって、fooメソッドはaの固有クラス内で定義され
barメソッドはそれ自体内で定義されaます。

代わりにa.foo使用する必要がある場合。class_eval

class_evalインスタンスメソッドを作成する原因となります:

b.class_eval { def foo; puts 'foo'; end }
a.foo
# => foo
于 2012-12-08T08:45:31.597 に答える