2009-09-15 5 views
19

Fooをinstance_evalで定義すると、違いはありますか。 。 。'yield self'はinstance_evalと同じですか?

class Foo 
    def initialize(&block) 
     instance_eval(&block) if block_given? 
    end 
    end 

。 。 。あるいは「降伏自己」で:だから「yield self

x = Foo.new { def foo; 'foo'; end } 
x.foo 

Foo.new後のブロックは常にのコンテキストで評価されることを意味します

class Foo 
    def initialize 
    yield self if block_given? 
    end 
end 

どちらの場合も、あなたはこれを行うことができますFooクラス。

これは間違いありませんか?

答えて

14

2つのコードは非常に異なるものです。 instance_evalを使用すると、オブジェクトのコンテキストでブロックを評価していることになります。つまり、defを使用すると、そのオブジェクトのメソッドが定義されます。また、ブロック内に受信機を持たないメソッドを呼び出すと、それをあなたのオブジェクト上で呼び出すこともできます。

selfを生成するときには、selfを引数としてブロックに渡しますが、ブロックは引数をとらないので無視されます。したがって、この場合、自己を産むことは何もしないことと同じことをします。 defはブロック外のdefとまったく同じように振る舞いますが、selfを生成すると実際にメソッドを定義する内容は変更されません。あなたができることは:

class Foo 
    def initialize 
    yield self if block_given? 
    end 
end 
x = Foo.new {|obj| def obj.foo() 'foo' end} 
x.foo 

受信者を明示的に指定する必要があるということです。収率バージョンで

を、OBJブロックで、この場合に新たに作成されたFooのインスタンスであり得ているオブジェクトを、次のようになります

編集を明確にします。自己はブロックの外にあるのと同じ価値を持ちます。ブロック内のinstance_evalバージョンselfは、新しく作成されたFooインスタンスになります。

+0

あなたの "Edit to clarify"では、ブロック内のobjに自己が与えられたということではありませんか?たぶん私は別の方法でそれを読んでいるだけですが、私はオブジェクトが初期化されているのを見て、自己は 'obj'としてブロックに与えられ、ブロックの中でメソッドfooはobjを通してselfに定義されます。 – uzo

+1

私は確かに同じことを意味します。私は "新しく作成されたFooインスタンス"を書きました。なぜなら、initializeメソッド(新しく作成されたFooインスタンス)内のselfはブロック内のselfと同じではなく、単に "self"と言うと、どちらが意味するのか不明です。 – sepp2k

4

あなただけ歩留まり IRB外で使用する特別な時に、私の好みに少し新しいそこにあるを使用して、コメント

から更新

class Foo 
    def initialize 
    yield if block_given? 
    end 
end 

自己キーワードをドロップすることができます。

class Foo 
    def initialize(&block) 
    instance_eval(&block) if block_given? 
    end 
end 
x = Foo.new { def foo; 'foo'; end }    
#=> #<Foo:0xb800f6a0>            
x.foo #=> "foo"               
z = Foo.new #=> #<Foo:0xb800806c>            
z.foo #=>NoMethodError: undefined method `foo' for #<Foo:0xb800806c> 

チェックだけでなく、この1:

class Foo2 
    def initialize 
    yield if block_given? 
    end 
end 
x = Foo2.new { def foo; 'foo'; end } #=> #<Foo:0xb7ff1bb4> 
x.foo #=> private method `foo' called for #<Foo2:0xb8004930> (NoMethodError) 
x.send :foo => "foo" 
z = Foo.new #=> #<Foo:0xb800806c> 
z.send :foo => "foo" 

することができますように

しかしアプローチと歩留まりアプローチinstance_evalをの間には大きな有意差があり、このスニペットをチェック違いは、前者がオブジェクトの初期化中にシングルトンメソッドfooを追加していることです後は、Objectクラスのすべてのインスタンスにプライベートメソッドを追加します。

+2

"違いが分かるように、前者は初期化されるオブジェクトにシングルトンメソッドfooを追加し、後でFoo2クラスのすべてのインスタンスにそのメソッドを追加しています。"実際、後者はメソッドをObject(実際のルビー(irbではなく))ではprivateメソッドとして追加するので、x.fooやz.fooはできません(fooだけ)。言い換えれば、defはブロックの外側に書かれたのとまったく同じように振る舞います(ただし、メソッドが当然降伏しない限り、何も起こりません)。 – sepp2k

+0

あなたは絶対に本当です、ありがとう – khelll

+0

Ruby 1.9では、プライベートメソッド定義を取得しません。代わりに、 x = Foo2.new {def foo; 'foo'; end} は、Object上でメソッド 'foo'を定義します(sepp2kのように)。あなたは言って、これを見ることができます: x.methods(偽).grep/fooの/ #=> [] Object.new.foo #=> "foo" という –

7

これらは異なっています。ブロック内のselfの値はyield(self)に変更されず、instance_eval(&block)の値は変更されません。

class Foo 
    def with_yield 
    yield(self) 
    end 

    def with_instance_eval(&block) 
    instance_eval(&block) 
    end 
end 

f = Foo.new 

f.with_yield do |arg| 
    p self 
    # => main 
    p arg 
    # => #<Foo:0x100124b10> 
end 

f.with_instance_eval do |arg| 
    p self 
    # => #<Foo:0x100124b10> 
    p arg 
    # => #<Foo:0x100124b10> 
end 
+0

二 'のp arg'を印刷する必要があります' '#'ではなく、 'nil'です。 – sepp2k

+0

1.8.7では、Fooインスタンスが出力されます。私はそれが無かったと思ったが、なぜそうでないのか分からなかった。 –

+0

1.9 nilが印刷されます。私は1.8.7のFooインスタンスの印刷について何の説明も見当たりません。出力を誤解していませんか? – sepp2k

関連する問題