Ruby Block, Procs e Instance_Eval
-
28-10-2019 - |
Pregunta
Recientemente intenté hacer algo parecido a esto:
a = "some string"
b = Proc.new{ upcase }
a.instance_eval b
Que da el error:
TypeError: no se puede convertir Proc en cadena
Pero esto funciona:
def b(&block)
"some string".instance_eval &block
end
b{ upcase }
Una mirada adicional con este método:
def b(&block)
"some string".instance_eval block
end
Produce lo mismo Proc to String
error.
Entonces ... mi comprensión de los bloques es que son solo procs. Pero obviamente hay algo especial en tener esto &
ampersand ...
¿Alguien puede explicarme esto? ¿Es posible convertir un proceso normal para ser lo que sea que sea especial sobre esto? &block
¿objeto?
editar
Acabo de descubrir mi segunda pregunta, prependa un &
Para el proceso ... eso fue fácil, pero ¿qué está haciendo realmente esto?
Solución
Todo lo que tiene que hacer para que su primer ejemplo funcione es esto:
>> a.instance_eval &b #=> "SOME STRING"
La razón es que instance_eval
necesita una cadena o un bloque y el ampersand proporciona el último.
Para comprender las diferencias entre bloques y procesos, tal vez la siguiente publicación de blog ayuda:
Otros consejos
La diferencia es que a.instance_eval b
está pasando b
como un argumento regular a instancia_eval, mientras que a.instance_eval &b
lo está pasando como un bloque. Esas son dos cosas diferentes.
Considere esta llamada de método:
obj.foo(bar) do |x|
stuff(x)
end
Que invoca el método foo
con un argumento regular (bar
) y un argumento de bloque (do |x| stuff(x) end
). En la definición del método, se distinguen prefijando &
al parámetro de bloque:
def foo(arg, &block)
Y si desea pasar una expresión variable en lugar de un bloque literal, eso también se logra mediante el prefijo & a la expresión (que debería producir un proc).
Si pasa una discusión sin ningún &
, entra en el argumento ranura en lugar de la bloquear ranura. No importa que el argumento sea una instancia de Proc. La sintaxis dicta cómo se pasa y se trata por el método.
Es porque instance_eval acepta una cadena para evaluar o un bloque. instance_eval(&block)
está pasando tu block
como un bloque a instancia_eval.
La diferencia crucial es que Una instancia de proc es un objeto mientras Un bloque no es un objeto. los &
es un operador que intercambia un bloque y una instancia de proc mutuamente.
Todos los argumentos a un método deben ser un objeto. Además de los argumentos, un método puede tomar un bloque. instance_eval
es un método que toma un argumento de cadena o un bloque. Pasar un objeto PROC no satisfará ninguno de los casos. Si adjuntas &
a un objeto de proc, eso será a mano como un bloque.
Esto funcionará:
a = "some string"
b = Proc.new{ upcase }
a.instance_eval &b
los instance_eval
El método puede recibir un bloque de argumentos.b
es un Proc
.