使用自定义规则设置

时间:2012-04-14 13:18:51

标签: ruby-on-rails ruby

根据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拒绝元素?

2 个答案:

答案 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)的行。