Ruby设置自定义类

时间:2017-08-11 10:02:44

标签: ruby set

我正在尝试正确实现一些自定义类,以便在集合中使用它们。自定义类B包含一个包含类A对象的数组实例变量。这是一个MWE:

#!/usr/bin/env ruby

require 'set'

class A
  attr_reader :a

  def initialize(a)
    @a = a
  end

  def hash
    @a.hash
  end

  def eql?(other)
    @a == other.a
  end
end

class B
  attr_reader :instances

  def initialize
    @instances = Array.new
  end

  def add(i)
    @instances.push(i)
  end

  def hash
    @instances.hash
  end

  def eql?(other)
    @instances == other.instances
    #@instances.eql?(other.instances)
  end
end

s = Set.new

b1 = B.new
b1.add(A.new(4))
b1.add(A.new(5))

b2 = B.new
b2.add(A.new(4))
b2.add(A.new(5))

s.add(b1)
s.add(b1)
s.add(b2)
puts s.size

输出为2,预期为1,因为b1b2是使用相同值构建的对象。

如果我在B类的eql?实现中使用==而不是eql?,那么输出是正确的。根据{{​​3}}中==的定义,==的使用不应该在这里正确吗?我理解这个错误在哪里?

1 个答案:

答案 0 :(得分:1)

TL; DR因为:

  • Array#==通过==
  • 比较元素
  • Array#eql?通过eql?
  • 比较元素
  

根据ruby文档中==的定义,==的使用不应该在这里正确吗?

如果您通过Array#==比较数组,相应的项目也会通过==进行比较:(强调添加)

  

如果两个数组包含相同数量的元素并且每个元素等于(根据Object#==

,则它们是相等的

示例:

[A.new(4)] == [A.new(4)]
#=> false

它失败,因为元素根据==不相等:

A.new(4) == A.new(4)
#=> false
  

如果我在类eql?==的实现中使用eql?而不是B,那么输出就是正确的。

Array#eql?有效,因为它会通过eql?比较相应的项目:(强调添加)

  

如果self和other是同一个对象,或者两个数组都具有相同的内容(根据Object#eql?),则返回true

示例:

[A.new(4)].eql? [A.new(4)]
#=> true

因为:

A.new(4).eql? A.new(4)
#=> true

为了让==正常工作,您必须实施A#==

class A
  # ...
  def eql?(other)
    @a == other.a
  end
  alias_method :==, :eql?
end

A.new(4) == A.new(4)     #=> true
[A.new(4)] == [A.new(4)] #=> true