New tail recursion decorator

Duncan Booth duncan.booth at invalid.invalid
Wed May 10 09:00:41 EDT 2006


Kay Schluehr wrote:

> 
> Diez B. Roggisch wrote:
>> Kay Schluehr wrote:
>>
>> > http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691
>>
>> Neat.
>>
>> Diez
> 
> Hi Diez,
> 
> for all those who already copied and pasted the original solution and
> played with it I apologize for radical changes in the latest version (
> the recipe is on version 1.5 now ! ). The latest implementation is
> again a lot faster than the previous one. It does not only get rid of
> exceptions but also of stack-frame inspection. 
> 
> Regards,
> Kay
> 
I'm not convinced by this. You have to recognise that the function is using 
tail recursion, and then you have to modify the code to know that it is 
using tail recursion. This is not always trivial. For example, the given 
example is:

@tail_recursion
def factorial(n, acc=1):
    "calculate a factorial"
    if n == 0:
       return acc
    res = factorial(n-1, n*acc)
    return res

but a more common way to write the function would be:

@tail_recursion
def factorial(n):
    "calculate a factorial"
    if n == 0:
       return 1
    return n * factorial(n-1)

which won't work because it isn't actually tail recursion, but it looks 
sufficiently close to tail recursion that it would probably mislead a lot 
of people into expecting it will work. If you are going to have to rewrite 
functions in a stilted manner, and they use simple tail recursion, then why 
not just factor out the tail recursion in the first place.

My other problem with this is that the decorator is very fragile although 
this may be fixable. e.g. (using the published example) an exception 
thrown inside the function makes future calls return garbage:

>>> factorial(3)
6
>>> factorial('a')

Traceback (most recent call last):
  File "<pyshell#5>", line 1, in -toplevel-
    factorial('a')
  File "<pyshell#1>", line 12, in result
    tc = g(*args,**kwd)
  File "<pyshell#3>", line 6, in factorial
    res = factorial(n-1, n*acc)
TypeError: unsupported operand type(s) for -: 'str' and 'int'
>>> factorial(3)
('continue', (3,), {})




More information about the Python-list mailing list