2009-09-15 10 views
19

Czy jest jakaś różnica, jeśli zdefiniujesz Foo z instance_eval:. . .Czy "yield self" jest takie samo jak instance_eval?

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

. . . lub „ja dochodowości”:

class Foo 
    def initialize 
    yield self if block_given? 
    end 
end 

W obu przypadkach można to zrobić:

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

Więc „yield self” oznacza, że ​​blok po Foo.new zawsze jest oceniana w kontekście z Klasa Foo.

Czy to prawda?

Odpowiedz

14

Twoje dwie części kodu robią bardzo różne rzeczy. Korzystając z klasy instance_eval, oceniasz blok w kontekście obiektu. Oznacza to, że użycie def definiuje metody na tym obiekcie. Oznacza to również, że wywołanie metody bez odbiornika wewnątrz bloku spowoduje wywołanie go na obiekcie.

Kiedy ustępujesz, podajesz siebie jako argument do bloku, ale ponieważ twój blok nie przyjmuje żadnych argumentów, jest po prostu ignorowany. Więc w tym przypadku oddanie siebie robi to samo, co nie daje niczego. def zachowuje się tutaj dokładnie tak, jak poza tym blokiem, dając samo siebie nie zmienia tego, na co definiujesz metodę. Co można zrobić, to:

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

Różnica do instance_eval jest, że trzeba określić odbiornik wyraźnie.

Edycja wyjaśnienie:

W wersji z wydajnością obj w bloku jest obiekt, który jest uzyskane, która w tym przypadku jest to nowoutworzone przykład foo. Podczas gdy jaźń będzie miała taką samą wartość, jaką miała poza blokiem. Dzięki instancji instance_eval w wersji self wewnątrz bloku zostanie utworzona nowo utworzona instancja Foo.

+0

W swojej "Edycji do wyjaśnienia", nie masz na myśli, że jaźń uległa obstawieniu w bloku? Być może po prostu czytam to w inny sposób, ale widzę, że obiekt jest inicjowany, self jest poddawany blokowi jako "obj", a następnie wewnątrz bloku metoda foo jest definiowana na self przez obj. – uzo

+1

Jestem prawie pewien, mamy na myśli to samo. Napisałem "nowo utworzoną instancję Foo", ponieważ self wewnątrz metody initialize (która jest nowo utworzoną instancją Foo) nie jest tym samym, co self wewnątrz bloku i jeśli po prostu powiesz "self", nie jest jasne, które masz na myśli. – sepp2k

4

Po prostu można usunąć Własna słowa kluczowego

class Foo 
    def initialize 
    yield if block_given? 
    end 
end 

Update z uwag

Korzystanie wydajność jest nieco nowego do mojego gustu, szczególnie kiedy jest stosowany poza IRB.

Jednak istnieje duża i znacząca różnica pomiędzy instance_eval podejścia i wydajność podejścia, sprawdź ten fragment:

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> 

sprawdzić ten jeden, a także:

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" 

jak można widzimy różnicę polegającą na tym, że pierwsza z nich dodaje metodę singleton do inicjowanego obiektu, whil e później dodaje prywatną metodę do wszystkich instancji klasy Object.

+2

"Jak widać różnica polega na tym, że pierwsza z nich dodaje metodę singleton foo do zainicjowanego obiektu, podczas gdy ta późniejsza dodaje tę metodę do wszystkich instancji klasy Foo2" W rzeczywistości ta ostatnia dodaje metodę do Obiekt (i w prawdziwym ruby ​​(w przeciwieństwie do irb) doda go jako prywatną metodę, więc nie możesz zrobić x.foo lub z.foo, tylko foo). Innymi słowy, def zachowuje się dokładnie tak, jakbyś zapisał je poza blokiem (chyba że metoda nie ustąpi oczywiście, w takim przypadku nic się nie dzieje). – sepp2k

+0

Jesteś absolutnie prawdziwy, dzięki – khelll

+0

W Rubim 1.9, nie otrzymujesz definicji metody prywatnej. Zamiast tego, x = Foo2.new {def foo; 'bla'; end} zdefiniuje metodę 'foo' na obiekcie (jak powiedział sepp2k). Można zobaczyć to, mówiąc: x.methods (fałsz) .grep/foo/ # => [] Object.new.foo # => "foo" –

7

Są różne. yield(self) nie zmienia wartości self wewnątrz bloku, natomiast ma wartość 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

Drugi 'p arg' należy wydrukować' nil', nie '# '. – sepp2k

+0

W 1.8.7 drukowana jest instancja Foo. Myślałem, że to też nie będzie, nie wiem, dlaczego tak nie jest. –

+0

W 1.9 drukuje się zero. Nie widzę żadnego wyjaśnienia dla 1.8.7 drukowania instancji Foo. Czy jesteś pewien, że źle odczytałeś wynik? – sepp2k

Powiązane problemy