metodos - ¿Puedes proporcionar argumentos a la sintaxis del mapa(&: método) en Ruby?
manejo de objetos en ruby (5)
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 abreviatura de a.map {|x| x.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 &:class
, está haciendo una class
llamada de método para cada elemento de 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}
a la &:
sintaxis?
No estoy sugiriendo que la sintaxis &:
sea mejor. Simplemente estoy interesado en 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
Como confirma la publicación a la que vinculó, a.map(&:class)
no es una abreviatura de a.map {|x| x.class}
a.map {|x| x.class}
pero para a.map(&:class.to_proc)
.
Esto significa que se llama a to_proc
en lo que sigue al operador &
.
Entonces, puedes darle un Proc
directamente:
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 veo otra forma de to_proc
: no es que especifique a qué método llamar, simplemente le pasa algo que responde a to_proc
.
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 de la gema Facetas :
require ''facets''
a = [1,3,5,7,9]
a.map &:+.(2)
Para su ejemplo, se puede 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 un objeto Method
. Entonces &
, en 2.method(:+)
, en realidad es un método de llamada #to_proc
, que lo está convirtiendo en un objeto Proc
. Luego, sigue ¿Cómo llamas al operador &: en Ruby? .
Puedes crear un parche simple en Symbol
como este:
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 súper genial como pasar bloques [taquigrafía] al bloque taquigráfico:
[[''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 comentario a continuación , puede crear una sintaxis más corta, si cambia el nombre del método with
que desea 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]]
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]