From: "Mauricio Fernández" Date: 2005-09-14T22:09:07+09:00 Subject: Re: Sets, uniqueness not unique. On Wed, Sep 14, 2005 at 09:14:10PM +0900, Hugh Sasse wrote: > On Wed, 14 Sep 2005, David A. Black wrote: > >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. object.c: VALUE rb_obj_id(VALUE obj) { if (SPECIAL_CONST_P(obj)) { return LONG2NUM((long)obj); } return (VALUE)((long)obj|FIXNUM_FLAG); } [...] rb_define_method(rb_mKernel, "hash", rb_obj_id, 0); [...] > 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? The defaults are actually those of Hash. You can follow the call chain starting from static struct st_hash_type objhash = { rb_any_cmp, rb_any_hash, }; in hash.c. For user-defined classes, it will end up using #hash and #eql? defined in Kernel. [rb_any_cmp and rb_any_hash have some extra logic for Symbol, Fixnum and String values, and some core classes redefine the associated methods]. Given the above definition of Kernel#hash, if you redefine it, you'll most probably want to change #eql? too (see below). As far as Hash objects (and hence Sets) are concerned, modifying #eql? while keeping #hash unchanged would be effectless (unless you restrict it further so that obj.eql?(obj) is false, which doesn't seem quite right). static VALUE rb_obj_equal(VALUE obj1, VALUE obj2) { if (obj1 == obj2) return Qtrue; return Qfalse; } [...] rb_define_method(rb_mKernel, "eql?", rb_obj_equal, 1); -- Mauricio Fernandez