4

上周我想在stackoverflow.com上回答一个问题,但是在irb中运行了一些测试后,我发现了一件有趣的事情。

class X
  def ==(other)
    p "X#=="
    super
  end
end

data = [ 1 ]
data.include?(X.new)

我希望这里Array#include?会调用Fixnum#==数组中的每个项目。所以X#==永远不会调用 is 并且永远不会打印调试消息。

但实际上在我的 ruby​​ 版本(REE 1.8.7、MRI 1.8.7、1.9.2 和 1.9.3)中,它会打印出X#==调试消息。

如果我这样做truefalsenil什至Object.new它永远不会打印出X#==消息。

但如果我重新定义Fixnum#==这样的:

class Fixnum
  def ==(other)
    p "Fixnum#=="
    super
  end
end

在打印调试消息后实际上调用了原始实现,它打印出来Fixnum#==并且X#==永远不会像我最初预期的那样打印出来。

更新

当我用针切换干草堆时,它变得更加疯狂:

data = [ X.new ]
data.include?(1)

X#==即使它#==之前调用了针上的方法,它也会打印出来。

有人能指出这背后的原因吗?或者只是一个优化问题?

4

1 回答 1

4

因此include?将发送:==到数组的每个元素。

如果您的元素是truefalsenil,则相等性测试很容易失败,因为只有trueto==true...

对于Fixnums,就不是很清楚了,例如1 == 1.0 # => true。因此Fixnum#==,在出现未知论点的情况下会保持礼貌,并颠倒论点的顺序。这将允许您定义自己的“数字”类型。

现在更让你困惑的是,为了理解发生了什么,你重新定义 Fixnum#==了. 调用super不会调用原始方法,而是调用Object#==。尝试alias_method_chain(或者prepend如果在 Ruby 2.0 中!)

顺便说一句,查看实际来源,Fixnum将直接处理Fixnum,BignumFloat. 对于其他内置类(例如Rational, Complex, BigDecimal)以及用户类,Fixnum#==将反转接收器和参数。我不会依赖它为 执行此操作的事实Rational,但所有实现都会为用户类执行此操作。

于 2013-04-13T04:26:02.590 回答