-1

how to get rid of lots of function calls? Here is an example of the recursive function:

def factorial(n):
    if n <= 1:
      return 1
    else:
      return n * factorial(n - 1)

I heard you can do it with decorators easily but I don't know how to use them

emilchess
  • 117
  • 1
  • 3

2 Answers2

4

Assuming you have looked over your algorithm already carefully and eliminated any redundant calls, you could try to rewrite your function in an iterative way (ie using loops rather than recursion).

Recursion can often express a solution to a problem in a nice way, but it is rather memory hungry (saving state on the stack repeatedly) and not that speedy due to all the function calls. I see its main benefit in its expressive power.

Memoization is another option, so rather than re-computing (calling a function over), you first look up to see if you've previously computed (and stored) a value already and use it instead.

Levon
  • 138,105
  • 33
  • 200
  • 191
  • I agree that here it's better to use a loop instead of a recursion. But sometimes you want your code to look short and nice. Also it's quicklier to write a recursion instead of a loop. – emilchess Aug 24 '12 at 13:11
  • 1
    Levon: Recursion has it's place; a good divide-and-conquer algorithm is best coded as a recursion. – Martijn Pieters Aug 24 '12 at 13:15
  • @emilchess "quicker to write" doesn't always translate to "faster to execute" though :-) – Levon Aug 24 '12 at 13:15
  • @MartijnPieters I'm not against recursion, but just looking at the trade-offs. – Levon Aug 24 '12 at 13:16
0

You're looking for tail call optimization, which is basically a technique to convert recursive programs to iterative without rewriting them. For example, if you call your factorial function with n = 1000, Python fails complaining that "maximum recursion depth exceeded". However, when you rewrite the function to be tail-recursive:

def tail_factorial(n, result=1):
    if n <= 1:
        return result
    else:
        return fac(n - 1, result * n)

and then use a "trampoline" to call it:

def trampoline_factorial(n):

    def fac(n, result=1):
        if n <= 1:
            return result
        else:
            return lambda: fac(n - 1, result * n)

    f = fac(n)
    while callable(f):
        f = f()
    return f

you can evaluate 1000! without any problems.

Tail-call optimization can be indeed automated in Python using decorators, see e.g. here

georg
  • 211,518
  • 52
  • 313
  • 390