Ho trovato questo codice in un RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Cosa significa " (&:name)
in" map(&:name)
?
Ho trovato questo codice in un RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Cosa significa " (&:name)
in" map(&:name)
?
Risposte:
È una scorciatoia per tags.map(&:name.to_proc).join(' ')
Se foo
è un oggetto con un to_proc
metodo, puoi passarlo a un metodo as &foo
, che lo chiamerà foo.to_proc
e lo utilizzerà come blocco del metodo.
Il Symbol#to_proc
metodo è 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
&
, cioètags.map(&:name.to_proc).join(' ')
Un'altra scorciatoia interessante, sconosciuta a molti, è
array.each(&method(:foo))
che è una scorciatoia per
array.each { |element| foo(element) }
Chiamando method(:foo)
abbiamo preso un Method
oggetto self
che rappresenta il suo foo
metodo e abbiamo usato il &
per indicare che ha un to_proc
metodo che lo converte in a Proc
.
Questo è molto utile quando vuoi fare cose senza stile. Un esempio è verificare se esiste una stringa in un array uguale alla stringa "foo"
. C'è il modo convenzionale:
["bar", "baz", "foo"].any? { |str| str == "foo" }
E c'è il modo senza punti:
["bar", "baz", "foo"].any?(&"foo".method(:==))
Il modo preferito dovrebbe essere quello più leggibile.
array.each{|e| foo(e)}
è ancora più corto :-) +1 comunque
&method
?
[1,2,3].map(&Array.method(:new))
È equivalente a
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
Mentre notiamo anche che la #to_proc
magia e commerciale può funzionare con qualsiasi classe, non solo con Symbol. Molti rubyisti scelgono di definire #to_proc
sulla classe Array:
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]
La e commerciale &
funziona inviando un to_proc
messaggio sul suo operando, che, nel codice sopra, è di classe Array. E poiché ho definito il #to_proc
metodo su Array, la linea diventa:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
È una scorciatoia per tags.map { |tag| tag.name }.join(' ')
&
operatore unario chiama to_proc
il suo operando. Quindi non è specifico per il metodo map, e in effetti funziona su qualsiasi metodo che prende un blocco e passa uno o più argomenti al blocco.
La risposta di Josh Lee è quasi corretta, tranne per il fatto 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 print [[1,'a'],[2,'b'],[3,'c']].map(&:first)
viene eseguito, Ruby divide il primo input [1,'a']
in 1 e 'a' per dare obj
1 e args*
'a' per causare un errore poiché l'oggetto Fixnum 1 non ha il metodo self (che è: primo).
Quando [[1,'a'],[2,'b'],[3,'c']].map(&:first)
viene eseguito;
:first
è un oggetto Symbol, quindi quando &:first
viene assegnato a un metodo map come parametro, viene richiamato Symbol # to_proc.
map invia il messaggio di chiamata a: first.to_proc con il parametro [1,'a']
, ad es., :first.to_proc.call([1,'a'])
viene eseguito.
La procedura to_proc nella classe Symbol invia un messaggio di invio a un oggetto array ( [1,'a']
) con il parametro (: primo), ad es. [1,'a'].send(:first)
viene eseguito.
scorre il resto degli elementi [[1,'a'],[2,'b'],[3,'c']]
nell'oggetto.
È lo stesso dell'esecuzione [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
dell'espressione.
[1,2,3,4,5,6].inject(&:+)
: iniettare si aspetta una lambda con due parametri (memo e oggetto) e la :+.to_proc
consegna - Proc.new |obj, *args| { obj.send(self, *args) }
oppure{ |m, o| m.+(o) }
Qui stanno accadendo due cose ed è importante capirle entrambe.
Come descritto in altre risposte, Symbol#to_proc
viene chiamato il metodo.
Ma la ragione per cui to_proc
viene chiamato il simbolo è perché viene passato map
come argomento a blocchi. Posizionare &
davanti a un argomento in una chiamata di metodo fa sì che venga passato in questo modo. Questo vale per qualsiasi metodo Ruby, non solo 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: #<Proc:0x007fd23d010da8>
some_method(&"whatever")
# TypeError: wrong argument type String (expected Proc)
# (String doesn't respond to #to_proc)
La Symbol
viene convertito in un Proc
perché è passato come un blocco. Possiamo dimostrarlo provando a 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 a blocchi. Passandolo con si &
ottiene .map
il blocco che si aspetta.
Fondamentalmente esegue la chiamata del metodo tag.name
su ogni tag dell'array.
È una scorciatoia rubino semplificata.
Anche se abbiamo già ottime risposte, guardando nella prospettiva di un principiante, vorrei aggiungere ulteriori informazioni:
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).
L'importante è che tu abbia un method
nome name
che verrà usato dal metodo map come argomento invece che dallo block
stile tradizionale .
Innanzitutto, &:name
è una scorciatoia per &:name.to_proc
, dove :name.to_proc
restituisce un Proc
(qualcosa di simile, ma non identico a un lambda) che quando chiamato con un oggetto come (primo) argomento, chiama ilname
metodo su quell'oggetto.
In secondo luogo, mentre &
in def foo(&block) ... end
converte un blocco passato a foo
a Proc
, fa il contrario quando applicato a a Proc
.
Pertanto, &:name.to_proc
è un blocco che accetta un oggetto come argomento e chiama il name
metodo su di esso, ad es { |o| o.name }
.
È lo stesso di seguito:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end