What are :+ and &:+ in Ruby?

2019-01-03 05:04发布

I've seen these several times but I can't figure out how to use them. The pickaxe says that these are special shortcuts but I wasn't able to find the syntactical description.

I've seen them in such contexts:

[1,2,3].inject(:+)

to calculate sum for example.

标签: ruby
2条回答
聊天终结者
2楼-- · 2019-01-03 05:42

Let's start with an easier example. Say we have an array of strings we want to have in caps:

['foo', 'bar', 'blah'].map { |e| e.upcase }
# => ['FOO', 'BAR', 'BLAH']

Also, you can create so called Proc objects (closures):

block = proc { |e| e.upcase }
block.call("foo") # => "FOO"

You can pass such a proc to a method with the & syntax:

block = proc { |e| e.upcase }
['foo', 'bar', 'blah'].map(&block)
# => ['FOO', 'BAR', 'BLAH']

What this does, is call to_proc on block and then calls that for every block:

some_object = Object.new
def some_object.to_proc
  proc { |e| e.upcase }
end

['foo', 'bar', 'blah'].map(&some_object)
# => ['FOO', 'BAR', 'BLAH']

Now, Rails first added the to_proc method to Symbol, which later has been added to the ruby core library:

:whatever.to_proc # => proc { |e| e.whatever }

Therefore you can do this:

['foo', 'bar', 'blah'].map(&:upcase)
# => ['FOO', 'BAR', 'BLAH']

Also, Symbol#to_proc is even smarter, as it actually does the following:

:whatever.to_proc # => proc { |obj, *args| obj.send(:whatever, *args) }

This means that

[1, 2, 3].inject(&:+)

equals

[1, 2, 3].inject { |a, b| a + b }
查看更多
叼着烟拽天下
3楼-- · 2019-01-03 05:43

inject accepts a symbol as a parameter, this symbol must be the name of a method or operator, which is this case is :+

so [1,2,3].inject(:+) is passing each value to the method specified by the symbol, hence summing all elements in the array.

source: https://ruby-doc.org/core-2.5.1/Enumerable.html

查看更多
登录 后发表回答