Cosa significa map (&: name) in Ruby?

Ho trovato questo codice in un RailsCast :

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

Cosa significa il (&:name) nella map(&:name) ?

È una scorciatoia per tags.map(&:name.to_proc).join(' ')

Se foo è un object con un metodo to_proc , allora puoi passarlo a un metodo come &foo , che chiamerà foo.to_proc e lo userà come blocco del metodo.

Il metodo Symbol#to_proc è stato originariamente aggiunto da ActiveSupport ma è stato integrato in Ruby 1.8.7. Questa è la sua implementazione:

 class Symbol def to_proc Proc.new do |obj, *args| obj.send self, *args end end end 

Un’altra fredda stenografia, sconosciuta a molti, è

 array.each(&method(:foo)) 

che è una scorciatoia per

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

Chiamando method(:foo) abbiamo preso un object Method da self che rappresenta il suo metodo foo e usato il & per significare che ha un metodo to_proc che lo converte in un Proc .

Questo è molto utile quando vuoi fare cose senza punti . Un esempio è per verificare se c’è una stringa in una matrice che è uguale alla stringa "foo" . C’è il modo convenzionale:

 ["bar", "baz", "foo"].any? { |str| str == "foo" } 

E c’è il modo point-free:

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

Il modo preferito dovrebbe essere il più leggibile.

È equivalente a

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

Notiamo anche che la magia di #to_proc può funzionare con qualsiasi class, non solo con Symbol. Molti Rubyists scelgono di definire #to_proc sulla class Array:

 class Array def to_proc proc { |receiver| receiver.send *self } end end # And then... [ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ] #=> ["Hello world!", "Goodbye world!"] 

E commerciale & funziona inviando to_proc messaggio sul suo operando, che, nel codice precedente, è di class Array. E poiché ho definito il metodo #to_proc su Array, la linea diventa:

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

È una scorciatoia per tags.map { |tag| tag.name }.join(' ') tags.map { |tag| tag.name }.join(' ')

 tags.map(&:name) 

equivale a

 tags.map{|tag| tag.name} 

&:name usa solo il simbolo come nome del metodo da chiamare.

La risposta di Josh Lee è quasi corretta, tranne che il codice Ruby equivalente avrebbe dovuto essere il seguente.

 class Symbol def to_proc Proc.new do |receiver| receiver.send self end end end 

non

 class Symbol def to_proc Proc.new do |obj, *args| obj.send self, *args end end end 

Con questo codice, quando viene print [[1,'a'],[2,'b'],[3,'c']].map(&:first) , Ruby divide il primo input [1,'a'] in 1 e’ a ‘per dare obj 1 e args* ‘ a ‘per causare un errore come Fixnum object 1 non ha il metodo self (che è: primo).


Quando [[1,'a'],[2,'b'],[3,'c']].map(&:first) viene eseguito;

  1. :first è un object simbolo, quindi quando &:first viene assegnato a un metodo map come parametro, viene richiamato Symbol # to_proc.

  2. map invia il messaggio di chiamata a: first.to_proc con parametro [1,'a'] , ad esempio :first.to_proc.call([1,'a']) viene eseguito.

  3. La procedura to_proc nella class Symbol invia un messaggio di invio a un object array ( [1,'a'] ) con parametro (: first), ad es. [1,'a'].send(:first) viene eseguito.

  4. itera sopra il resto degli elementi in [[1,'a'],[2,'b'],[3,'c']] object.

È la stessa cosa dell’esecuzione di un’espressione [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first) .

Qui stanno accadendo due cose, ed è importante comprenderle entrambe.

Come descritto in altre risposte, viene chiamato il metodo Symbol#to_proc .

Ma la ragione per to_proc viene chiamata sul simbolo è perché viene passata alla map come argomento di blocco. Posizionare & di fronte a un argomento in una chiamata di metodo fa sì che venga passato in questo modo. Questo è vero per qualsiasi metodo Ruby, non solo per la map con i simboli.

 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: # some_method(&"whatever") # TypeError: wrong argument type String (expected Proc) # (String doesn't respond to #to_proc) 

Il Symbol viene convertito in Proc perché viene passato come blocco. Possiamo mostrare questo cercando di passare un proc a .map senza la e commerciale:

 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"] 

Anche se non ha bisogno di essere convertito, il metodo non saprà come usarlo perché si aspetta un argomento di blocco. Passandolo con &.map il blocco che si aspetta.

(&: name) è l’abbreviazione di (&: name.to_proc) è uguale a tags.map{ |t| t.name }.join(' ') tags.map{ |t| t.name }.join(' ')

to_proc è attualmente implementato in C

Anche se abbiamo già ottime risposte, guardando attraverso una prospettiva di un principiante vorrei aggiungere le informazioni aggiuntive:

Cosa significa map (&: name) in Ruby?

Questo significa che stai passando un altro metodo come parametro alla funzione mappa. (In realtà stai passando un simbolo che viene convertito in un proc. Ma questo non è così importante in questo caso particolare).

Ciò che è importante è che tu abbia un method chiamato name che verrà usato dal metodo map come argomento invece del tradizionale stile di block .

Qui :name è il simbolo che punta al name del metodo dell’object tag. Quando passiamo &:name to map , considereremo il name come object proc. In breve, tags.map(&:name) funziona come:

 tags.map do |tag| tag.name end 

significa

 array.each(&:to_sym.to_proc) 

È lo stesso di seguito:

 def tag_names if @tag_names @tag_names else tags.map{ |t| t.name }.join(' ') end 

map (&: name) prende un object enumerabile (tag nel tuo caso) ed esegue il metodo name per ogni elemento / tag, emettendo ogni valore restituito dal metodo.

È una scorciatoia per

 array.map { |element| element.name } 

che restituisce la matrice di nomi di elementi (tag)