Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define hash function for a class including comparable?

Imagine a class including comparable like this:

class Element
  include Comparable
  attr_accessor :name, :pos_x, :pos_y

  def initialize(name, pos_x, pos_y)
    @name = name
    @pos_x = pos_x
    @pos_y = pos_y
  end

  def <=>(other)
    if (@pos_x == other.pos_x) and (@pos_y == other.pos_y)
      return 0
    else 
      return @name <=> other.name
    end
  end

  def eql?(other)
    self == other
  end
end

How would you implement the hash function such that a.hash == b.hash in this case? In general I'd do:

def hash
  @name.hash
end

But this does not include pos_x and pos_y.

like image 249
Paddi91 Avatar asked Nov 01 '22 03:11

Paddi91


1 Answers

Unfortunately this is mathematically impossible to define a valid hash function in this case.

Let a, b be two elements with equal positions, and different names. According to eql? definition, this implies that h(a) == h(b). Since this is true for any names values, the hash function is to be independent on name attribute, which is however in contradiction with the second check. Hence there is no hash function for this eql? definition. Sorry. :(

Update:

As noted by toro2k - your equality definition is not transitive. In general if a == b and b == c, it is required that a == c. According to your eql? function:

{pos_x: 1, pos_y: 1, name: 'a'} == {pos_x: 1, pos_y: 1, name: 'b'}
{pos_x: 1, pos_y: 1, name: 'b'} == {pos_x: 2, pos_y: 2, name: 'b'}

but

{pos_x: 1, pos_y: 1, name: 'a'} != {pos_x: 2, pos_y: 2, name: 'b'}

That's the root of your problem here.

like image 177
BroiSatse Avatar answered Nov 15 '22 06:11

BroiSatse