J'ai trouvé ce code dans un RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Que signifie le (&:name)
dans map(&:name)
?
C'est un raccourci pour tags.map(&:name.to_proc).join(' ')
Si foo
est un objet avec une méthode to_proc
, vous pouvez le transmettre à une méthode en tant que &foo
, qui appellera foo.to_proc
et l'utilisera comme bloc.
La méthode Symbol#to_proc
a été ajoutée à l'origine par ActiveSupport mais a été intégrée à Ruby 1.8.7. Voici sa mise en oeuvre:
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
Un autre raccourci, inconnu de beaucoup, est
array.each(&method(:foo))
qui est un raccourci pour
array.each { |element| foo(element) }
En appelant method(:foo)
, nous avons pris un objet Method
dans self
qui représente sa méthode foo
et nous avons utilisé le &
pour indiquer qu'il possède une to_proc
méthode qui le convertit en Proc
.
Ceci est très utile lorsque vous voulez faire du style sans points. Un exemple consiste à vérifier si une chaîne dans un tableau est égale à la chaîne "foo"
. Il y a la manière conventionnelle:
["bar", "baz", "foo"].any? { |str| str == "foo" }
Et il y a le moyen sans point:
["bar", "baz", "foo"].any?(&"foo".method(:==))
La manière préférée devrait être la plus lisible.
C'est équivalent à
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
Notons également que l'esperluette #to_proc
magic peut fonctionner avec n'importe quelle classe, pas seulement avec Symbol. De nombreux rubyists ont choisi de définir #to_proc
sur la classe Array:
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]
Esperluette &
fonctionne en envoyant un message to_proc
sur son opérande, qui, dans le code ci-dessus, appartient à la classe Array. Et depuis que j'ai défini la méthode #to_proc
sur Array, la ligne devient:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
C'est un raccourci pour tags.map { |tag| tag.name }.join(' ')
tags.map(&:name)
est le même que
tags.map{|tag| tag.name}
&:name
utilise simplement le symbole comme nom de méthode à appeler.
La réponse de Josh Lee est presque correcte, sauf que le code Ruby équivalent aurait dû être le suivant.
class Symbol
def to_proc
Proc.new do |receiver|
receiver.send self
end
end
end
ne pas
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
Avec ce code, lorsque print [[1,'a'],[2,'b'],[3,'c']].map(&:first)
est exécuté, Ruby divise la première entrée [1,'a']
en 1 et 'a' pour donner obj
1 et args*
'a' afin de provoquer une erreur, car l'objet Fixnum 1 n'a pas la méthode self (qui est: first) .
Lorsque [[1,'a'],[2,'b'],[3,'c']].map(&:first)
est exécuté;
:first
est un objet Symbol. Ainsi, lorsque &:first
est donné à une méthode de carte en tant que paramètre, Symbol # to_proc est appelé.
map envoie le message d'appel à: first.to_proc avec le paramètre [1,'a']
, par exemple, :first.to_proc.call([1,'a'])
est exécuté.
la procédure to_proc de la classe Symbol envoie un message d'envoi à un objet de tableau ([1,'a']
) avec le paramètre (: first), par exemple, [1,'a'].send(:first)
est exécuté.
itère sur le reste des éléments dans l'objet [[1,'a'],[2,'b'],[3,'c']]
.
Cela revient à exécuter l'expression [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
.
Deux choses se passent ici et il est important de comprendre les deux.
Comme décrit dans d'autres réponses, la méthode Symbol#to_proc
est appelée.
Mais la raison pour laquelle to_proc
est appelé sur le symbole est qu’elle est passée à map
en tant qu’argument de bloc. Placer &
devant un argument dans un appel de méthode provoque sa transmission de cette façon. Cela est vrai pour toutes les méthodes Ruby, pas seulement map
avec des symboles.
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
est convertie en une Proc
car elle est transmise en tant que bloc. Nous pouvons montrer cela en essayant de passer un proc à .map
sans l'esperluette:
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"]
Même si elle n'a pas besoin d'être convertie, la méthode ne saura pas l'utiliser car elle attend un argument de blocage. Le transmettre avec &
donne à .map
le bloc attendu.
(&: name) est l'abréviation de (&: name.to_proc), il est identique à tags.map{ |t| t.name }.join(' ')
to_proc est effectivement implémenté en C
Bien que nous ayons déjà de bonnes réponses, en regardant à travers la perspective d'un débutant, j'aimerais ajouter les informations supplémentaires:
Que signifie map (&: name) en Ruby?
Cela signifie que vous transmettez une autre méthode en tant que paramètre à la fonction map. (En réalité, vous passez un symbole qui est converti en proc. Mais ce n’est pas si important dans ce cas particulier).
L'important est que vous ayez une method
nommée name
qui sera utilisée par la méthode map comme argument au lieu du style traditionnel block
.
map (&: name) prend un objet énumérable (les balises dans votre cas) et exécute la méthode name pour chaque élément/balise, en affichant chaque valeur renvoyée par la méthode.
C'est un raccourci pour
array.map { |element| element.name }
qui retourne le tableau des noms d'élément (tag)
Ici, :name
est le symbole qui pointe vers la méthode name
de l'objet balise . Lorsque nous passons &:name
à map
, il traitera name
comme un objet proc. Pour faire court, tags.map(&:name)
agit comme:
tags.map do |tag|
tag.name
end
ça veut dire
array.each(&:to_sym.to_proc)
C'est pareil que ci-dessous:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end
En gros, il exécute l'appel de méthode tag.name
sur chaque balise du tableau.
C'est un raccourci simplifié pour Ruby.