J'ai trouvé ce code dans un RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Que signifie l' (&:name)
in map(&:name)
?
J'ai trouvé ce code dans un RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Que signifie l' (&:name)
in map(&:name)
?
Réponses:
C'est un raccourci pour tags.map(&:name.to_proc).join(' ')
Si foo
est un objet avec une to_proc
méthode, vous pouvez le passer à une méthode as &foo
, qui l'appellera foo.to_proc
et l'utilisera comme bloc de la méthode.
La Symbol#to_proc
méthode a été initialement ajoutée par ActiveSupport mais a été intégrée à Ruby 1.8.7. Voici sa mise en œuvre:
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
&
, c'esttags.map(&:name.to_proc).join(' ')
Un autre raccourci cool, 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 Method
objet self
qui représente sa foo
méthode, et avons utilisé le &
pour signifier qu'il a une to_proc
méthode qui le convertit en a Proc
.
C'est très utile lorsque vous voulez faire des choses sans style. Un exemple consiste à vérifier s'il existe une chaîne dans un tableau qui est égale à la chaîne "foo"
. Il y a la voie conventionnelle:
["bar", "baz", "foo"].any? { |str| str == "foo" }
Et il y a la manière sans point:
["bar", "baz", "foo"].any?(&"foo".method(:==))
La manière préférée devrait être la plus lisible.
array.each{|e| foo(e)}
est encore plus court :-) +1 quand même
&method
?
[1,2,3].map(&Array.method(:new))
C'est équivalent à
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
Notons également que la #to_proc
magie de l' esperluette peut fonctionner avec n'importe quelle classe, pas seulement avec Symbol. De nombreux Rubyistes choisissent 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 to_proc
message sur son opérande, qui, dans le code ci-dessus, est de classe Array. Et puisque j'ai défini la #to_proc
méthode sur Array, la ligne devient:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
C'est un raccourci pour tags.map { |tag| tag.name }.join(' ')
&
opérateur unaire fait appel to_proc
à son opérande. Elle n'est donc pas spécifique à la méthode map et fonctionne en fait sur toute méthode qui prend un bloc et transmet un ou plusieurs arguments au bloc.
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, lorsqu'il 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» pour provoquer une erreur car l'objet Fixnum 1 n'a pas la méthode self (qui est: la première).
Quand [[1,'a'],[2,'b'],[3,'c']].map(&:first)
est exécuté;
:first
est un objet Symbol, donc quand &:first
est donné à une méthode de carte en tant que paramètre, Symbol # to_proc est invoqué.
la carte envoie un 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 dans la classe Symbol envoie un message d'envoi à un objet 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 de l' [[1,'a'],[2,'b'],[3,'c']]
objet.
Cela revient à exécuter l' [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
expression.
[1,2,3,4,5,6].inject(&:+)
- inject attend un lambda avec deux paramètres (mémo et item) et le :+.to_proc
livre - Proc.new |obj, *args| { obj.send(self, *args) }
ou{ |m, o| m.+(o) }
Il se passe deux choses ici, et il est important de comprendre les deux.
Comme décrit dans d'autres réponses, la Symbol#to_proc
méthode est appelée.
Mais la raison to_proc
pour laquelle le symbole est appelé est qu'il est passé en map
tant qu'argument de bloc. Placer &
devant un argument dans un appel de méthode le fait passer de cette façon. Cela est vrai pour toute méthode Ruby, pas seulement map
pour les 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)
Le Symbol
est converti en un Proc
car il est transmis en tant que bloc. Nous pouvons le montrer en essayant de passer un proc à .map
sans 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 s'il n'a pas besoin d'être converti, la méthode ne saura pas comment l'utiliser car elle attend un argument de bloc. Le passer avec &
donne .map
le bloc qu'il attend.
Il exécute essentiellement l'appel de méthode tag.name
sur chaque balise du tableau.
Il s'agit d'un raccourci rubis simplifié.
Bien que nous ayons déjà d'excellentes réponses, en regardant dans la perspective d'un débutant, j'aimerais ajouter des 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 de carte. (En réalité, vous passez un symbole qui est converti en proc. Mais ce n'est pas si important dans ce cas particulier).
Ce qui est important, c'est que vous ayez un method
nom name
qui sera utilisé par la méthode map comme argument au lieu du block
style traditionnel .
Tout d'abord, &:name
est un raccourci pour &:name.to_proc
, où :name.to_proc
renvoie un Proc
(quelque chose de similaire, mais pas identique à un lambda) qui, lorsqu'il est appelé avec un objet comme (premier) argument, appelle la name
méthode sur cet objet.
Deuxièmement, alors que &
in def foo(&block) ... end
convertit un bloc passé en foo
a Proc
, il fait le contraire lorsqu'il est appliqué à a Proc
.
Ainsi, &:name.to_proc
est un bloc qui prend un objet comme argument et appelle la name
méthode dessus, c'est-à-dire { |o| o.name }
.
C'est la même chose que ci-dessous:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end