上周我想在stackoverflow.com上回答一个问题,但在 irb 中进行了一些测试后,我发现了一件有趣的事情。
class X
def ==(other)
p "X#=="
super
end
end
data = [ 1 ]
data.include?(X.new)
我希望Array#include?
会在数组中的每个项目上调用Fixnum#==
。因此永远不会调用X#==
并且永远不会打印调试消息。
但实际上在我的ruby版本(REE 1.8.7,MRI 1.8.7,1.9.2和1.9.3)中,它打印出X#==
调试消息。
如果我在true
或false
或nil
或甚至Object.new
上执行此操作,则永远不会打印出X#==
消息。
但如果我像这样重新定义Fixnum#==
:
class Fixnum
def ==(other)
p "Fixnum#=="
super
end
end
在打印调试消息后实际上调用了原始实现,打印出Fixnum#==
并且X#==
永远不会按照我原先的预期打印出来。
当我用针切换干草堆时,它变得更加疯狂了:
data = [ X.new ]
data.include?(1)
它打印出X#==
,即使它之前在针上调用了#==
方法。
有人能指出背后的原因是什么?或者只是一个优化问题?
答案 0 :(得分:4)
因此include?
会将:==
发送到您阵列的每个元素。
如果您的元素是true
,false
和nil
,则相等测试很容易失败,因为只有true
==
到true
等等......
对于Fixnum
,它不是那么清楚,例如1 == 1.0 # => true
。因此Fixnum#==
在未知参数的情况下将是礼貌的,并且反转参数的顺序。这将允许您定义自己的“数字”类型。
现在让你感到困惑的是,为了理解发生了什么,你重新定义 Fixnum#==
。调用super
不会调用原始方法,而是调用Object#==
。试试alias_method_chain
(或prepend
如果在Ruby 2.0中!)
BTW,查看实际来源,Fixnum
将直接处理Fixnum
,Bignum
和Float
。对于其他内置类(例如Rational
,Complex
,BigDecimal
)以及用户类,Fixnum#==
将反转接收器和参数。我不会依赖它为Rational
执行此操作的事实,但所有实现都将为用户类执行此操作。