for - ruby method
¿Qué significa mapa(&: nombre) en Ruby? (14)
Encontré este código en un RailsCast :
def tag_names
@tag_names || tags.map(&:name).join('' '')
end
¿Qué significa el (&:name)
en el map(&:name)
?
(&: name) es la abreviatura de (&: name.to_proc) es igual que tags.map{ |t| t.name }.join('' '')
tags.map{ |t| t.name }.join('' '')
to_proc está implementado en C
Aquí :name
es el símbolo que apunta al name
del método del objeto de etiqueta. Cuando pasamos &:name
to map
, tratará el name
como un objeto proc. Para abreviar, tags.map(&:name)
actúa como:
tags.map do |tag|
tag.name
end
Aunque ya tenemos excelentes respuestas, mirando la perspectiva de un principiante, me gustaría agregar la información adicional:
¿Qué significa mapa (&: nombre) en Ruby?
Esto significa que está pasando otro método como parámetro a la función de mapa. (En realidad, está pasando un símbolo que se convierte en un proceso. Pero esto no es tan importante en este caso en particular).
Lo importante es que tiene un method
llamado name
que será utilizado por el método de mapa como un argumento en lugar del estilo de block
tradicional.
Dos cosas están sucediendo aquí, y es importante entender ambas.
Como se describe en otras respuestas, se Symbol#to_proc
método Symbol#to_proc
.
Pero la razón to_proc
se llama a to_proc
en el símbolo es porque se pasa a map
como un argumento de bloque. Colocar &
delante de un argumento en una llamada de método hace que se pase de esta manera. Esto es cierto para cualquier método de Ruby, no solo para map
con símbolos.
def some_method(*args, &block)
puts "args: #{args.inspect}"
puts "block: #{block.inspect}"
end
some_method(:whatever)
# args: [:whatever]
# block: nil
some_method(&:whatever)
# args: []
# block: #<Proc:0x007fd23d010da8>
some_method(&"whatever")
# TypeError: wrong argument type String (expected Proc)
# (String doesn''t respond to #to_proc)
El Symbol
se convierte en un Proc
porque se pasa como un bloque. Podemos mostrar esto intentando pasar un proc a .map
sin el signo:
arr = %w(apple banana)
reverse_upcase = proc { |i| i.reverse.upcase }
reverse_upcase.is_a?(Proc)
=> true
arr.map(reverse_upcase)
# ArgumentError: wrong number of arguments (1 for 0)
# (map expects 0 positional arguments and one block argument)
arr.map(&reverse_upcase)
=> ["ELPPA", "ANANAB"]
Aunque no es necesario convertirlo, el método no sabrá cómo usarlo porque espera un argumento de bloqueo. Pasándolo con &
da .map
el bloque que espera.
Es equivalente a
def tag_names
@tag_names || tags.map { |tag| tag.name }.join('' '')
end
Es lo mismo que a continuación:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join('' '')
end
Es taquigrafía para tags.map { |tag| tag.name }.join('' '')
tags.map { |tag| tag.name }.join('' '')
Es una abreviatura de tags.map(&:name.to_proc).join('' '')
Si foo
es un objeto con un método to_proc
, entonces puede pasarlo a un método como &foo
, que llamará foo.to_proc
y usarlo como el bloque del método.
El método Symbol#to_proc
fue originalmente agregado por ActiveSupport pero se integró en Ruby 1.8.7. Esta es su implementación:
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
La respuesta de Josh Lee es casi correcta, excepto que el código Ruby equivalente debería ser el siguiente.
class Symbol
def to_proc
Proc.new do |receiver|
receiver.send self
end
end
end
no
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
Con este código, cuando se ejecuta la print [[1,''a''],[2,''b''],[3,''c'']].map(&:first)
, Ruby divide la primera entrada [1,''a'']
en 1 y'' a ''para dar obj
1 y args*
'' a ''para causar un error ya que el objeto Fixnum 1 no tiene el método self (que es: primero).
Cuando se ejecuta [[1,''a''],[2,''b''],[3,''c'']].map(&:first)
;
:first
es un objeto Símbolo, por lo tanto, cuando&:first
se asigna a un método de mapa como parámetro, se invoca el Símbolo # to_proc.el mapa envía el mensaje de llamada a: first.to_proc con el parámetro
[1,''a'']
, por ejemplo, se:first.to_proc.call([1,''a''])
.El procedimiento to_proc en la clase de símbolos envía un mensaje de envío a un objeto de matriz (
[1,''a'']
) con el parámetro (: primero), por ejemplo,[1,''a''].send(:first)
Se ejecuta[1,''a''].send(:first)
.itera sobre el resto de los elementos en
[[1,''a''],[2,''b''],[3,''c'']]
objeto.
Esto es lo mismo que ejecutar la expresión [[1,''a''],[2,''b''],[3,''c'']].map(|e| e.first)
.
Otra taquigrafía fresca, desconocida para muchos, es
array.each(&method(:foo))
que es una taquigrafía para
array.each { |element| foo(element) }
Al llamar a method(:foo)
, tomamos un objeto Method
de self
que representa su método foo
, y usamos el &
para indicar que tiene un method to_proc
que lo convierte en un Proc
.
Esto es muy útil cuando quieres hacer cosas sin puntos . Un ejemplo es verificar si hay alguna cadena en una matriz que sea igual a la cadena "foo"
. Existe la forma convencional:
["bar", "baz", "foo"].any? { |str| str == "foo" }
Y ahí está el camino libre de puntos:
["bar", "baz", "foo"].any?(&"foo".method(:==))
La forma preferida debería ser la más legible.
Si bien también #to_proc
que el símbolo #to_proc
magic puede funcionar con cualquier clase, no solo con Symbol. Muchos Rubyists eligen definir #to_proc
en la clase Array:
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ ''Hello'', ''Goodbye'' ].map &[ :+, '' world!'' ]
#=> ["Hello world!", "Goodbye world!"]
Ampersand &
works envía to_proc
mensaje to_proc
en su operando, que, en el código anterior, pertenece a la clase Array. Y como #to_proc
método #to_proc
en Array, la línea se convierte en:
[ ''Hello'', ''Goodbye'' ].map { |receiver| receiver.send( :+, '' world!'' ) }
significa
array.each(&:to_sym.to_proc)
map (&: name) toma un objeto enumerable (etiquetas en su caso) y ejecuta el método de nombre para cada elemento / etiqueta, generando cada valor devuelto del método.
Es una taquigrafía para
array.map { |element| element.name }
que devuelve la matriz de nombres de elementos (etiquetas)
tags.map(&:name)
es lo mismo que
tags.map{|tag| tag.name}
&:name
solo usa el símbolo como el nombre del método que se va a llamar.