Tail recursion to while iteration in 2 easy steps

Terry Reedy tjreedy at udel.edu
Thu Oct 3 15:04:21 EDT 2013


On 10/2/2013 10:34 PM, Steven D'Aprano wrote:

> You are both assuming that LOAD_CONST will re-use the same tuple
> (1, 2, 3) in multiple places.

No I did not. To save tuple creation time, a pre-compiled tuple is 
reused when its display expression is re-executed. If I had been 
interested in multiple occurrences of the same display, I would have tested.

 >>> def f():
	a = 1,'a',3333, 'bbb'; x = 1,'a',3333, 'bbb'
	b = 1,'a',3333, 'bbb'
	c = 'a'
	d = 3333 + 3333
	
 >>> f.__code__.co_consts
(None, 1, 'a', 3333, 'bbb', (1, 'a', 3333, 'bbb'), (1, 'a', 3333, 
'bbb'), (1, 'a', 3333, 'bbb'), 6666)

Empirically, ints and strings are checked for prior occurrence in 
co_consts before being added. I suspect None is too, but will not assume.

How is the issue of multiple occurrences of constants relevant to my 
topic statement? Let me quote it, with misspellings corrected.

"CPython core developers have been very conservative about what
transformations they put into the compiler." [misspellings corrected]

Aha! Your example and that above reinforce this statement. Equal tuples 
are not necessarily identical and cannot necessarily be substituted for 
each other in all code.

 >>> (1, 2) == (1.0, 2.0)
True

But replacing (1.0, 2.0) with (1, 2), by only storing the latter, would 
not be valid without some possibly tricky context analysis. The same is 
true for equal numbers, and the optimizer pays attention.

 >>> def g():
	a = 1
	b = 1.0

 >>> g.__code__.co_consts
(None, 1, 1.0)

For numbers, the proper check is relatively easy:

for item in const_list:
   if type(x) is type(item) and x == item:
     break  # identical item already in working list
else:
   const_list.append(x)

Writing a valid recursive function to do the same for tuples, and 
proving its validity to enough other core developers to make it 
accepted, is much harder and hardly seems worthwhile.

It would probably be easier to compare the parsed AST subtrees for the 
displays rather than the objects created from them.

---
 > py> def f():
 > ...     a = (1, 2, 3)
 > ...     b = (1, 2, 3)
[snip]
 > So even though both a and b are created by the same LOAD_CONST 
byte-code,

I am not sure what you mean by 'created'. LOAD_CONST puts the address of 
an object in co_consts on the top of the virtual machine stack.

 > the object is not re-used (although it could be!)

It can be reused, in this case, because the constant displays are 
identical, as defined above.

 > and two distinct tuples are created.

Because it is not easy to make the compiler see that only one is needed.

-- 
Terry Jan Reedy




More information about the Python-list mailing list