你能在 Ruby 中为 map(&:method) 语法提供参数吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23695653/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 06:29:29  来源:igfitidea点击:

Can you supply arguments to the map(&:method) syntax in Ruby?

ruby

提问by Zack Xu

You're probably familiar with the following Ruby shorthand (ais an array):

您可能熟悉以下 Ruby 简写(a是一个数组):

a.map(&:method)

For example, try the following in irb:

例如,在 irb 中尝试以下操作:

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

The syntax a.map(&:class)is a shorthand for a.map {|x| x.class}.

语法a.map(&:class)a.map {|x| x.class}.

Read more about this syntax in "What does map(&:name) mean in Ruby?".

在“ map(&:name) 在 Ruby 中的含义是什么?”中阅读有关此语法的更多信息。

Through the syntax &:class, you're making a method call classfor each array element.

通过语法&:class,您可以class为每个数组元素进行方法调用。

My question is: can you supply arguments to the method call? And if so, how?

我的问题是:你能为方法调用提供参数吗?如果是这样,如何?

For example, how do you convert the following syntax

比如下面的语法怎么转换

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

to the &:syntax?

&:语法?

I'm not suggesting that the &:syntax is better. I'm merely interested in the mechanics of using the &:syntax with arguments.

我并不是说&:语法更好。我只是对使用&:带参数的语法的机制感兴趣。

I assume you know that +is a method on Integer class. You can try the following in irb:

我假设你知道这+是一个 Integer 类的方法。您可以在 irb 中尝试以下操作:

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

回答by Uri Agassi

You can create a simple patch on Symbollike this:

您可以Symbol像这样创建一个简单的补丁:

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

Which will enable you to do not only this:

这将使您不仅可以做到这一点:

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

But also a lot of other cool stuff, like passing multiple 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] 

And even work with inject, which passes two arguments to the block:

甚至使用inject,它将两个参数传递给块:

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

Or something super cool as passing [shorthand] blocks tothe shorthand block:

或者将 [速记] 块传递速记块时非常酷的事情:

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

Here is a conversation I had with @ArupRakshit explaining it further:
Can you supply arguments to the map(&:method) syntax in Ruby?

这是我与@ArupRakshit 进行的进一步解释的对话:
您能否为 Ruby 中的 map(&:method) 语法提供参数?



As @amcaplan suggested in the comment below, you could create a shorter syntax, if you rename the withmethod to call. In this case, ruby has a built in shortcut for this special method .().

正如@amcaplan 在下面评论中建议的那样,如果将with方法重命名为call. 在这种情况下,ruby 为这个特殊方法提供了一个内置的快捷方式.()

So you could use the above like this:

所以你可以像这样使用上面的:

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

回答by Arup Rakshit

For your example can be done a.map(&2.method(:+)).

对于你的例子可以做到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)> 

Here is how it works :-

下面是它的工作原理 :-

[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(:+)gives a Methodobject. Then &, on 2.method(:+), actually a call #to_procmethod, which is making it a Procobject. Then follow What do you call the &: operator in Ruby?.

2.method(:+)给出一个Method对象。然后 &, on 2.method(:+),实际上是一个调用#to_proc方法,这使它成为一个Proc对象。然后按照Ruby 中的 &: 运算符是什么?.

回答by Kostas Rousis

As the post you linked to confirms, a.map(&:class)is not a shorthand for a.map {|x| x.class}but for a.map(&:class.to_proc).

当你链接到确认后,a.map(&:class)是不是一个速记a.map {|x| x.class}a.map(&:class.to_proc)

This means that to_procis called on whatever follows the &operator.

这意味着to_proc&运算符后面的任何内容上都会调用它。

So you could give it directly a Procinstead:

所以你可以直接给它一个Proc

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

I know that most probably this defeats the purpose of your question but I can't see any other way around it - it's not that you specify which method to be called, you just pass it something that responds to to_proc.

我知道这很可能违背了您的问题的目的,但我看不到任何其他方法 - 不是您指定要调用的方法,而是将响应to_proc.

回答by Agis

Short answer: No.

简短的回答:没有。

Following @rkon's answer, you could also do this:

按照@rkon 的回答,您也可以这样做:

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

回答by michau

Instead of patching core classes yourself, as in the accepted answer, it's shorter and cleaner to use the functionality of the Facets gem:

不像在接受的答案中那样自己修补核心类,使用Facets gem的功能更短更干净:

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

回答by Pedro Augusto

There is another native option for enumerables which is pretty only for two arguments in my opinion. the class Enumerablehas the method with_objectwhich then returns another Enumerable. So you can call & operator for a method with each item and the object as arguments.

可枚举还有另一个本机选项,在我看来,它仅适用于两个参数。类Enumerable具有with_object方法,然后返回另一个Enumerable。因此,您可以使用每个项目和对象作为参数为方法调用 & 运算符。

Example:

例子:

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(&:+) # => [3, 5, 7, 9, 11]

In the case you want more arguments you should repeat the proccess but it's ugly in my opinion:

如果你想要更多的论点,你应该重复这个过程,但我认为这很丑陋:

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

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

回答by localhostdotdev

I'm not sure about the Symbol#withalready posted, I simplified it quite a bit and it works well:

我不确定Symbol#with已经发布的内容,我对其进行了相当多的简化,并且效果很好:

class Symbol
  def with(*args, &block)
    lambda { |object| object.public_send(self, *args, &block) }
  end
end

(also uses public_sendinstead of sendto prevent calling private methods, also calleris already used by ruby so this was confusing)

(也使用public_send代替send来防止调用私有方法,也caller已经被 ruby​​ 使用,所以这很混乱)