2012-04-14 65 views
5

根据Set doc,使用eql?比较集合中的元素。用自定义规则设置

我有一个类,如:

class Foo 
    attr_accessor :bar, :baz 

    def initialize(bar = 1, baz = 2) 
    @bar = bar 
    @baz = baz 
    end 

    def eql?(foo) 
    bar == foo.bar && baz == foo.baz 
    end 
end 

在控制台:

f1 = Foo.new 
f2 = Foo.new 
f1.eql? f2 #=> true 

但是......

s = Set.new 
s << f1 
s << f2 
s.size #=> 2 

因为f1等于f2s应该包括其中。

如何使set拒绝具有自定义规则的元素?

回答

7

你链接到明确地说(重点煤矿)的文档:

每对夫妇要素的平等根据Object#eql?
Object#hash确定,因为Set使用Hash作为存储。

如果添加hash方法的类,为eql?对象返回相同的值,它的工作原理:

# With your current class 

f1, f2 = Foo.new, Foo.new 
p f1.eql?(f2) 
#=> true 
p f1.hash==f2.hash 
#=> false 
p Set[f1,f2].length 
#=> 2 

# Fix the problem 
class Foo 
    def hash 
    [bar,hash].hash 
    end 
end 

f1, f2 = Foo.new, Foo.new 
p f1.eql?(f2) 
#=> true 
p f1.hash==f2.hash 
#=> true 
p Set[f1,f2].length 
#=> 1 

说实话,我从来没有怎么写的伟大意义一个定制hash方法时,涉及多个值。

+0

那么我该怎么办? – apneadiving 2012-04-14 13:28:36

+0

@apneadiving查看我的编辑。 – Phrogz 2012-04-14 13:29:58

+3

难道你不能只用'[bar,baz] .hash'作为散列吗? – 2012-04-14 13:36:08

0

根据Ruby编程语言手册(由Ruby语言创建者松本幸雄合着)hash方法的通用配方涉及一些数值(17/37)常量的用法。对于给定的例子建议的解决办法是这样的:

def hash 
    code = 17 
    code = 37*code + @bar.hash 
    code = 37*code + @baz.hash 
    code 
end 

所以一般我们重复“代码= 37 *代码+ @ x.hash”为每个显著实例变量(@x)线。

+0

btw:提到这种方法是从Joshua Bloch(Prentice Hall)着的Effective Java一书改编而来的, – Kotu 2014-04-01 09:19:47