[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Hashes, Sets, and eql?

Aaron Patterson

11/24/2007 6:33:00 AM

Hey everyone!

I've been working with sets and I need to use them as a key in a hash.
Unfortunately, it looks like Sets are implemented as a Hash (which makes
sense), but it looks like Set#eql? calls eql? on the underlying Hash.

This seems bad to me. It means that I can't make two sets eql? to each
other.

Take this irb session for example:

>> require 'set'
=> true
>> a = Set.new([1,2])
=> #<Set: {1, 2}>
>> b = Set.new([1,2])
=> #<Set: {1, 2}>
>> a.eql?(b)
=> false

I understand that sets are unordered, but it still seems weird to me
that the preceding code won't work. My workaround(?) for now is
converting the set to an array and sorting the array by hash key, and
keying on that:

>> a.to_a.sort_by { |x| x.hash }.eql?(b.to_a.sort_by { |x| x.hash })
=> true

--
Aaron Patterson
http://tenderlovem...

6 Answers

MonkeeSage

11/24/2007 6:55:00 AM

0

Hi Aaron,

Set#eql? is probably just Object#eql?, which, as I understand it would
compare the actual objects (not their contents). What you want is a
"shallow" comparison (==).

irb(main):002:0> require 'set'
=> true
irb(main):003:0> a = Set.new([1,2])
=> #<Set: {1, 2}>
irb(main):004:0> b = Set.new([1,2])
=> #<Set: {1, 2}>
irb(main):005:0> a == b
=> true

Regards,
Jordan

Aaron Patterson

11/24/2007 7:26:00 AM

0

On Sat, Nov 24, 2007 at 03:55:00PM +0900, MonkeeSage wrote:
> Hi Aaron,
>
> Set#eql? is probably just Object#eql?, which, as I understand it would
> compare the actual objects (not their contents). What you want is a
> "shallow" comparison (==).

Nope. I don't want a shallow comparison. I need to use my sets as hash
keys, so I need .eql? and .hash. Set calls eql? on the internal hash object.

From the set source:

def hash # :nodoc:
@hash.hash
end

def eql?(o) # :nodoc:
return false unless o.is_a?(Set)
@hash.eql?(o.instance_eval{@hash})
end

--
Aaron Patterson
http://tenderlovem...

MonkeeSage

11/24/2007 11:35:00 AM

0

On Nov 24, 1:25 am, Aaron Patterson <aa...@tenderlovemaking.com>
wrote:

> Nope. I don't want a shallow comparison. I need to use my sets as hash
> keys, so I need .eql? and .hash. Set calls eql? on the internal hash object.

And eql? implies a.hash == b.hash. That is an object comparison, since
Object#hash gives a unique value for every object like Object#id. What
you need is shallow comparison of values rather than objects: a == b.
I'm not sure how to easily make Hash do that.

Regards,
Jordan

Phrogz

11/24/2007 2:34:00 PM

0

On Nov 24, 4:35 am, MonkeeSage <MonkeeS...@gmail.com> wrote:
> On Nov 24, 1:25 am, Aaron Patterson <aa...@tenderlovemaking.com>
> wrote:
>
> > Nope. I don't want a shallow comparison. I need to use my sets as hash
> > keys, so I need .eql? and .hash. Set calls eql? on the internal hash object.
>
> And eql? implies a.hash == b.hash. That is an object comparison, since
> Object#hash gives a unique value for every object like Object#id. What
> you need is shallow comparison of values rather than objects: a == b.
> I'm not sure how to easily make Hash do that.

Not always true:

irb(main):001:0> a1 = [:a,1,'foo']
=> [:a, 1, "foo"]
irb(main):002:0> a2 = [:a,1,'foo']
=> [:a, 1, "foo"]
irb(main):003:0> a1.hash
=> 876036089
irb(main):004:0> a2.hash
=> 876036089

This discussion (using Hashes as hash or set keys, and the
requirements and possible implementations of of eql? for this to work
as sometimes desired) occurred in the last month or two on this list.

MonkeeSage

11/24/2007 4:45:00 PM

0

On Nov 24, 8:34 am, Phrogz <phr...@mac.com> wrote:
> Not always true:
>
> [...]

Point taken. But still should not be relied on.

> This discussion (using Hashes as hash or set keys, and the
> requirements and possible implementations of of eql? for this to work
> as sometimes desired) occurred in the last month or two on this list.

Thanks, found it. [ http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/572e8d8b01a7d24e/c5f532...
].

So it seems the only way to do it is something like...

class Set
def hash
to_a.sort.hash
end
def eql?(o)
return false unless o.is_a?(Set)
@hash == o.instance_eval{@hash}
end
end

....which is basically what Aaron was already doing.

Seems like there really should be a way to index keys on value rather
than identity (even if it's not the default or requires a separate
class like someone on the other thread mentioned that Smalltalk has).

Regards,
Jordan

Aaron Patterson

11/24/2007 6:53:00 PM

0

On Sat, Nov 24, 2007 at 11:35:05PM +0900, Phrogz wrote:
> On Nov 24, 4:35 am, MonkeeSage <MonkeeS...@gmail.com> wrote:
> > On Nov 24, 1:25 am, Aaron Patterson <aa...@tenderlovemaking.com>
> > wrote:
> >
> > > Nope. I don't want a shallow comparison. I need to use my sets as hash
> > > keys, so I need .eql? and .hash. Set calls eql? on the internal hash object.
> >
> > And eql? implies a.hash == b.hash. That is an object comparison, since
> > Object#hash gives a unique value for every object like Object#id. What
> > you need is shallow comparison of values rather than objects: a == b.
> > I'm not sure how to easily make Hash do that.
>
> Not always true:
>
> irb(main):001:0> a1 = [:a,1,'foo']
> => [:a, 1, "foo"]
> irb(main):002:0> a2 = [:a,1,'foo']
> => [:a, 1, "foo"]
> irb(main):003:0> a1.hash
> => 876036089
> irb(main):004:0> a2.hash
> => 876036089

Yes. I guess my point is this: given the behavior of eql? and hash on
Array, would you expect the same behavior from Set? I did, and found it
surprising when they didn't behave the same way.

Now, I can understand an argument against having the same behavior since Sets
are unordered. But if that is the case, then why even implement .eql?
and .hash on the Set class? Since those methods are implemented on Set,
it leads me to believe that the original intent was for .eql? and .hash
to behave the same way as on Array.

--
Aaron Patterson
http://tenderlovem...