在 ruby​​ 中将 &:method 和 :method 作为函数参数传递的区别

Sen*_*jai 1 ruby symbols function-pointers metaprogramming ruby-on-rails

我正在努力理解何时使用&符号将符号传递给表示方法的函数。例如,如果我想计算范围 1..10 的总和,我可以执行以下操作:

(1..10).inject(:+)

这最初让我相信,如果你想传递一个符号来定义一个方法来“神奇地”在函数中使用,你可以将函数名称作为符号传递。但是后来我在 Rails 中看到了类似的东西:

total = Product.find(product_list).sum(&:price)

如果我理解正确,&:price 与调用 :price.to_proc 相同。我不明白上面是如何工作的。

Ari*_*iao 5

在方法调用的实际参数列表中,&object是内置语法,它将

  1. 转换object为 Proc 使用object.to_proc
  2. 将 Proc 作为方法的块参数传递

Symbol#to_proc将符号(例如:the_symbol)转换为proc {|obj| obj.send(:the_symbol)}. 每当object响应to_proc方法并返回 Proc时,您都可以使用此语法。

abc = "aha~"
class << abc
  def to_proc
    proc {|obj| obj.to_i * 2 }
  end
end
p ["1", "2", "3"].map(&abc)
#=> [2, 4, 6]
Run Code Online (Sandbox Code Playgroud)

(1..10).inject(:+)显示注入接受一个符号作为参数。如何使用符号是方法特定的行为。在inject的特殊情况下,它具有与 相同的效果(1..10).inject{|a, b| a.send(:+, b)}。它只是一个简单的、普通的参数,效果取决于接受符号作为参数的方法的实现。

请注意,sum在 ActiveSupport 中接受一个带有单个参数的块,其效果是“将原始序列中的值映射到新的值并计算它们的总和”,因此

total = Product.find(product_list).sum(&:price)
Run Code Online (Sandbox Code Playgroud)

等价于

total = Product.find(product_list).sum(&proc{|p| p.send(:price)})
# or you may write
total = Product.find(product_list).sum{|p| p.price }
Run Code Online (Sandbox Code Playgroud)

它具有与以下相同的返回值,但不会产生中间临时数组:

total = Product.find(product_list).map{|p| p.price}.sum
Run Code Online (Sandbox Code Playgroud)