58

我正在学习 rails 并关注这个线程。我被这个to_proc方法困住了。我仅将符号视为字符串的替代品(它们类似于字符串,但在内存方面更便宜)。如果我还缺少其他符号,请告诉我。请以简单的方式解释to_proc它的含义和用途。

4

4 回答 4

114

一些方法需要一个块,并且这种模式经常出现在一个块中:

{|x| x.foo}

人们想用更简洁的方式来写。为了做到这一点,他们使用了以下组合:符号、方法Symbol#to_proc、隐式类转换和&运算符。如果你把&一个实例放在参数位置的前面Proc,那将被解释为一个块。如果您将除Proc实例之外的其他内容与 结合起来&,那么隐式类转换将尝试使用在该对象上定义的方法将其转换为Proc实例to_proc(如果有的话)。在一个Symbol实例的情况下,to_proc以这种方式工作:

:foo.to_proc # => ->x{x.foo}

例如,假设您编写:

bar(&:foo)

运算符与不是实例的&相结合,因此隐式类转换适用于它,这给出了. 现在适用于此并被解释为一个块,它给出::fooProcSymbol#to_proc->x{x.foo}&

bar{|x| x.foo}
于 2013-02-14T18:21:46.500 回答
47

解释这一点的最简单方法是举一些例子。

(1..3).collect(&:to_s)  #=> ["1", "2", "3"]

是相同的:

(1..3).collect {|num| num.to_s}  #=> ["1", "2", "3"]

[1,2,3].collect(&:succ)  #=> [2, 3, 4]

是相同的:

[1,2,3].collect {|num| num.succ}  #=> [2, 3, 4]

to_proc 返回一个 Proc 对象,它通过符号响应给定的方法。所以在第三种情况下,数组 [1,2,3] 调用它的 collect 方法和。succ 是类 Array 定义的方法。所以这个参数是一种简写方式,表示收集数组中的每个元素并返回其后继元素,并从中创建一个新数组,结果为 [2,3,4]。符号 :succ 被转换为 Proc 对象,因此它调用数组的 succ 方法。

于 2013-02-14T18:38:23.560 回答
7

对我来说,最清楚的解释是看到它的简单实现。如果我重新实现 Symbol#to_proc,它可能看起来像这样:

class Symbol  # reopen Symbol class to reimplement to_proc method
  def to_proc
    ->(object) { object.send(self) }
  end
end

my_lambda = :to_s.to_proc

puts my_lambda.(1)  # prints '1'; .() does the same thing as .call()
puts my_lambda.(1).class  # prints 'String'

puts [4,5,6].map(&:to_s)  # prints "4\n5\n6\n"
puts [4,5,6].map(&:to_s).first.class  # prints 'String'
于 2015-03-12T15:41:13.900 回答
2

对于仍然有点困惑的任何人,运行以下代码可能会使事情变得更清晰:

class Symbol
  def to_proc
    proc do |obj|
      puts "Symbol proc: #{obj}.send(:#{self})"
      obj.send(self)
    end
  end
end

class Array
  def map(&block)
    copy = self.class.new
    self.each do |index|
      puts "Array.map:   copy << block.call(#{index})"
      copy << block.call(index)
    end
    copy
  end
end

remapped_array = [0, 1, 2].map &:to_s
puts "remapped array: #{remapped_array.inspect}"

这些不是Symbol.to_procor的实际实现Array.map,它们只是我用来演示map &:to_s类似调用如何工作的简化版本。

于 2016-04-26T05:48:58.487 回答