Give google a day or two and <a href="http://lists.lrug.org/pipermail/chat-lrug.org/2008-May/002439.html">http://lists.lrug.org/pipermail/chat-lrug.org/2008-May/002439.html</a> should start appearing if you search for it. <br>
<br>Admittedly the lrug mailing list archive is likely to have little or no google juice, so that page is not likely to be up the rankings, but I thought I'd just point out that these messages don't disappear into the non-indexed ether of our inboxes.<br>
<br>Muz<br><br><div class="gmail_quote">2008/5/7 Daniel Tenner <<a href="mailto:daniel.ruby@tenner.org">daniel.ruby@tenner.org</a>>:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Agreed.<br>
<br>
This is quite blog-worthy...anyone willing to post it up for posterity/non-londoners? :-)<br><font color="#888888">
<br>
Daniel</font><div><div></div><div class="Wj3C7c"><br>
<br>
On 7 May 2008, at 15:117 May 2008, James Adam wrote:<br>
<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
Really nice post Matthew - it's good to know the quirks of our<br>
rarely-fickle mistress :)<br>
<br>
On Wed, May 7, 2008 at 2:50 PM, Matthew Willson <<a href="mailto:matthew@playlouder.com" target="_blank">matthew@playlouder.com</a>> wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
Thought this might amuse some on here (or be useful to know - it's been the<br>
source of a couple of hard-to-track-down bugs in the past).<br>
<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
{{} => true}[{}]<br>
</blockquote></blockquote>
=> nil<br>
<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
{{} => true, {} => false}<br>
</blockquote></blockquote>
=> {{}=>true, {}=>false}<br>
<br>
but yet,<br>
<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
{} == {}<br>
</blockquote></blockquote>
=> true<br>
<br>
What's going on here?<br>
<br>
Ruby's Hashes behave very strangely when you try to use a Hash itself as a<br>
key of a Hash.<br>
<br>
This acts as a subtle gotcha when you try to memoize a function which takes<br>
hash arguments - and so a tricky-to-address bug in libraries like this:<br>
<a href="http://raa.ruby-lang.org/project/memoize/" target="_blank">http://raa.ruby-lang.org/project/memoize/</a><br>
<br>
<br>
Why?<br>
<br>
Ruby calls Object#hash on each key of a Hash, using that numeric hash<br>
(small h) to allocate that object to a bucket of the hash table data<br>
structure. Equality, when it comes to Hash lookups and unique keys of a<br>
Hash, will only work if the keys generate the same numeric hash as a result<br>
of their hash methods.<br>
<br>
For most ruby data structures, x.hash == y.hash is implied by x == y,<br>
and everything works fine.<br>
But, not for Hashes themselves!<br>
<br>
(NB. this also affects data structures like Arrays which themselves contain<br>
a Hash, since Array#hash must call hash recursively on its contents).<br>
<br>
(Interestingly, for things like 1.0 == 1, x.hash == y.hash also fails.<br>
Note, x.hash == y.hash is always implied by x.eql?(y), but this equality<br>
isn't a desperately useful one, and seems to have been constructed<br>
artificially as an equality for use with Hash which is consistent with<br>
.hash)<br>
<br>
<br>
<br>
Why might it have been implemented this way?<br>
<br>
Hashes are insensitive to the order of their keys - so, for example, we<br>
have:<br>
{:a => true, :b => true} == {:b => true, :a => true}<br>
<br>
When you're actually being given two concrete objects to compare, you can<br>
just check that each key from the one has an equal corresponding value in<br>
the other, and vice versa.<br>
<br>
But, when you're asked to generate a numeric hash which uniquely identifies<br>
the equivalence class, you'd have to do something to ensure the hashing<br>
isn't order-sensitive. Like ordering the key/value pairs by their individual<br>
hashes before feeding into the hash function.<br>
<br>
<br>
Some attempts at an answer in the form of an overridden Hash#hash:<br>
<br>
(1) sort key/value pairs by the numeric hash of the pair first:<br>
<br>
class Hash<br>
def hash<br>
sort_by {|pair| pair.hash}.hash<br>
end<br>
<br>
def eql?(other)<br>
self == other # hash is now consistent with ==, so we need to make<br>
eql? consistent too<br>
end<br>
end<br>
<br>
(2) Use an XOR of the hashes of the key/value pairs (XOR is<br>
order-insensitive)<br>
<br>
class Hash<br>
def hash<br>
inject(0) {|hash,pair| hash ^ pair.hash}<br>
end<br>
<br>
def eql?(other)<br>
self == other<br>
end<br>
end<br>
<br>
These then fix, eg:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
{}.hash == {}.hash<br>
</blockquote></blockquote>
=> true<br>
<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
{{} => true}[{}]<br>
</blockquote></blockquote>
=> true<br>
<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
{{} => true, {} => false}<br>
</blockquote></blockquote>
=> {{}=>false}<br>
<br>
(Note, overriding eql? is required to make the last two work)<br>
<br>
Now, I'm sure there's a reason Matz didn't do it this way - perhaps a<br>
performance reason, perhaps a gotcha that I haven't noticed with my<br>
approach. Perhaps it'll be fixed in 1.9.<br>
But at any rate, it's useful to be aware of the issue.<br>
<br>
-Matt<br>
<br>
_______________________________________________<br>
Chat mailing list<br>
<a href="mailto:Chat@lists.lrug.org" target="_blank">Chat@lists.lrug.org</a><br>
<a href="http://lists.lrug.org/listinfo.cgi/chat-lrug.org" target="_blank">http://lists.lrug.org/listinfo.cgi/chat-lrug.org</a><br>
<br>
</blockquote>
<br>
<br>
<br>
-- <br>
* J *<br>
~<br>
_______________________________________________<br>
Chat mailing list<br>
<a href="mailto:Chat@lists.lrug.org" target="_blank">Chat@lists.lrug.org</a><br>
<a href="http://lists.lrug.org/listinfo.cgi/chat-lrug.org" target="_blank">http://lists.lrug.org/listinfo.cgi/chat-lrug.org</a><br>
</blockquote>
<br>
_______________________________________________<br>
Chat mailing list<br>
<a href="mailto:Chat@lists.lrug.org" target="_blank">Chat@lists.lrug.org</a><br>
<a href="http://lists.lrug.org/listinfo.cgi/chat-lrug.org" target="_blank">http://lists.lrug.org/listinfo.cgi/chat-lrug.org</a><br>
</div></div></blockquote></div><br>