Pouvez-vous fournir des arguments à la syntaxe map (&: method) dans Ruby?

Vous connaissez probablement le raccourci Ruby suivant ( a est un tableau):

 a.map(&:method) 

Par exemple, essayez ce qui suit dans irb:

 >> a=[:a, 'a', 1, 1.0] => [:a, "a", 1, 1.0] >> a.map(&:class) => [Symbol, Ssortingng, Fixnum, Float] 

La syntaxe a.map(&:class) est un raccourci pour a.map {|x| x.class} a.map {|x| x.class} .

En savoir plus sur cette syntaxe dans ” Que signifie map (&: name) dans Ruby? “.

A travers la syntaxe &:class , vous créez une class appel de méthode pour chaque élément du tableau.

Ma question est la suivante: pouvez-vous fournir des arguments à l’appel de méthode? Et si oui, comment?

Par exemple, comment convertissez-vous la syntaxe suivante?

 a = [1,3,5,7,9] a.map {|x| x + 2} 

à la syntaxe &:

Je ne suggère pas que la syntaxe &: soit meilleure. Je suis simplement intéressé par la mécanique de l’utilisation de la syntaxe &: avec des arguments.

Je suppose que vous savez que + est une méthode sur la classe Integer. Vous pouvez essayer ce qui suit dans irb:

 >> a=1 => 1 >> a+(1) => 2 >> a.send(:+, 1) => 2 

Vous pouvez créer un patch simple sur Symbol comme ceci:

 class Symbol def with(*args, &block) ->(caller, *rest) { caller.send(self, *rest, *args, &block) } end end 

Ce qui vous permettra de ne pas faire que cela:

 a = [1,3,5,7,9] a.map(&:+.with(2)) # => [3, 5, 7, 9, 11] 

Mais aussi beaucoup d’autres trucs sympas, comme passer plusieurs parameters:

 arr = ["abc", "babc", "great", "fruit"] arr.map(&:center.with(20, '*')) # => ["********abc*********", "********babc********", "*******great********", "*******fruit********"] arr.map(&:[].with(1, 3)) # => ["bc", "abc", "rea", "rui"] arr.map(&:[].with(/a(.*)/)) # => ["abc", "abc", "at", nil] arr.map(&:[].with(/a(.*)/, 1)) # => ["bc", "bc", "t", nil] 

Et même travailler avec inject , qui passe deux arguments au bloc:

 %w(abecd ab cd).inject(&:gsub.with('cde')) # => "cdeeecde" 

Ou quelque chose de super cool en passant des blocs [sténographie] au bloc sténographique:

 [['0', '1'], ['2', '3']].map(&:map.with(&:to_i)) # => [[0, 1], [2, 3]] [%w(ab), %w(cd)].map(&:inject.with(&:+)) # => ["ab", "cd"] [(1..5), (6..10)].map(&:map.with(&:*.with(2))) # => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]] 

Voici une conversation que j’ai eu avec @ArupRakshit pour l’expliquer davantage:
Pouvez-vous fournir des arguments à la syntaxe map (&: method) dans Ruby?


Comme @amcaplan a suggéré dans le commentaire ci – dessous , vous pouvez créer une syntaxe plus courte, si vous renommez la méthode with pour call . Dans ce cas, ruby ​​a un raccourci intégré pour cette méthode spéciale.

Donc, vous pouvez utiliser ce qui précède comme ceci:

 class Symbol def call(*args, &block) ->(caller, *rest) { caller.send(self, *rest, *args, &block) } end end a = [1,3,5,7,9] a.map(&:+.(2)) # => [3, 5, 7, 9, 11] [(1..5), (6..10)].map(&:map.(&:*.(2))) # => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]] 

Pour votre exemple peut être fait a.map(&2.method(:+)) .

 Arup-iMac:$ pry [1] pry(main)> a = [1,3,5,7,9] => [1, 3, 5, 7, 9] [2] pry(main)> a.map(&2.method(:+)) => [3, 5, 7, 9, 11] [3] pry(main)> 

Voici comment ça marche: –

 [3] pry(main)> 2.method(:+) => # [4] pry(main)> 2.method(:+).to_proc => # [5] pry(main)> 2.method(:+).to_proc.call(1) => 3 

2.method(:+) donne un object Method . Puis & , sur 2.method(:+) , en fait un appel à la méthode #to_proc , qui en fait un object Proc . Puis suivez ce que vous appelez l’opérateur &: dans Ruby? .

Comme le post que vous avez lié à confirme, a.map(&:class) n’est pas un raccourci pour a.map {|x| x.class} a.map {|x| x.class} mais pour un a.map(&:class.to_proc) .

Cela signifie que to_proc est appelé sur tout ce qui suit l’opérateur & .

Vous pouvez donc lui donner directement un Proc :

 a.map(&(Proc.new {|x| x+2})) 

Je sais que cela va probablement à l’encontre du but de votre question, mais je ne vois aucun autre moyen de contourner cela – ce n’est pas que vous spécifiez la méthode à appeler, vous transmettez simplement quelque chose qui répond à to_proc .

Réponse courte: non

Après la réponse de @rkon, vous pouvez aussi faire ceci:

 a = [1,3,5,7,9] a.map &->(_) { _ + 2 } # => [3, 5, 7, 9, 11] 

Au lieu de patcher vous-même les classes principales, comme dans la réponse acceptée, il est plus court et plus propre d’utiliser les fonctionnalités du joyau Facets :

 require 'facets' a = [1,3,5,7,9] a.map &:+.(2) 

Il y a une autre option native pour les énumérables qui n’est jolie que pour deux arguments à mon avis. la classe Enumerable a la méthode with_object qui renvoie ensuite un autre Enumerable . Vous pouvez donc appeler & operator pour une méthode avec chaque élément et l’object comme arguments.

Exemple:

a = [1,3,5,7,9] a.to_enum.with_object(2).map(&:+) # => [3, 5, 7, 9, 11]

Dans le cas où vous voulez plus d’arguments, vous devriez répéter le processus mais c’est moche à mon avis:

a = [1,3,5,7,9] a.to_enum.with_object(2).map(&:+).to_enum.with_object(5).map(&:+) # => [8, 10, 12, 14, 16]