为什么Symbol#to_proc有这种类型的行为?

时间:2014-01-30 16:25:06

标签: ruby

我发现这个示例代码在Ruby中实现了自定义Symbol#to_proc:

class Symbol
  def to_proc
    puts "In the new Symbol#to_proc!"
    Proc.new { |obj| obj.send(self) }
  end
end

它包含额外的“puts ...”字符串,以确保它不是内置方法。当我执行代码

p %w{ david black }.map(&:capitalize)

结果是:

In the new Symbol#to_proc!
["David", "Black"]

但为什么不是这样的?

In the new Symbol#to_proc!
["David"]
In the new Symbol#to_proc!
["Black"]

我的逻辑是这样的:map逐个产生元素来阻止。 Block获取第一个元素并执行.to_proc,而不是第二个元素。但为什么只执行一次执行?

2 个答案:

答案 0 :(得分:6)

调用to_proc方法一次以返回随后重复使用的Proc对象,这样您就可以看到正确的行为。

如果你把内线放进去,你会看到你期待的东西:

class Symbol
  def to_proc
    Proc.new { |obj|
      puts "In the new Symbol#to_proc!"
      obj.send(self)
    }
  end
end

答案 1 :(得分:3)

在Ruby中,map使用块。 &运算符在其后面的对象上调用to_proc,并将调用to_proc返回的值传递给block。有了这些信息,让我们再看看你的例子。在您的示例中,&:capitalize将导致to_proc上的:capitalize方法调用。由于:capitalize是一个符号,它将在Symbol类上调用to_proc,由您重新定义。

:capitalize.to_proc

将返回:

In the new Symbol#to_proc!
=> #<Proc:0x007fa08183df28@(irb):4>

&运算符将使用返回的Proc对象,并将该proc对象传递给map作为块。在重新定义的to_proc方法定义中,puts刚刚执行,并且puts打印到控制台(假设您在控制台中运行它),您将看到它已打印。它永远不会传递给地图,所以你永远不会看到它打印两次。

但是,如果您想要您期望的行为,请使用第一个答案。希望它有所帮助。