¿Puede proporcionar argumentos a la sintaxis map(&:method) en Ruby?

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

a.map(&:method)

Por ejemplo, pruebe lo siguiente en irb:

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

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

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

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

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

Por ejemplo, ¿cómo convertir la sintaxis siguiente?

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

a la sintaxis?&:

No estoy sugiriendo que la sintaxis sea mejor. Simplemente me interesa la mecánica de usar la sintaxis con argumentos.&:&:

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

>> a=1
=> 1
>> a+(1)
=> 2
>> a.send(:+, 1)
=> 2
Respuestas:9 Respuestas 9
Tiempo:hace 8 años, 4 meses
Última modificación:hace 5 meses

Solución

Puede crear un parche simple de esta manera:Symbol

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

Lo que le permitirá hacer no solo esto:

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

Pero también muchas otras cosas geniales, 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 , que pasa dos argumentos al bloque:inject

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

O algo súper genial como pasar bloques [de taquigrafía] al bloque de 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 explicándolo más:
¿Puede proporcionar argumentos a la sintaxis map(&:method) en Ruby?


Como @amcaplan sugiere en el comentario siguiente, puede crear una sintaxis más corta, si cambia el nombre del método a . En este caso, ruby tiene un atajo incorporado para este método especial.withcall.()

Así que podrías usar lo anterior de esta manera:

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

Aquí hay una versión que usa Refinamientos (que es menos hacky que el parche de mono globalmente):Symbol

module AmpWithArguments

  refine Symbol do
    def call(*args, &block)
      ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
    end
  end

end

using AmpWithArguments

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

Otras respuestas

Respuesta corta: No.

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

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

Deja un comentario