Question

J'ai trouvé ce code dans un RailsCast :

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

Que signifie (& amp;: name) dans map (& amp;: name) ?

Était-ce utile?

La solution

C'est un raccourci pour tags.map (& amp;: name.to_proc) .join ('')

Si foo est un objet avec une méthode to_proc , vous pouvez le transmettre à une méthode sous la forme & amp; foo , qui appellera foo.to_proc et utilisez-le comme bloc de la méthode.

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 œuvre:

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

Autres conseils

Un autre raccourci intéressant, inconnu de beaucoup, est

array.each(&method(:foo))

qui est un raccourci pour

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

En appelant la méthode (: foo) , nous avons pris un objet Méthode de self qui représente sa méthode foo . , et a utilisé le & amp; pour indiquer qu'il possède un to_proc méthode qui la convertit en Proc .

Ceci est très utile lorsque vous souhaitez appliquer un style sans points . Un exemple consiste à vérifier si un tableau contient une chaîne égale à la chaîne " foo " <. Il y a la manière conventionnelle:

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

Et il y a la méthode sans points:

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

La méthode préférée devrait être la plus lisible.

Cela équivaut à

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

Bien que nous remarquions également que l'esperluette #to_proc 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 & amp; 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 identique à

tags.map{|tag| tag.name}

& amp;: 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

pas

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

Avec ce code, lorsque affiche [[1, 'a'], [2, 'b'], [3, 'c']]. map (& amp;: first) est exécuté, Ruby divise la première entrée [1, 'a'] en 1 et 'a' pour donner obj 1 et args * 'a '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 (& amp;: first) est exécuté;

  1. : first est un objet Symbol. Ainsi, lorsque & amp;: first est donné à une méthode map 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 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 de l'objet [[1, 'a'], [2, 'b'], [3, 'c']] .

Cela revient à exécuter [[1, 'a'], [2, 'b'], [3, 'c']]. map (| e | e.first) expression.

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 si to_proc est appelé sur le symbole, c'est parce qu'il est transmis à map en tant qu'argument de bloc. Si vous placez & amp; devant un argument dans un appel de méthode, il est transmis de cette façon. Ceci 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)

Le Symbole est converti en un Proc car il est transmis sous forme de 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 comment l'utiliser car elle attend un argument de blocage. Le passer avec & amp; donne à .map le bloc attendu.

(& amp;: name) est l'abréviation de (& amp;: name.to_proc), il est identique à tags.map {| t | t.name} .join ('')

to_proc est réellement 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.)

Ce qui est important, c’est que vous ayez une méthode nommée nom qui sera utilisée par la méthode map comme argument au lieu du bloc traditionnel style.

map (& amp;: name) prend un objet énumérable (balises dans votre cas) et exécute la méthode name pour chaque élément / balise, en générant 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 : nom est le symbole qui pointe vers la méthode nom de l'objet tag. Lorsque nous passons & amp;: name à map , il traitera name comme un objet proc. Pour résumer, tags.map (& amp;: name) agit comme:

tags.map do |tag|
  tag.name
end

cela signifie

array.each(&:to_sym.to_proc)

Il exécute en gros l'appel de méthode tag.name sur chaque balise du tableau.

C’est un raccourci rubis simplifié.

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top