想象一下这样的课程:
class Element
include Comparable
attr_accessor :name, :pos_x, :pos_y
def initialize(name, pos_x, pos_y)
@name = name
@pos_x = pos_x
@pos_y = pos_y
end
def <=>(other)
if (@pos_x == other.pos_x) and (@pos_y == other.pos_y)
return 0
else
return @name <=> other.name
end
end
def eql?(other)
self == other
end
end
在这种情况下,如何实现hash
函数a.hash == b.hash
?一般来说,我会这样做:
def hash
@name.hash
end
但这不包括pos_x
和pos_y
。
答案 0 :(得分:1)
不幸的是,在这种情况下,在数学上不可能定义有效的散列函数。
设a,b是两个具有相同位置和不同名称的元素。根据{{1}}定义,这意味着eql?
。由于任何名称值都是如此,因此哈希函数与name属性无关,但这与第二次检查相矛盾。因此,此h(a) == h(b)
定义没有散列函数。抱歉。 :(
更新
如toro2k所述 - 您的等式定义不具有传递性。通常,如果a == b和b == c,则需要a == c。根据您的eql?
功能:
eql?
但
{pos_x: 1, pos_y: 1, name: 'a'} == {pos_x: 1, pos_y: 1, name: 'b'}
{pos_x: 1, pos_y: 1, name: 'b'} == {pos_x: 2, pos_y: 2, name: 'b'}
这是你问题的根源。
答案 1 :(得分:1)
除了你已经在做的事情之外,你无能为力。请注意,您的hash
函数非常糟糕,因为它的分布很差并且会导致大量冲突,这可能会破坏Hash
的O(1)摊销的最坏情况步骤复杂度保证,但你真的无能为力。
但请注意,您的等式和排序定义存在更大的问题:它们不是相等或排序!
平等和订购关系需要
你的平等和秩序关系都违反了传递性:
one = Element.new('Z', 1, 1)
two = Element.new('A', 1, 1)
three = Element.new('A', 2, 2)
one == two # => true
two == three # => true
one == three # => false # Huh?
one <= two # => true
two <= three # => true
one <= three # => false # Huh?
使用太空船运营商的所有方法(例如Enumerable#sort
)都依赖于这些法律。因此,您的<=>
和eql?
实施从根本上被打破了。
答案 2 :(得分:0)
您可以覆盖==
,因为Object#hash
不接受参数:
def ==(o)
(@pos_x == o.pos_x && pos_y == o.pos_y) || (@name == o.name)
end
alias_method :eql?, :==