How does Ruby's Array.| compare elements for e

2020-07-11 08:34发布

Here's some example code:

class Obj
  attr :c, true

  def == that
    p '=='
    that.c == self.c
  end
  def <=> that
    p '<=>'
    that.c <=> self.c
  end
  def equal? that
    p 'equal?'
    that.c.equal? self.c
  end
  def eql? that
    p 'eql?'
    that.c.eql? self.c
  end
end

a = Obj.new
b = Obj.new

a.c = 1
b.c = 1

p [a] | [b]

It prints 2 objects but it should print 1 object. None of the comparison methods get called. How is Array.| comparing for equality?

标签: ruby
3条回答
疯言疯语
2楼-- · 2020-07-11 09:08

Ruby's Array class is implemented in C, and from what I can tell, uses a custom hash table to check for equality when comparing objects in |. If you wanted to modify this behavior, you'd have to write your own version that uses an equality check of your choice.

To see the full implementation of Ruby's Array#|: click here and search for "rb_ary_or(VALUE ary1, VALUE ary2)"

查看更多
够拽才男人
3楼-- · 2020-07-11 09:27

Ruby is calling the hash functions and they are returning different values, because they are still just returning the default object_id. You will need to def hash and return something reflecting your idea of what makes an Obj significant.

>> class Obj2 < Obj
>>   def hash; t = super; p ['hash: ', t]; t; end
>> end
=> nil
>> x, y, x.c, y.c = Obj2.new, Obj2.new, 1, 1
=> [#<Obj2:0x100302568 @c=1>, #<Obj2:0x100302540 @c=1>, 1, 1]
>> p [x] | [y]
["hash: ", 2149061300]
["hash: ", 2149061280]
["hash: ", 2149061300]
["hash: ", 2149061280]
[#<Obj2:0x100302568 @c=1>, #<Obj2:0x100302540 @c=1>]
查看更多
女痞
4楼-- · 2020-07-11 09:33

Array#| is implemented using hashs. So in order for your type to work well with it (as well as with hashmaps and hashsets), you'll have to implement eql? (which you did) and hash (which you did not). The most straight forward way to define hash meaningfully would be to just return c.hash.

查看更多
登录 后发表回答