[Ruby] eql? and hash on Sets
Aaron Patterson
aaron at tenderlovemaking.com
Sun Nov 25 11:27:35 PST 2007
On Sat, Nov 24, 2007 at 06:09:10PM -0800, Ryan Davis wrote:
>
> On Nov 24, 2007, at 10:35 , Aaron Patterson wrote:
>
> > Given the behavior of Array#hash and Array#eql?, what would you expect
> > Set#hash and Set#eql? to do? I expected Set to behave the same way as
> > Array, but it does not.
>
> I would expect set to behave more like Hash than Array... or at least,
> I would expect it to act like some genetic freak merge of the two.
>
> > However, I was surprised to find that Set
> > implements eql? and hash. That leads me to believe that the original
> > intent was for Set to behave like Array in that department, and that
> > this is a bug.
>
> Why would the implementation of #eql? and #hash lead you to believe
> that it'd act like an Array? I don't see how that follows.
I guess it didn't really. Set just seemed to quack like an Array most
of the time. And since .eql? and .hash were implemented, I expected
them to sort of act like Array as well.
>
> That said, it seems wrong that Set#eql? is defined by Hash#eql? (which
> defaults to Object#eql? and is therefor the same as Object#equal?--
> object identity). So yes, it seems like a bug, sorta.
Agreed.
>
> > As far as I can tell, no two Sets can eql? each other.
>
> No two different sets...
>
> > % irb
> > >> require 'set'
> > => true
> > >> s = Set.new [1, 2, 3]
> > => #<Set: {1, 2, 3}>
> > >> s2 = s
> > => #<Set: {1, 2, 3}>
> > >> s.eql? s2
> > => true
>
> The real question is, how exactly are you abusing sets that makes you
> poke into this area? Using them as hash keys?
I'm using them in CSSPool. I need to keep track of properties for each
rule. Each rule has a selector and many properties. I don't really
care what order the properties are in, but I do care that they are
unique. So Set seemed appropriate. Then I wanted to reduce my CSS, and
group all rules by property. So if many rules had the same properties,
I could reduce the size of the CSS.
Here's a short example:
h1 {
background: red;
}
div {
background: red;
}
Could be re-written as:
h1, div {
background: red;
}
Here is what I made Set do:
class Set
def eql?(o)
return false unless o.is_a?(Set)
@hash.keys.sort_by { |x| x.hash }.
eql?(o.instance_eval{@hash}.keys.sort_by { |x| x.hash })
end
def hash
@hash.keys.sort_by { |x| x.hash }.hash
end
end
I don't particularly care what order the set is in, so I sorted by hash
and eql?'d on that. It could probably be sped up by comparing lengths
of the keys, but I didn't really care.....
--
Aaron Patterson
http://tenderlovemaking.com/
More information about the Ruby
mailing list