6

A few days ago someone said to me that recursion would be better then iteration and should, if possible, always be used.

So, I dove into recursion and tried writing a simple program to get the factorial of a number. This is the recursion:

def fact(n):
    if n == 1:
        return 1
    return n * fact(n - 1)

and although this works fine, it gets a RuntimeError: maximum recursion depth exceeded as soon as n gets above 997.

So I wrote a simple function that does exactly the same, but with a for loop.

def fact(n):
    a = 1
    for x in range (0, n, 1):
        a = a * (n - x)
    return a

while n < 10000 it gives the answer within 150ms.

So, I though maybe recursion is faster with low numbers, but nope. It takes longer: timing of recursion and iteration

So my question is:
Is there any reason at all to use recursion when writing programs in Python?
and:
Are there any problems that can only be solved with recursion?

Nathan
  • 900
  • 2
  • 10
  • 28
  • 1
    See http://stackoverflow.com/a/13592002/98057 (for context on tail recursion in Python) – André Laszlo Apr 01 '16 at 09:28
  • @AndréLaszlo that doesn't answer the question if there are any problems that can only be solved with recursion though. – Nathan Apr 01 '16 at 09:30
  • 2
    All recursive algorithms can be rewritten iteratively, http://stackoverflow.com/questions/931762/can-every-recursion-be-converted-into-iteration but for certain problems writing it recursively is a lot easier – Padraic Cunningham Apr 01 '16 at 09:31
  • That's correct. If I had a good answer I would post an anser instead of a comment :) I didn't vote to close, btw. Upvote for interesting question. Added clarification. – André Laszlo Apr 01 '16 at 09:31
  • You're using Python 2. You do realize that `range` in Python 2 creates a list of `n` items. Use `xrange` instead; or preferably switch to Python 3. And function calls in Python *are* expensive compared to iteration. – Antti Haapala -- Слава Україні Apr 01 '16 at 09:38
  • "someone said to me that recursion would be better then iteration and should, if possible, always be used". If that person was talking about Python, they are very much mistaken. Recursion should be avoided in Python except where it's natural for the problem domain (eg processing recursive data structures, like trees) where an equivalent iterative algorithm ultimately has to perform the recursion "by hand", building its own explicit stack. – PM 2Ring Apr 01 '16 at 09:51
  • [Tail recursion is its own reward.](http://www.xkcd.com/1270/) – Joseph Farah Apr 01 '16 at 10:00

3 Answers3

7

There are no problems that need to be solved by explicit recursion; and there are no problems that need to be solved by explicit iteration - Church and Turing have proved that they're interchangeable.

However there are problems that have a neater code, and are easier to reason about if you use recursion. Indeed, Python standard library and even the C code itself uses recursion internally in many places and so is prone to hitting the dreaded recursion limit in many places, for example printing the repr of nested tuples:

>>> from functools import reduce
>>> x = reduce(lambda x, y: (x,), range(10000))
>>> x  # recursion limit hit in C code
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: maximum recursion depth exceeded while getting the repr of a tuple
>>> import pprint
>>> pprint.pprint(x)  # recursion limit hit in Python standard library code.
Traceback (most recent call last):
...
  File "/usr/lib/python3.4/pprint.py", line 390, in _safe_repr
    orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level)
  File "/usr/lib/python3.4/pprint.py", line 340, in _safe_repr
    if issubclass(typ, dict) and r is dict.__repr__:
RuntimeError: maximum recursion depth exceeded while calling a Python object

You can always convert a recursive algorithm to an iteration with a state machine and stack of intermediate arguments, because after all that's pretty what a CPU does to implement recursion.

2

I can't tell which is faster, but recursion definitely has its place in programming. For example, consider the following function, which is supposed to "flatten" nested lists, into one great big list:

def expand_list(lst):
    result = []
    for element in lst:
        if isinstance(element, list):
            result += expand_list(element)
        else:
            result.append(element)
    return result

(E.g. expand_list([1,2,3,[4,5,[6,7]]]) evaluates to [1,2,3,4,5,6,7])

Iteration wouldn't be able to handle this very well.

(Side note: there are functions in Python to do the same as above function - this was just an example.)

acdr
  • 4,538
  • 2
  • 19
  • 45
  • It's not that bad: `stack = lst[::-1]` `while stack:` `element = stack.pop()` `if isinstance(element, list): stack.extend(element[::-1])` `else: yield element`. – Veedrac Apr 01 '16 at 10:02
  • FWIW, your code is simpler with `if isinstance(element, list): yield from expand_list(element)` `else: yield element`. – Veedrac Apr 01 '16 at 10:03
1

Iteration is generally (and also in this case) more effective (performance wise, assuming you're performing similar operations). Recursion has more overhead since you have to make a new function call every time which costs resources.

https://en.wikipedia.org/wiki/Recursion_(computer_science) says

"The power of recursion evidently lies in the possibility of defining an infinite set of objects by a finite statement"

but the use case would have to be very specific for recursion to be the better choice.

About the error, Python has a built in function which returns what the maximum recursion depth is, should you find this useful: https://docs.python.org/2/library/sys.html#sys.getrecursionlimit

Lars de Bruijn
  • 1,430
  • 10
  • 15
  • Is there any case where recursion is more effective, even with the overhead? – Nathan Apr 01 '16 at 09:34
  • 1
    There are many problems which by their nature are recursive, which often means that a recursive algorithm will be natural and obvious. If you measure effectiveness only in execution time, it'd be hard to find a case where recursion wins, but you should be measuring cost vs benefit in terms of programmer time, complexity, etc. – tripleee Apr 01 '16 at 09:36
  • @DavidWestern Here is an interesting thread with a lot of examples. Maybe this will get you a more specific answer: http://stackoverflow.com/questions/3021/what-is-recursion-and-when-should-i-use-it – Lars de Bruijn Apr 01 '16 at 09:38
  • 1
    I strongly disagree with "Iteration is generally more effective". This is not a generalization you can make. Even in the absence of TCO, recursion (well, a function) still gives you scope control that you don't get from loops in most languages. The overhead of a function call is well worth the consideration to contain the scope of variables. Python 2 scope leaks even in list comprehensions. – kojiro Apr 01 '16 at 13:17
  • @kojiro I ment effective in the context of performance. I should have been more clear on that matter. Thank you for your addition. – Lars de Bruijn Apr 01 '16 at 13:35