您可能熟悉以下Ruby简写(a
是一个数组):
a.map(&:method)
例如,在irb中尝试以下内容:
>> a=[:a, 'a', 1, 1.0]
=> [:a, "a", 1, 1.0]
>> a.map(&:class)
=> [Symbol, String, Fixnum, Float]
语法a.map(&:class)
是a.map {|x| x.class}
的缩写。
在“What does map(&:name) mean in Ruby?”中详细了解此语法。
通过语法&:class
,您为每个数组元素进行方法调用class
。
我的问题是:你能为方法调用提供参数吗?如果是这样,怎么样?
例如,如何转换以下语法
a = [1,3,5,7,9]
a.map {|x| x + 2}
到&:
语法?
我并不是说&:
语法更好。
我只是对使用&:
语法和参数的机制感兴趣。
我假设你知道+
是Integer类的一个方法。您可以在irb中尝试以下操作:
>> a=1
=> 1
>> a+(1)
=> 2
>> a.send(:+, 1)
=> 2
答案 0 :(得分:127)
您可以在Symbol
上创建一个简单的补丁:
class Symbol
def with(*args, &block)
->(caller, *rest) { caller.send(self, *rest, *args, &block) }
end
end
这使您不仅可以这样做:
a = [1,3,5,7,9]
a.map(&:+.with(2))
# => [3, 5, 7, 9, 11]
但也有许多其他很酷的东西,比如传递多个参数:
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]
甚至可以使用inject
,它将两个参数传递给块:
%w(abecd ab cd).inject(&:gsub.with('cde'))
# => "cdeeecde"
或者通过[速记]阻止到速记块的超酷:
[['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]]
这是我与@ArupRakshit进行的一次对话,进一步解释: Can you supply arguments to the map(&:method) syntax in Ruby?
正如comment below中@amcaplan所建议的那样,如果将with
方法重命名为call
,则可以创建更短的语法。在这种情况下,ruby具有此特殊方法.()
的内置快捷方式。
所以你可以像这样使用上面的内容:
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]]
答案 1 :(得分:42)
您的示例可以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)>
以下是它的工作原理: -
[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(:+)
会提供Method
个对象。然后在&
上的2.method(:+)
实际上是一个调用#to_proc
方法,该方法使其成为Proc
对象。然后按What do you call the &: operator in Ruby?。
答案 2 :(得分:10)
在您关联到确认的帖子中,a.map(&:class)
不是a.map {|x| x.class}
的简写,而是a.map(&:class.to_proc)
的简写。
这意味着在to_proc
运算符后面的任何内容都会调用&
。
所以你可以直接给它Proc
代替:
a.map(&(Proc.new {|x| x+2}))
我知道这很可能会破坏你的问题的目的,但我看不到任何其他方法 - 你不是指定要调用的方法,你只是传递一些响应{{1}的方法}。
答案 3 :(得分:9)
简短回答:不。
按照@ rkon的回答,你也可以这样做:
a = [1,3,5,7,9]
a.map &->(_) { _ + 2 } # => [3, 5, 7, 9, 11]
答案 4 :(得分:5)
与使用Facets gem的功能:
相比,您可以自己修补核心类,而不是在接受的答案中修补核心类。require 'facets'
a = [1,3,5,7,9]
a.map &:+.(2)
答案 5 :(得分:2)
在我看来,可枚举还有另一个本机选项,仅适用于两个参数。类 Enumerable 具有方法 with_object ,该方法随后返回另一个 Enumerable 。因此,您可以为每个项目和对象作为参数的方法调用&运算符。
示例:
a = [1,3,5,7,9]
a.to_enum.with_object(2).map(&:+) # => [3, 5, 7, 9, 11]
如果您想要更多参数,则应重复此过程,但我认为这很丑陋:
a = [1,3,5,7,9]
a.to_enum.with_object(2).map(&:+).to_enum.with_object(5).map(&:+) # => [8, 10, 12, 14, 16]
答案 6 :(得分:0)
我不确定已经发布的Symbol#with
,我对其进行了相当简化,并且效果很好:
class Symbol
def with(*args, &block)
lambda { |object| object.public_send(self, *args, &block) }
end
end
(也使用public_send
而不是send
来防止调用私有方法,而且caller
已经被ruby使用,所以很混乱)
答案 7 :(得分:0)
如果你的方法需要的所有参数都是数组中的一个元素,这可能是最简单的方法:
def double(x)
x * 2
end
[1, 2, 3].map(&method(:double))
=> [2, 4, 6]