Pregunta

Encontré este código en a RailsCast :

def tag_names
  @tag_names || tags.map(&:name).join(' ')
end

¿Qué significa (& amp;: name) en map (& amp;: name) ?

¿Fue útil?

Solución

Es la abreviatura de tags.map (& amp;: name.to_proc) .join ('')

Si foo es un objeto con un método to_proc , puede pasarlo a un método como & amp; foo , que llamará foo.to_proc y úselo como bloque del método.

El método Symbol # to_proc fue agregado originalmente por ActiveSupport pero se ha integrado 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

Otros consejos

Otra taquigrafía genial, desconocida para muchos, es

array.each(&method(:foo))

que es una abreviatura de

array.each { |element| foo(element) }

Al llamar al método (: foo) tomamos un objeto Method de self que representa su método foo y usó & amp; para indicar que tiene un to_proc método que lo convierte en un Proc .

Esto es muy útil cuando desea hacer cosas con estilo 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 existe la forma sin puntos:

["bar", "baz", "foo"].any?(&"foo".method(:==))

La forma preferida debería ser la más legible.

Es equivalente a

def tag_names
  @tag_names || tags.map { |tag| tag.name }.join(' ')
end

Si bien también tengamos en cuenta que el ampersand #to_proc magic puede funcionar con cualquier clase, no solo Symbol. Muchos rubíes eligen definir #to_proc en la clase de matriz:

class Array
  def to_proc
    proc { |receiver| receiver.send *self }
  end
end

# And then...

[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]

Ampersand & amp; funciona enviando el mensaje to_proc en su operando, que, en el código anterior, es de la clase Array. Y como definí el método #to_proc en Array, la línea se convierte en:

[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }

Es la abreviatura de tags.map {| tag | tag.name} .join ('')

tags.map(&:name)

es lo mismo que

tags.map{|tag| tag.name}

& amp;: name solo usa el símbolo como el nombre del método a llamar.

La respuesta de Josh Lee es casi correcta, excepto que el código Ruby equivalente debería haber sido 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 print [[1, 'a'], [2, 'b'], [3, 'c']]. map (& amp;: first) es ejecutado, 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 [[1, 'a'], [2, 'b'], [3, 'c']]. map (& amp;: first) se ejecuta;

  1. : first es un objeto Symbol, por lo que cuando & amp;: first se asigna a un método de mapa como parámetro, se invoca Symbol # to_proc.

  2. El mapa
  3. envía un mensaje de llamada a: first.to_proc con el parámetro [1, 'a'] , por ejemplo, : first.to_proc.call ([1, 'a' ]) se ejecuta.

  4. El procedimiento
  5. to_proc en la clase Symbol 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.

  6. itera sobre el resto de los elementos en el objeto [[1, 'a'], [2, 'b'], [3, 'c']] .

Esto es lo mismo que ejecutar [[1, 'a'], [2, 'b'], [3, 'c']]. map (| e | e.first) expresión.

Aquí están sucediendo dos cosas, y es importante comprender ambas.

Como se describe en otras respuestas, se llama al método Symbol # to_proc .

Pero la razón por la que se invoca to_proc en el símbolo es porque se pasa a map como un argumento de bloque. Colocar & amp; delante de un argumento en una llamada a un método hace que se pase de esta manera. Esto es cierto para cualquier método de Ruby, no solo 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 Símbolo se convierte en un Proc porque se pasa como un bloque. Podemos mostrar esto al tratar de pasar un proceso a .map sin el ampersand:

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 bloque. Pasarlo con & amp; le da a .map el bloque que espera.

(& amp;: name) es la abreviatura de (& amp;: name.to_proc) es lo mismo que tags.map {| t | t.name} .join ('')

to_proc se implementa realmente en C

Aunque ya tenemos excelentes respuestas, mirando a través de la perspectiva de un principiante me gustaría agregar la información adicional:

  

¿Qué significa map (& amp;: name) 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 particular).

Lo importante es que tenga un método llamado name que será utilizado por el método map como argumento en lugar del bloque tradicional estilo.

map (& amp;: name) toma un objeto enumerable (etiquetas en su caso) y ejecuta el método de nombre para cada elemento / etiqueta, generando cada valor devuelto por el método.

Es una abreviatura de

array.map { |element| element.name }

que devuelve la matriz de nombres de elementos (etiquetas)

Aquí : name es el símbolo que apunta al método name del objeto de etiqueta. Cuando pasamos & amp;: name a map , tratará a name como un objeto proc. Para abreviar, tags.map (& amp;: name) actúa como:

tags.map do |tag|
  tag.name
end

significa

array.each(&:to_sym.to_proc)

Básicamente ejecuta la llamada al método tag.name en cada etiqueta de la matriz.

Es una taquigrafía rubí simplificada.

Es lo mismo que a continuación:

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top