2009-09-15 12 views
19

¿Hay alguna diferencia si define Foo con instance_eval:. . .¿Es 'yield self' lo mismo que instance_eval?

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

. . . o con 'auto rendimiento':

class Foo 
    def initialize 
    yield self if block_given? 
    end 
end 

En cualquiera de los casos se puede hacer esto:

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

Así 'yield self' significa que el bloque después Foo.new siempre se evalúa en el contexto de la Clase Foo

¿Es esto correcto?

Respuesta

14

Sus dos códigos hacen cosas muy diferentes. Al usar instance_eval está evaluando el bloque en el contexto de su objeto. Esto significa que usar def definirá métodos en ese objeto. También significa que llamar a un método sin un receptor dentro del bloque lo llamará a su objeto.

Al ceder uno mismo estás pasando uno mismo como un argumento al bloque, pero como tu bloque no toma ningún argumento, simplemente se ignora. Entonces en este caso rendirse a sí mismo hace lo mismo que no ceder nada. El def aquí se comporta exactamente como un def fuera del bloque, lo que produce uno mismo no cambia realmente lo que define el método. Lo que podría hacer es:

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

La diferencia de instance_eval es que usted tiene que especificar explícitamente el receptor.

Editar para aclarar:

En la versión con rendimiento, obj en el bloque será el objeto que se produjo, que en este caso es es la instancia Foo recién creado. Mientras que uno mismo tendrá el mismo valor que tenía fuera del bloque. Con la versión de instance_eval self dentro del bloque se creará la instancia de Foo recién creada.

+0

En su "Editar para aclarar", ¿no quiere decir que el yo se rindió al obj en el bloque? Tal vez solo estoy leyéndola de una manera diferente, pero veo que el objeto se inicializa, el yo se cede al bloque como 'obj' y luego dentro del bloque el método foo se define en sí mismo a través de obj. – uzo

+1

Estoy bastante seguro, nos referimos a lo mismo. Escribí "la instancia de Foo recién creada" porque self dentro del método de inicialización (que es la instancia de Foo recién creada) no es lo mismo que self dentro del bloque y si solo dice "self", no está claro a qué se refiere. – sepp2k

4

Sólo se puede quitar la palabra clave auto

class Foo 
    def initialize 
    yield if block_given? 
    end 
end 

actualización de los comentarios

Usando rendimiento hay un poco nueva para mi gusto, especialmente cuando se utiliza fuera del IRB.

Sin embargo, hay una diferencia grande y significativa entre instance_eval aproximación y enfoque del rendimiento, compruebe este fragmento:

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> 

Comprobar éste a su vez:

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" 

Como se puede ver la diferencia es que el anterior está agregando un método singleton foo al objeto que se inicializa, cuando e el último agrega un método privado a todas las instancias de la clase Object.

+2

"Como puede ver, la diferencia es que la primera agrega un método singleton foo al objeto que se inicializa, mientras que la última agrega ese método a todas las instancias de la clase Foo2" En realidad, este último está agregando el método a Object (y en ruby ​​real (a diferencia de irb) lo agregará como un método privado, por lo que no puedes hacer x.foo o z.foo, solo foo). En otras palabras, la def se comporta exactamente como si la hubieras escrito fuera del bloque (a menos que el método no ceda, por supuesto, no sucede nada). – sepp2k

+0

Usted es absolutamente cierto, gracias – khelll

+0

En Ruby 1.9, no obtiene la definición de método privado. En cambio, x = Foo2.new {def foo; 'foo'; end} definirá el método 'foo' en Object (como sepp2k dijo). Se puede ver esto diciendo: x.methods (falso) .grep/foo/ # => [] Object.new.foo # => "foo" –

7

Son diferentes. yield(self) no cambia el valor de self dentro del bloque, mientras que instance_eval(&block) sí lo hace.

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

El segundo 'p arg' debe imprimir' nil', no '# '. – sepp2k

+0

En 1.8.7, se imprime la instancia de Foo. Pensé que sería nulo también, no estoy seguro de por qué no lo es. –

+0

En 1.9 nil está impreso. No puedo ver ninguna explicación para 1.8.7 imprimiendo la instancia de Foo. ¿Estás seguro de que no has leído mal la salida? – sepp2k

Cuestiones relacionadas