Как работает Ruby's Array. сравнить элементы на равенство? - PullRequest
6 голосов
/ 27 марта 2010

Вот пример кода:

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]

Он печатает 2 объекта, но должен печатать 1 объект. Ни один из методов сравнения не вызывается. Как массив. сравнивая на равенство?

Ответы [ 3 ]

6 голосов
/ 27 марта 2010

Array#| реализовано с использованием хэшей. Таким образом, чтобы ваш тип хорошо работал с ним (а также с хэш-картами и хэш-наборами), вам нужно реализовать eql? (что вы сделали) и hash (что вы не сделали). Самый простой способ осмысленно определить хеш - это просто вернуть c.hash.

1 голос
/ 27 марта 2010

Класс Ruby Array реализован на C, и, насколько я могу судить, использует собственную хеш-таблицу для проверки на равенство при сравнении объектов в |. Если вы хотите изменить это поведение, вам придется написать собственную версию, которая использует проверку на равенство по вашему выбору.

Чтобы увидеть полную реализацию Ruby's Array#|: , нажмите здесь и найдите "rb_ary_or(VALUE ary1, VALUE ary2)"

0 голосов
/ 27 марта 2010

Ruby вызывает хеш-функции, и они возвращают разные значения, потому что они все еще просто возвращают object_id по умолчанию. Вам нужно будет def hash и вернуть что-то, отражающее ваше представление о том, что делает Obj значимым.

>> 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>]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...