Pregunta ¿Puedes proporcionar argumentos a la sintaxis del mapa (&: método) en Ruby?


Probablemente estés familiarizado con la siguiente taquigrafía de Ruby (a es una matriz):

a.map(&:method)

Por ejemplo, intente lo siguiente en irb:

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

La sintaxis a.map(&:class) es una forma abreviada de a.map {|x| x.class}.

Lea más sobre esta sintaxis en "¿Qué significa map (&: name) en Ruby?".

A través de la sintaxis &:class, estás haciendo una llamada a un método class para cada elemento de la matriz.

Mi pregunta es: ¿pueden proporcionar argumentos a la llamada al método? Y si es así, ¿cómo?

Por ejemplo, cómo convertir la siguiente sintaxis

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

al &: ¿sintaxis?

No estoy sugiriendo que el &: la sintaxis es mejor Me interesa simplemente la mecánica de usar el &: sintaxis con argumentos

Supongo que sabes eso + es un método en la clase Integer. Puede probar lo siguiente en irb:

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

76
2018-05-16 13:01


origen


Respuestas:


Puede crear un parche simple en Symbol Me gusta esto:

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

Lo cual le permitirá no solo hacer esto:

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

Pero también muchas otras cosas interesantes, como pasar múltiples parámetros:

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] 

E incluso trabajar con inject, que pasa dos argumentos al bloque:

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

O algo super chulo como pasar bloques [taquigrafía] a el taquigrafía:

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

Aquí hay una conversación que tuve con @ArupRakshit para explicarlo más a fondo:
¿Puedes proporcionar argumentos a la sintaxis del mapa (&: método) en Ruby?


Como @amcaplan sugirió en el Comenta abajo, podría crear una sintaxis más corta, si cambia el nombre del with método para call. En este caso, ruby ​​tiene un atajo incorporado para este método especial .().

Entonces podrías usar lo anterior así:

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]] 

110
2018-05-17 12:55



Para que tu ejemplo se pueda hacer 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)> 

Así es como funciona :-

[3] pry(main)> 2.method(:+)
=> #<Method: Fixnum#+>
[4] pry(main)> 2.method(:+).to_proc
=> #<Proc:0x000001030cb990 (lambda)>
[5] pry(main)> 2.method(:+).to_proc.call(1)
=> 3

2.method(:+) da una Method objeto. Entonces &, en 2.method(:+), en realidad una llamada #to_proc método, que lo está convirtiendo en un Proc objeto. Luego sigue ¿Cómo llamas al operador &: en Ruby?.


35
2018-05-16 13:17



Como confirma la publicación a la que vinculó, a.map(&:class) no es una abreviatura de a.map {|x| x.class} pero para a.map(&:class.to_proc).

Esto significa que to_proc se invoca en lo que sigue a la & operador.

Entonces podrías darle directamente un Proc en lugar:

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

Sé que lo más probable es que esto derrote el propósito de su pregunta, pero no puedo ver otra forma de evitarlo: no es que especifique qué método utilizar, simplemente le pasa algo que responde a to_proc.


9
2018-05-16 13:13



Respuesta corta: No.

Siguiendo la respuesta de @ rkon, también puedes hacer esto:

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

7
2018-05-16 13:41



En lugar de parchear las clases básicas usted mismo, como en la respuesta aceptada, es más corto y más limpio utilizar la funcionalidad del Gema facetas:

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

4
2017-08-26 14:24