根据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
等于f2
,s
应该不包含这两者。
如何使用自定义规则制作set
拒绝元素?
答案 0 :(得分: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
方法有充分的理解。
答案 1 :(得分:0)
根据The Ruby Programming Language一书(由Ruby语言创建者Yukihiro Matsumoto共同撰写),哈希方法的通用配方涉及一些数字(17/37)常量用法。对于给定的示例,建议的解决方案将是:
def hash
code = 17
code = 37*code + @bar.hash
code = 37*code + @baz.hash
code
end
所以一般来说,我们重复&#34;代码= 37 *代码+ @ x.hash&#34;每个重要实例变量(@x)的行。