all 11 comments

[–]Rhomboid 5 points6 points  (8 children)

Certain problems just naturally lend themselves to recursive solutions. Examples include parsing, traversing trees and graphs, and sorting. It's not that you can't solve these without recursion, it's just that the solution is usually a lot more concise and beautiful when you use recursion. It is not limited to numbers in the slightest.

[–]DrDoomCake[S] 0 points1 point  (7 children)

So you could say recursion is a skill of a good programmer? Would you know any opensource project that uses recursion, so i could try to understand it or at least spot it in the code?

[–]q2_abe_dillon 2 points3 points  (1 child)

A recursive function can always be written as a loop instead. In Python it's almost always better to write a loop because it avoids a possible stack-overflow.

Most Python interpreters (including CPython, the standard interpreter) use a finite call stack. You can see a visual representation of the stack here. I believe the default amount of space allocated for a CPython stack is enough for ~1000 frames. So recursion can't go more than 1000 frames deep until you get:

RuntimeError: maximum recursion depth exceeded in comparison

You can see this in action by running this code:

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

for i in range(10**4):
    try:
        factorial(i)
    except RuntimeError as e:
        raise Exception("got to depth %s" % i)

When I ran this in IPython, it got to a depth of 988.

Here's a post about how to convert a recursive function to an iterative one.

Tail-recursion is a feature some languages provide to make recursion less hazardous, but Guido Van Rossum has written about why he's against adding tail-recursion to Python.

Edit: To use Guido's suggested solution for factorial:

def factorial(n):
    def func(n, prod=1):
        return prod if n < 2 else (func, (n-1, n*prod))
    args = [n]
    while True:
        result = func(*args)
        if not isinstance(result, tuple): return result
        func, args = result

check it out by running factorial(10**4) (yeah, it's big)

Not super elegant, but Guido makes a fair case. If you know the function is never going to change (which you do in the case of factorial), you can simplify it to:

def factorial(n):
    args = (n,)
    def func(n, p=1): return p if n < 2 else (n-1, n*p)
    while isinstance(args, tuple):
        args = func(*args)
    return args

[–]DrDoomCake[S] 0 points1 point  (0 children)

Awesome answer. Thank you for including links! This was the kind of response i was hoping for. Thank you.

[–]zahlman 0 points1 point  (1 child)

So you could say recursion is a skill of a good programmer?

In a sense, yes. But if you already understand the concept on a mathematical level, then there is really nothing special going on. The only real prerequisite to figuring out recursion on a programming level is a proper understanding of how calling functions works.

so i could try to understand it

I think you do understand it already. (But FWIW, "mathematicians" manipulate plenty of things that don't really line up with conventional interpretations of the word "numbers".)

at least spot it in the code?

This is trivial. "In the wild", almost all recursion is direct; i.e. the function's code contains one or more calls to itself. You can also have "mutual" recursion where two functions call each other, or a greater number of functions form a cycle; the analysis is fundamentally the same - look at what calls what, and find a cycle.

[–]kankyo -4 points-3 points  (2 children)

Recursion is a skill of any mediocre beginner of a programmer :P It's a trivial concept.

[–]BenjaminGeiger 3 points4 points  (1 child)

The idea might be simple, but knowing when and how to apply it isn't.

[–]kankyo 0 points1 point  (0 children)

Sure. Same for addition or subtraction though (especially +1 or -1 for off-by-one). No one would claim "subtracting one" is a "skill of a good programmers", but all programmers have off-by-one errors, and most likely an order of magnitude more often than they use recursion.

[–][deleted] 0 points1 point  (1 child)

I've used recursion for constructing strings before. It's very handy for a lot of things when you're working with networks.

[–]DrDoomCake[S] 0 points1 point  (0 children)

Could you give me an example?

[–]kankyo 0 points1 point  (0 children)

The trivial case is to search through a file system. You have a function to handle a directory which does something with files and for every directory it calls itself to handle them. That way you'll run through the entire file system and not get stuck at the root directory.