From: "Shot (Piotr Szotkowski)" Date: 2010-06-12T02:41:22+09:00 Subject: Re: comparing objects --5vNYLRcllDrimb99 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Robert Klemme: > 2010/6/11 Shot (Piotr Szotkowski) : >> The difference is that in many, many cases it=E2=80=99s much faster to c= heck >> whether two objects are surely different (via a fast #hash function) >> than whether they=E2=80=99re surely the same (#eql? can be quite slow). > This is not necessarily true. Any reasonable implementation > of #eql? will bail out as soon as it sees a difference. Sure, reasonable implementations of #eql? will test object properties in the decreasing order of probability of a given property being different between two objects (and bail out as soon as possible), but there are cases where a fast #hash might be useful (partly immutable objects which cache the hash based on the immutable parts, perhaps?), exactly because it doesn=E2=80=99t have to reliably tell whether two objects are su= rely the same (just whether they surely differ). I agree I might=E2=80=99ve went over the top with the =E2=80=98many, many= =E2=80=99 remark, though (but then I did not say =E2=80=98most=E2=80=99, just =E2=80= =98many=E2=80=99=E2=80=A6). ;) > On the contrary, you always need to look at the > complete state of an instance to calculate #hash. This is definitely not true; you only should consider the parts that differentiate two objects of a given class most often, but you definitely do not =E2=80=98need=E2=80=99 to look at the complete state = (even a constant #hash is valid, albeit quite useless). The whole point of #hash is that it acts only as a hint whether two objects are =E2=80=98the same=E2=80=99 =E2=80=93 it=E2=80=99s your choice h= ow credible vs how performant it needs to be. At the same time, #eql? has to be 100% credible (although you=E2=80=99re right that it can take many of the same shortcuts a given #hash takes, and that there are cases where #hash can be slower than #eql?, as in your Array example, but it=E2=80=99s just because you *wa= nt* that #hash to depend on the complete state of an instance). > I can easily construct an example where #eql? beats #hash: [=E2=80=A6] > Notice also how #eql? with equal arrays is not much slower than #hash. Sure, because Array#hash is implemented in the way you describe (its hash depends on all of its elements). I was pointing out that there are cases where it doesn=E2=80=99t make sense to implement #hash like this, and having both #hash and #eql? gives you more control and more choices. >> class ImmutableString >> =C2=A0def initialize string >> =C2=A0 =C2=A0@string =3D string.dup.freeze >> =C2=A0 =C2=A0@length =3D string.length >> =C2=A0end >> =C2=A0def hash >> =C2=A0 =C2=A0@length >> =C2=A0end >> =C2=A0alias eql? =3D=3D >> end > Bad hash implementation. Why don't you use String#hash? Because String#hash depends on the contents of the string and is recomputed every time, while in this particular scenario (where the vast majority of very long strings differ in length) it might be faster to refer to the cached length. Of course with immutable strings you probably should just cache the hash, but I made the example immutable to not have to add that @length needs to be recomputed on mutations (I was also quite explicit that this is not an optimal example, just a simple one). Of course in this case a sane #eql? implementation would also bail out as soon as the lengths differ, but my point was that #hash doesn=E2=80=99t = have to be credible on whether two objects really differ, while #eql? has to, so in many cases #eql? has to start with checking all the properties that #hash value depends upon anyway (but Array#eql? and Array#hash are a good counterexample where such checks can bail out faster), plus it often should check the class of ther =E2=80=98other=E2=80=99 as well (which= is quick, but one more check nevertheless). > If the set of attributes to be used for the specific comparison needed > in this thread is not the same as the set that we identify as keyish > for class User in general one cannot use User#eql? and User#hash for > quick set intersection. Sure, but I assume it=E2=80=99s not a very common situation; I=E2=80=99d th= ink twice before I designed an object with different =E2=80=98equality=E2=80=99 seman= tics. On the other hand, crafting your own #=3D=3D, #hash and #eql? is quite common (at least I do it very often, because I often end up storing my objects in Sets). Note also that I was explicitely replying to the remark that it=E2=80=99s =E2=80=98odd that both [#hash and #eql?] are necessary=E2=80=99, not to the= OP. ;) =E2=80=94 Shot --=20 1986: Brad Cox and Tom Love create Objective-C, announcing =E2=80=98this language has all the memory safety of C combined with all the blazing speed of Smalltalk=E2=80=99. Modern historians suspect the two were dyslexi= c. [James Iry, A Brief, Incomplete, and Mostly Wrong History of Programming La= nguages] --5vNYLRcllDrimb99 Content-Type: application/pgp-signature; name="signature.asc" Content-Description: Digital signature Content-Disposition: inline -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkwSdT4ACgkQi/mCfdEo8UpE8QCgo+vDTEyVrTmgAcUmerCQ0t4G ZYgAnRz5jUbaIQdg7j89HQcyqNuf4w2P =+9Ou -----END PGP SIGNATURE----- --5vNYLRcllDrimb99--