ruby - etiquetas - que es un elemento en html
Propósito de &(ampersand) en Ruby para procs y métodos de llamada (4)
Me he dado cuenta de que muchos ejemplos relacionados con Ruby Procs tienen el siguiente y el símbolo.
# Ruby Example
shout = Proc.new { puts ''Yolo!'' }
def shout_n_times(n, &callback)
n.times do
callback.call
end
end
shout_n_times(3, &shout)
# prints ''Yolo!'' 3 times
Mi pregunta es ¿cuál es el propósito funcional detrás del símbolo &? Parece que si escribí el mismo código exacto sin &, funciona como se esperaba:
# Same code as previous without &
shout = Proc.new { puts ''Yolo!'' }
def shout_n_times(n, callback)
n.times do
callback.call
end
end
shout_n_times(3, shout)
# prints ''Yolo!'' 3 times
Bueno, cuando tienes un bloque , si aplicas &
antes del bloque, se convierte en objeto Proc
y viceversa.
_unary &_
: tiene algo que ver con convertir cosas desde y hacia bloques. Si no le quita nada más a esto, recuerde que cuando ve un "&" único en Ruby, está pensando en hacer algo en un bloque o en hacer un bloque en algo.
En su primer ejemplo, en esta línea shout_n_times(3, &shout)
de shout_n_times(3, &shout)
, está convirtiendo el objeto Proc
que hace referencia la variable shoot
en un block
. y luego en la lista de parámetros del método, lo está convirtiendo de nuevo a un objeto Proc
.
En su segundo ejemplo funciona, porque está pasando directamente un objeto Proc
como un argumento de método y luego está llamando a #call
en él.
Como complemento, me recuerdo &
como un signo de conversión entre block
y Proc
.
Para convertir un block
a Proc
def foo(&p)
puts p.class
end
foo {} # => Proc
Para convertir un Proc
en un block
def bar
yield "hello"
end
p = Proc.new {|a| puts a }
bar &p # => hello
La diferencia es que en tu primer ejemplo:
# Ruby Example
shout = Proc.new { puts ''Yolo!'' }
def shout_n_times(n, &callback)
n.times do
callback.call
end
end
shout_n_times(3, &shout)
... la sintaxis de la llamada a su método le permite reescribir la definición del método de la siguiente manera:
shout = Proc.new { puts ''Yolo!'' }
def shout_n_times(n)
n.times do
yield
end
end
shout_n_times(3, &shout)
--output:--
Yolo!
Yolo!
Yolo!
Estas dos afirmaciones:
shout = Proc.new { puts ''Yolo!'' }
...
shout_n_times(3, &shout)
... son equivalentes a:
shout_n_times(3) do
puts ''Yolo!''
end
Y escribir el rendimiento () dentro de la definición del método de shout_n_times () llama al bloque que se especifica después de la llamada al método:
method call +--start of block specified after the method call
| |
V V
shout_n_times(3) do
puts ''Yolo!''
end
^
|
+--end of block
Verá, un bloque es como un método, y un bloque se pasa como un argumento invisible en la llamada al método después de lo cual se escribe el bloque. Y dentro de la definición del método, quien haya escrito la definición del método puede ejecutar el bloque con el rendimiento (). Los bloques de Ruby no son más que una sintaxis especial que le permite pasar un método como argumento a otro método.
Este artículo proporciona una buena visión general de las diferencias.
Para resumir el artículo, Ruby permite bloques implícitos y explícitos. Además, Ruby tiene block, proc y lambda.
Cuando usted llama
def foo(block)
end
block
es solo un argumento simple del método. Se hace referencia al argumento en el block
variables, y la forma en que interactúa con él depende del tipo de objeto que pase.
def foo(one, block, two)
p one
p block.call
p two
end
foo(1, 2, 3)
1
NoMethodError: undefined method `call'' for 2:Fixnum
from (irb):3:in `foo''
from (irb):6
from /Users/weppos/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>''
foo(1, Proc.new { 1 + 1 }, 3)
1
2
3
Pero cuando utiliza el símbolo &
en la definición del método, el bloque adquiere un significado diferente. Estás definiendo explícitamente un método para aceptar un bloque. Y se aplicarán otras reglas (como no más de un bloque por método).
def foo(one, two, &block)
p one
p block.call
p two
end
En primer lugar, al ser un bloque, la firma del método ahora acepta "dos parámetros y un bloque", no "tres parámetros".
foo(1, 2, Proc.new { "from the proc" })
ArgumentError: wrong number of arguments (3 for 2)
from (irb):7:in `foo''
from (irb):12
from /Users/weppos/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>''
Eso significa que tienes que forzar que el tercer argumento sea un bloque que pasa el parámetro con el signo.
foo(1, 2, &Proc.new { "from the proc" })
1
"from the proc"
2
Sin embargo, esta es una sintaxis muy poco común. En Ruby, los métodos con bloques generalmente se llaman usando {}
foo(1, 2) { "from the block" }
1
"from the block"
2
o do end
foo(1, 2) do
"from the block"
end
1
"from the block"
2
Volvamos a la definición del método. Anteriormente mencioné que el siguiente código es una declaración de bloque explícita .
def foo(one, two, &block)
block.call
end
Los métodos pueden aceptar implícitamente un bloque. Los bloques implícitos se llaman con yield
.
def foo(one, two)
p yield
end
foo(1, 2) { "from the block" }
¿Puedes comprobar que se pasa el bloque usando block_given?
def foo(one, two)
if block_given?
p yield
else
p "No block given"
end
end
foo(1, 2) { "from the block" }
=> "from the block"
foo(1, 2)
=> "No block given"
Estas características relacionadas con el bloque no estarían disponibles si declara el "bloque" como un argumento simple (por lo tanto, sin el símbolo comercial), porque solo sería un argumento de método anónimo.