ruby - tutorial - ¿Es ''el rendimiento propio'' lo mismo que instance_eval?
ruby wikipedia (3)
Ellos 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
¿Hay alguna diferencia si define Foo con instance_eval:. . .
class Foo
def initialize(&block)
instance_eval(&block) if block_given?
end
end
. . . o con ''auto rendirse'':
class Foo
def initialize
yield self if block_given?
end
end
En cualquier caso puedes hacer esto:
x = Foo.new { def foo; ''foo''; end }
x.foo
Por lo tanto, " yield self
" significa que el bloque después de Foo.new siempre se evalúa en el contexto de la clase Foo.
¿Es esto correcto?
Solo puedes soltar la palabra clave
class Foo
def initialize
yield if block_given?
end
end
Actualización de comentarios
Usar el rendimiento es un poco nuevo para mi gusto, especialmente cuando se usa fuera de irb.
Sin embargo, hay una gran diferencia significativa entre el enfoque de instance_eval y el enfoque de rendimiento , verifique este fragmento de código:
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>
Marque este también:
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 puede ver, la diferencia es que el primero está agregando un método singleton foo al objeto que se está inicializando, mientras que el último está agregando un método privado a todas las instancias de la clase Object.
Tus dos piezas de código hacen cosas muy diferentes. Al utilizar 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.
Cuando te entregas a ti mismo, te estás pasando como un argumento al bloque, pero como tu bloque no acepta ningún argumento, simplemente se ignora. Entonces, en este caso, rendirse a uno mismo hace lo mismo que no producir nada. La def
aquí se comporta exactamente como lo haría una def
fuera del bloque, produciendo self no cambia realmente lo que define el método. Lo que podrías 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 con instance_eval es que 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 produce, que en este caso es la instancia de Foo recién creada. Mientras que uno mismo tendrá el mismo valor que tenía fuera del bloque. Con la versión instance_eval self
dentro del bloque será la instancia de Foo recién creada.