On Wed, 14 Sep 2005, David A. Black wrote:

> Hi --
>
> On Wed, 14 Sep 2005, Hugh Sasse wrote:
>
>> This being a Set I don't really need the call to include? now, but
>> it's there (from when I was using a hash for this).
>> 
>> I find two things that seem odd to me:
>> 
>> 1. eql? is never getting called, despite include?.
>> 
>> 2. I end up with duplicate students.
>> 
>> Sets *can't* hold duplicates, and include depends on eql? for Sets.
>
> Are you sure about that latter point?  In set.rb:
>
>  def include?(o)
>    @hash.include?(o)
>  end
>
> and in hash.c:
>
>    if (st_lookup(RHASH(hash)->tbl, key, 0)) {
>        return Qtrue;
>    ... }
>
> I haven't followed the trail beyond that... but I think any two
> student objects will count as different hash keys, even if they have
> similar string data.
>
>
> David

Right, there is some definite wierdness going on here.  I removed
the definition of eql? and set the hash to use MD5 sums.  I still
didn't get unique entries in my set.  Now I have

require 'md5'

class Student
         # [...]
   FIELDS = [:forename, :surname, :birth_dt, :picture, :coll_status]
   def initialize(forename0, surname0, birth_dt0,
                  picture0, coll_status0)
         # [...]
     @hash = FIELDS.inject(MD5.new()) do |d,m|
       d << send(m)
     end.hexdigest.hex
   end

   def hash
     @hash
   end

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

end

And this works.  Remmove the definition of eql? and include? always
gives untrue (I've not checked to see if it is nil or false).


This is in accordance with the entry in Pickaxe2 (page 570,
Object#hash) and ri,  that:
------------------------------------------------------------ Object#hash
      obj.hash    => fixnum
------------------------------------------------------------------------
      Generates a +Fixnum+ hash value for this object. This function must
      have the property that +a.eql?(b)+ implies +a.hash == b.hash+. The
      hash value is used by class +Hash+. Any hash value that exceeds the
      capacity of a +Fixnum+ will be truncated before being used.

(I'm not sure if my digests are too big)

What i don't really know is what the sufficient conditions are for
this?  Is it *necessary* to change hash and eql together? What are the
defaults for Set?

I suspect that my eql? ought to be

   def eql?(other)
     FIELDS.inject(true) do |b,v|
       t && (self.send(m) == other.send(m))
     end
   end

for that matter

         Hugh