我正在学习轨道并遵循这个线程。我坚持使用to_proc
方法。我认为符号只是字符串的替代品(它们就像字符串,但在内存方面更便宜(。如果我缺少符号,请告诉我。请以简单的方式解释to_proc
的含义及其用途。
某些方法采用块,并且此模式经常出现在块中:
{|x| x.foo}
人们希望以更简洁的方式写出来。为了做到这一点,他们使用以下组合:符号、方法Symbol#to_proc
、隐式类转换和&
运算符。如果将&
放在参数位置的Proc
实例前面,则会将其解释为块。如果将Proc
实例以外的其他内容与 &
组合在一起,则隐式类转换将尝试使用该对象上定义的方法(如果有(将其转换为Proc
实例to_proc
。如果是Symbol
实例,to_proc
的工作方式如下:
:foo.to_proc # => ->x{x.foo}
例如,假设您编写:
bar(&:foo)
&
运算符与 :foo
结合使用,这不是一个Proc
实例,因此隐式类强制转换Symbol#to_proc
应用于它,从而给出->x{x.foo}
。该&
现在适用于此,并被解释为一个块,它给出:
bar{|x| x.foo}
解释这一点的最简单方法是用一些例子。
(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] 调用其收集方法,并且 succ 是由类 Integer 定义的方法。因此,此参数是收集数组中的每个元素并返回其后继元素并从中创建一个新数组的简写方式,该数组的结果是 [2,3,4]。符号 :succ 正在转换为 Proc 对象,因此它调用数组的 succ 方法。
对我来说,最清晰的解释是看到它的简单实现。 如果我重新实现 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 "4n5n6n"
puts [4,5,6].map(&:to_s).first.class # prints 'String'
对于任何仍然有点困惑的人来说,运行以下代码可能会让事情更清晰一些:
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_proc
或Array.map
的实际实现,它们只是简化版本,我用它来演示map &:to_s
和类似调用的工作原理。