[Python-Dev] Comparison of recursive objects

Tim Peters tim.one@home.com
Sat, 20 Jan 2001 04:28:18 -0500


This is a multi-part message in MIME format.

------=_NextPart_000_0000_01C08299.69A67E20
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: 7bit

[Guido's checkin msg]
> ...
> In a discussion with Tim, where we discovered that our intuition
> on when a<=b should be true was failing, we decided to outlaw
> ordering comparisons on recursive objects.  (Once we have fixed our
> intuition and designed a matching algorithm that's practical and
> reasonable to implement, we can allow such orderings again.)

[Jeremy]
> Sounds sensible to me!  I was quite puzzled about what <= should
> return for recursive objects.

That's easy:  x <= y for recursive objects should return true if and only if
x < y or x == y return true <0.9 wink>.

x == y isn't a problem, although Python gives a remarkable answer:
recursive objects in Python are instances of rooted, ordered, directed,
finite, node-labeled graphs, and "x == y" in Python answers whether their
graphs are isomorphic.

Viewed that way (which is the correct way <0.5 wink>), the *natural* meaning
for "x <= y" is "y contains a subgraph isomorphic to x".  And that has
*almost* all the nice properties we like:

    x <= x is true
    (x <= y and y <= z) implies x <= z
    (x <= y and y <= x) if and only if x == y

However,

1. That's much harder to compute.
2. It implies, e.g., [2] <= [1, 2], and that's not what we *want*
   non-recursive sequence comparison to mean.
3. It's a partial ordering:  given arbitrary x and y, it may be that
   neither contains an isomorphic image of the other.
4. We've again given up on avoiding surprises in *simple* comparisons
   among builtin types, like (under current CVS):

>>> 1 < [1] < 0L < 1
1
>>> 1 < 1
0
>>>

   so it's hard to see why we should do any work at all to avoid
   violating "intuition" when comparing recursive objects:  we're
   already scrubbing the face of intuition with steel wool,
   setting it on fire, then putting it out with an axe <wink>.

Now let's look at Guido's example (or one of them, anyway):

>>> a = []
>>> a.append(a)
>>> a.append("x")
>>> b = []
>>> b.append(b)
>>> b.append("y")
>>> a
[[...], 'x']
>>> b
[[...], 'y']
>>>

I think it's a trick of *typography* that caused my first thought to be
"well, clearly, a < b".  That is, the *display* shows me two 2-element
lists, each with the same "blob" as the first element, and where a[1] is
obviously less than b[1].  Since "the blobs" are the same, the second
elements control the outcome.

But those "blobs" aren't really the same:  a[0] is a, and b[0] is b, so
asking whether a < b by looking first at their first elements just leads
back to the original question:  asking whether a[0] < b[0] is again asking
whether a < b, and that makes no progress.  Saying that a is less than b by
fiat is *consistent* with the rules for lexicographic ordering, but so is
insisting that a is greater than b.  There's no basis for picking one over
the other, and so no clear hope of coming up with a generally consistent
scheme.  Well, one clear hope:  if recursive comparison says "not equal", it
could resolve the dilemma by comparing object id instead.  That would be
consistent (I mostly think at the moment ...), but if you run the program
above multiple times it may say a < b on some runs and b < a on others.

WRT "the right way", it should be clear from the attached picture that
neither a nor b contains an isomorphic image of the other, so from that POV
they're not comparable (a != b, but neither a <= b nor b <= a holds).

So this is what Guido made Python do:

>>> a == b  # still cool:  they're not isomorphic and Python knows it
0
>>> a < b
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: can't order recursive values
>>> a <= b
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: can't order recursive values

In light of that, I still find these mildly surprising:

>>> a < a
0
>>> a <= a
1
>>>

I guess some recursive values are more orderable than others <wink -- but
that's true!  the ones Python can prove are equal are indeed "more
orderable">.

>>> import copy
>>> c = copy.deepcopy(a)
>>> c
[[...], 'x']
>>> a == c
1
>>> a <= c
1
>>> a < c
0
>>>

BTW, this kind of construction appears to give equality-testing that's at
best(!) exponential-time in the size of the dicts:

def timeeq(x, y):
    from time import clock
    import sys
    s = clock()
    result = x == y
    f = clock()
    print x, result, round(f-s, 1), "seconds"
    sys.stdout.flush()

d = {}
e = {}
timeeq(d, e)
d[0] = d
e[0] = e
timeeq(d, e)
d[1] = d
e[1] = e
timeeq(d, e)
d[2] = d
e[2] = e
timeeq(d, e)

Output:

{} 1 0.0 seconds
{0: {...}} 1 0.0 seconds
{1: {...}, 0: {...}} 1 6.5 seconds

After more than 15 minutes, the 3-element dict comparison still hasn't
completed (yikes!).

ackerman's-function-eat-your-heart-out-ly y'rs  - tim

------=_NextPart_000_0000_01C08299.69A67E20
Content-Type: image/jpeg;
	name="loopy.jpg"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
	filename="loopy.jpg"

/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcU
FhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgo
KCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAGsAdIDASIA
AhEBAxEB/8QAHAABAQEBAQEBAQEAAAAAAAAAAAYHCAUDBAIJ/8QASBAAAQIFAQMGCQgJBQADAQAA
AAECAwQFBhESBxMhFBYiMVZ1CBc3QZOVs9LUFTI2UVSktdMYI0JVYWZxluMkM4GRlCVEsaH/xAAb
AQEAAwEBAQEAAAAAAAAAAAAAAwQFBgIBB//EADkRAQACAQIDBAYIBQUBAAAAAAABAgMEEQUhQQYS
MVETYXGBkaEVIlJT0dLh8BQyQpLBFiNUscIz/9oADAMBAAIRAxEAPwDqkAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAEBTaFAuC6LyiVKerf8ApanCl4EOWrM3LQ4cPkMq/CMhRWtTpRHrnGVVynq8w6R9
suT+46j+eBVAleYdI+2XJ/cdR/PHMOkfbLk/uOo/ngVQJXmHSPtlyf3HUfzxzDpH2y5P7jqP54FU
CV5h0j7Zcn9x1H88cw6R9suT+46j+eBVAleYdI+2XJ/cdR/PHMOkfbLk/uOo/ngVQJXmHSPtlyf3
HUfzxzDpH2y5P7jqP54FUCV5h0j7Zcn9x1H88/Jaki2kX9cFNlpupRpJtMp8w2HO1CPN6Ij4s41y
tWM9ytykNmURcdFALUAAAAAAAAE1tEm52TttrqZORZGZjVCQlOUQmMc+GyNOQYT1aj2ubnS92MtU
+XNer9vLk9BTvhQKoErzXq/by5PQU74Uc16v28uT0FO+FAqgSvNer9vLk9BTvhRzXq/by5PQU74U
CqBK816v28uT0FO+FHNer9vLk9BTvhQKoErzXq/by5PQU74Uc16v28uT0FO+FAqgSvNer9vLk9BT
vhRzXq/by5PQU74UCqBK816v28uT0FO+FPEvimV6hWXcFXk75uB8zT6fMTcJsWXp6sc+HDc5EciS
qLjKJnCoBooAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlbM+kV+d9Q/w6SKolbM+kV+d9Q/w6SK
oAAAAAAAAAAAAAAErTfKncXctM9vPlUStN8qdxdy0z28+BVAAAAAAAAldpf0dk++qT+Iy5VErtL+
jsn31SfxGXKoAAAAAAAAAAAAAAErtZ8ll5dyznsHlUSu1nyWXl3LOeweBVAAAAAAAAAAAAAAAAAA
AAAAAAA5AuC7bv2keEDM2bTbqqVtU2DOzUnL8ieqaNzDdqc7QsNX63QVVEc5dOtURfroPBE2lXDc
VbqdtXFPzNUhQZLlktMTMRHxIWmKiParlTU/VvW8XOXSjEREwvAOnwAAAAEXKbS7embyjW0kaNCn
YcR0BIsViNhRIqLhYbVznVnKJlERVTCKuUzaGRbVNkcKvR5msW65kvU3NV8WVVESHNPz1oucMcqZ
yvU5cZx0nLPWHtaqNBmn0faBCnV0aGsjxIOmPA4NTEVq4VzdPS1cXdfzspijGptivNM8bRPhPR1N
uCYNfpo1HCrd61Yjv0n+bfzjzj9xz5RqlmfSK/O+of4dJFURWzapSdXqN6T9NmGTEnHq8J8OIzqV
Pk+T/wCUVFyiovFFRUXiWpdiYmN4cxatqWmto2mAAH15AAAAAAAAAAAJWm+VO4u5aZ7efKolab5U
7i7lpnt58CqAAAAAACPqW0Sh0+9YFsTCzXL4rmQ1iNhZhMe9MsYq5zlct4oip0kyqcceL3rTnadk
+n0ubUzNcNZtMRMzt5R4y+20v6OyffVJ/EZcqiV2l/R2T76pP4jLlUe0AAAAAAAAAAAAAAErtZ8l
l5dyznsHlUSu1nyWXl3LOeweBVAAAAAAAAAAAAAAAAAAAAAAAA41uCt3Dta28zNgVmuzMjbbanNS
vJZJqMasKBqd0k/bc7cIqK/UjXOVUTHRPy+BR5U6r3LF9vAN6u/wf7Jum7nXBPQp6XjxntiTMtKR
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
WpcOLCisRzIjFhy6K1yLwVFRVRUUbKJaBJ+F9GlZODCgS0CrVSHChQmI1kNjYcwiNaicEREREREP
jtinfk7ws1nuTTM3yap0yNyeVh640XTDgLoY39py4wiedVQ9DYdJVG4fCgqVfk6XPQZGWqE/NTaT
MPdvlEipGaxkVFXhE1ORNKKq8HeZqqgdZXpdVOtCjfKVW3zoSxGwWMgs1Pe9crhMqidSOXiqdX14
RYPx82x9grXoYX5ho9w0KmXFTXSFalGTUqrkfocqorXJ1KjkVFRetMovUqp1KpLeKOyP3J97j++V
c1dRNv8AamNvW3uG5eD0w7a6l5vv/Tttt8YeD4+bY+wVr0ML8wePm2PsFa9DC/MPe8UdkfuT73H9
8eKOyP3J97j++Rd3Wfar+/cv+n7N/dZfl+Z4Pj5tj7BWvQwvzDHr8uKkz95Q7itNs7JzLojZiIyY
gQ2tZGaqKj26XORcqmVRydeVyurCbDe9kbPrStyZq05Qt7u8NhQUnozXRoirhGoqv/qq4yqIirhc
GebENn0C6pqZqVbg72jS+YKMSKrFixsIuF08dKNdnrTiretNSFPURqMlow2mJmefLo6Lg9+EaTBl
4lgpetK/VnvbfW36RG879PL/ALaXsNujnO+6pqPB3M7HnYM1Gaz/AG0zLQoPRVVzxWWc7C9SORMr
xU1Ei9nEhK0qfvKn06C2BJStWhQ4MJucMb8nya+f61VVVetVVVXiqloa+Kt60iLzvL871+TBl1F7
6WncpPhEzvt+/l4AAJFQAAAAAAAAAAAx7aFfbrD2lTcdaak+yoUiTYico3SsWFGm1X9l2c71P+jY
SGfRqZVtqdd+VabJT26otN3fKYDYujMeezjUi4zhP+kI8tb2rtjnaV3h+XTYs8W1ePv058onb2eC
B/SD/ln7/wD4x+kH/LP3/wDxmtczbY7OUX/wwvdHM22OzlF/8ML3Sp6HV/eR8I/B0H0j2f8A+Hb+
+35mS/pB/wAs/f8A/GP0g/5Z+/8A+M1rmbbHZyi/+GF7o5m2x2cov/hhe6PQ6v7yPhH4H0j2f/4d
v77fmZDMbfYUzAiQJi1GRYMVqsfDfPI5r2qmFRUWFhUVPMZTcdYp05WUqVv0yNRIu83yshzWtkN/
BUWFhjVZhUVetetMaUTB1pzNtjs5Rf8AwwvdMh251G3aDAbQ6DRaE2px2qszFZJw95KsVEwidHCO
cirxzlqJnHSaqVdVhyxTvZbxtHqbvAOJ8Ptqow8P01q2t4/XmY26zMTMxy9nqjmkou1msTliVO26
rBhTizkm6SbOucqRWsejmvV/Wj3aXYReHFMu1cTouxKzNXDaNMqs/KckmZmHqfCRFROtURyZ46XI
iOTr4OTivWuPSmzWVoGx66azWILY1bi0SZjwmxYapyL9S5zUajkykRFRFV3WiphOpVdvxb0ePNEd
7Lbp4Oe7SavhmTJOLQYtpi282jwnptEeW/s8OXiAAvOWAAB59w0mBXaBU6ROPislqhKxZSK6EqI9
rIjFaqtVUVM4VcZRT/PCgQJW5KVZtqMnNxOzNwTDIrt0rtzDmGycNj/MjuMOJwRc9HjjKKd/39K3
DO2nPy9lz8tT6+/d8mmZlqOhsxEar8orH9bEcnzV4qnV1mC7O/B8uOU2owLsvuqUidbDmn1F7ZN0
TXGmldqa5U0Q0aiPXXwynRRunCrgOmgAAAAAAASuybyWWb3LJewYVRK7JvJZZvcsl7BhVAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAGCbZKDdt23/KUyWpz1pUJqJKTO7xBaj2osR8SImcLlqpheOGtw1VX
pbZQaVK0OjSdMkGaZaVhpDZlERXY63LhERXKuVVccVVVP3k9f9yQrUtWeqj1YsZjdEvDdj9ZFdwa
mMoqpniqIudKOXzFauKuG18sz4trNxDPxLFp+H46xEV5REdZnrPr/GZ6vJsyrU7nhesn8oSnLI9Z
asKBvm7yIjZCVa5WtzlcOhvRcdSscnmUtzljYVCnKltUk5xyvmHwmx5mZivfl2HMc3UqquXKr3t+
teOfrOpz5pM86ik3mNub1x/hNOE6munpfvfViZ9szPL5b+8ABaYYAAAAAAAAAABK03yp3F3LTPbz
5VErTfKncXctM9vPgVQAAAADwr7lqvN2jU4FtxtzVnw8QXo7QvWmpEd5nK3UiLwwqouU60zvZRsu
j06fdcF4t31YWIsSDAiREi7t+crFe5FVHPVeKcVx1/O+bsIIL6el7xkt0+DT03FtRpdLfSYdoi88
52+tt5b+X76yldrPksvLuWc9g8qiV2s+Sy8u5Zz2DyqJ2YAAAAAAAAAAAAAAAAldk3kss3uWS9gw
qiV2TeSyze5ZL2DCqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABh/hO1XTIUWkMfBXeRHzcVuf1jdKa
WLjPBq64nWnFW8OpTcDmrwl/p1Id2w/axSlxC01wTt1dR2Pw1y8VpNv6Ymfl+u6u8F2FAdbFemWw
tMz8o8mfE1KutjYMN7eHUmFiv/7/AKY2chtltP8AkiLdVN3u+5HUZaX3mnTr0UyRbnGVxnHVkuSx
gx+jx1p5MbimrnW6zLqJnfvTO3s6fCNoAASqAAAAAAAAAAABK03yp3F3LTPbz5VErTfKncXctM9v
PgVQAAAAAAAJXaz5LLy7lnPYPKoldrPksvLuWc9g8qgAAAAAAAAAAAAAAAAJXZN5LLN7lkvYMKol
dk3kss3uWS9gwqgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc3eExBitvOmx3Q3pBfT2sbEVq6XObEi
KqIvUqojm5T+KfWdIma7e7Y+XbNdPwG5naTqmG8fnQsfrU4qidSI7PFehhOsqa7HOTDMR7XQ9ltZ
TR8Tx2yeFvq/Hw+exsLrESv0u4qpH3qxZipQt4+IxG64jJCUhvciJwwr2Oxj/pOo0o5I2RTVrQLj
iy950ikTkpOMbDhzM/JsjbiIirp6Ts6GLqdlcdelVwiKp0b4uLI7G236rge6fdHnjNjieseKPtFw
q3DNbakR9S3Ovlt5e7wVQJXxcWR2Ntv1XA90eLiyOxtt+q4HulphKoEr4uLI7G236rge6PFxZHY2
2/VcD3QKoGSbVaBa1o2qtTpdgWvNRt+yEqxKVCWHBauem5GtRcZRG9acXJ/RcapNiVu96g2p0m2q
bJS6u0Mjy8nCk4ENqucnRwiK9G9JFVNTsIiLngVM2rjHbuVrM28nQcM4BfW4f4rLlrjxRO28z19n
4zDoi4dqlpURjtVUZPRtKPbCkP1yuRVx85F0IqcVwrkXH9UzkVzbcq9UN2yhy8GkQ0wrncJiI5eO
Uy5ulG8U4ac5Tr44PBrtBte09/LT9TjVyuwtKLKSjN1LQYqY1MixF6T28cdDS7oqi6VXKS0vLTlx
VyHLUyRYs1MuSHBlpZmlrURMIiZ8yInFzlVeCucqrlTLz6zPae7vtPlH4u84R2c4Vir6eaTesc+9
flHurO3L1zHsnZumzHa7U7lumWo1Vp0knKtW7jSyuZu9LHvXLXK7VnSidaY49ZtByjsmo0i/aYyh
3RR5afzv5d8vM6YkOFFYiqqq3i1+NDkwvDKo5OKIdCeLiyOxtt+q4Humjw/JfJimbzvO7jO12j02
k1ta6WndrNYnl4TznnHOfL9+M1RK03yp3F3LTPbz48XFkdjbb9VwPdPVoVt0O39/8g0am0zf6d7y
KVZB3mnOnVpRM4yuM9WVLzlnqgAAAAAAAldrPksvLuWc9g8qjz7hpMCu0Cp0icfFZLVCViykV0JU
R7WRGK1VaqoqZwq4yini816v28uT0FO+FAqgSvNer9vLk9BTvhRzXq/by5PQU74UCqBK816v28uT
0FO+FHNer9vLk9BTvhQKoErzXq/by5PQU74Uc16v28uT0FO+FAqgSvNer9vLk9BTvhRzXq/by5PQ
U74UCqBK816v28uT0FO+FHNer9vLk9BTvhQKoErzXq/by5PQU74Uc16v28uT0FO+FAbJvJZZvcsl
7BhVHn29SYFBoFMpEm+K+Wp8rClIToqor3MhsRqK5UREzhEzhEPQAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAA5q2mbIajSZ+LOWtKRp6kv6W4h9ONLqqomnT857ePBUyqIi6urUvg2TtSuG191A3/wAo
U1mG8lmlV2lqaUwx/W3DW4ROLUyq6VOsyTvHZ7b12aolSk91Or/9yWVIcb9nrXCo7g1E6SLhM4wZ
mTQWrbv6e20+TudF2sxZ8UaXi+P0lftdffHn64mJ9svMtHaxbNfgIkxOMpU41uXwZ16Mb1JnTEXo
uTK4TqcuFXSiFFzytjtHRf8A3QveMl/R8/mb7h/kH6Pn8zfcP8h6rl1kRtNIn3x+KDNoezl7zbHq
rVjy7tp/8ta55Wx2jov/ALoXvHmVPabZ1NjtgzFelXvc3Wiy6OjtxlU+dDRyIvDqznq+tDOP0fP5
m+4f5B+j5/M33D/IfZy6zpjj4/q804f2difr6u0x6qzH/mXzuHb7FV7mW5R2NYjkVI0+5VVzccU3
bFTC58+teCdXHhnFbuy672n3ysWZnZrlGUbT5Nrt2rUVXoiQ2/O09eVyuGpleBvFE2LWpTJ9k1Fb
O1DRhWwpyK10NHIqKiqjWt1dWMLlFRVyimhyMlK0+VZKyEtBlZaHnRCgw0YxuVyuETgnFVX/AJIv
4XUZv/tfaPKF2OP8H4bt9H6bvW+1b9d5+GznG1tiFenZqC+vug02SSJiKxIqRI7mImcs05bx6sqv
DiuF4Iu8WhadItKQdK0WW3W80rGivcrokZyJhFcq/wDK4TCIqrhEyp7oLmDSYsHOsc/NzvFO0Ot4
p9XNbav2Y5R+vvYvbOz6vSG2mbr0zBgtpPKZmZZHSKi60io9GtRvztSbzjlETorhV4Z2gAkw4a4Y
mK9Z3VOI8SzcRvS+bbetYrG3lG/z5gAJWeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAP/9k=

------=_NextPart_000_0000_01C08299.69A67E20--