web-dev-qa-db-fra.com

Que signifie map (&: name) en Ruby?

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)?

470
collimarco

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
489
Josh Lee

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_procmé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.

163
Gerry

C'est équivalent à

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

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!' ) }
41
Boris Stitnicky

C'est un raccourci pour tags.map { |tag| tag.name }.join(' ')

36
Oliver N.
tags.map(&:name)

est le même que

tags.map{|tag| tag.name}

&:name utilise simplement le symbole comme nom de méthode à appeler.

29
Albert.Qing

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é;

  1. :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é.

  2. 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é. 

  3. 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é.

  4. 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).

14
prosseek

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.

10
devpuppy

(&: 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

5
tessie

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.

2
Jonathan Duarte

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)

1
Sunda

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
1
timlentse

ça veut dire

array.each(&:to_sym.to_proc)
1
mminski

C'est pareil que ci-dessous:

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

En gros, il exécute l'appel de méthode tag.name sur chaque balise du tableau.

C'est un raccourci simplifié pour Ruby.

0
Olalekan Sogunle