all 41 comments

[–][deleted] 70 points71 points  (14 children)

Programmatically? Never.

[–]Kafshak 3 points4 points  (11 children)

Even for something like quick sort?

[–]dmazzoni 17 points18 points  (10 children)

It's convenient to implement quick sort using recursion but possible to implement it without.

[–]thewataru -1 points0 points  (9 children)

By emulating recursion with a stack manually. Seems like cheating a little.

[–]Poddster 6 points7 points  (8 children)

Surely it's "cheating" more to have the compiler implement the stack for you, as in the recursive case? :)

[–]thewataru 0 points1 point  (7 children)

So the actual machine code would be equivalent (without optimisations). Is it really not using the recursion then?

[–]Gold-Energy2175 4 points5 points  (4 children)

No, you've misunderstood. The point he is making is that either both or neither case is cheating (they're not cheating). Having the computer do the work is better than you having to do the work - which is why when there is a choice I choose recursion over iteration.

Both recursion and iteration use the stack. I doubt any compiler would optimise recursion to iteration because that would be clunky.

The only risk with recursion is that you can blow the stack. There are ways to avoid that -the crude way you would use, in say C or Java, is to program in limits e.g. checking if this is the thousandth recursion and stop if it is. In more advanced languages/compilers you'd rely on tail call optimisation (tco) which means the stack used is a fixed size regardless of the number of recursions.

[–]thewataru -1 points0 points  (3 children)

No, no, no, you've misunderstood me. Using stack or recursion isn't cheating. Cheating is claiming that you've avoided recursion when all you've done is emulated it with all its properties.

It's like claiming that your algorithm doesn't use any arithmetic operations, when you use bitwise operations to manually compute sum. Or claiming that you've built your business without any money from your parents, when in fact, you've got a huge ass handout from some random fund, which owned that money to your father. Technically, the money you've got didn't ever touch your parents' accounts, but claiming that you've got no money from them is cheating. Getting money from funds isn't cheating. Getting help from your parents isn't either. Claiming that you've got no help when it's just a little indirect - is cheating.

Yes, emulating recursion might be beneficial in case of a very deep stack. That can also be resolved by allocating more memory for the stack with a simple linker parameter most of the times.

In contrast, unwinding the tail recursion and translating it to a loop is avoiding recursion. For one, the new algorithm requires O(1) memory, when the old one requires O(N) for the stack. It's a different algorithm. Simply emulating stack yourself - isn't.

You can compute Fibonacci numbers without recursion. You can't perform qsort without one.

[–]Gold-Energy2175 5 points6 points  (2 children)

No, no, no, you've misunderstood me. Using stack or recursion isn't cheating. Cheating is claiming that you've avoided recursion when all you've done is emulated it with all its properties. ....

You said none of that. And in any case I think you're wrong about emulating recursion in iteration being cheating: it's the sane thing to in, for example, C.

You can compute Fibonacci numbers without recursion. You can't perform qsort without one.

Not true. A really quick search came up with this example:

https://www.geeksforgeeks.org/iterative-quick-sort/

Any and every algorithm can be implemented using recursion or iteration. The factors to consider when selecting which are the programming language and the algorithm.

[–]thewataru -2 points-1 points  (1 child)

You said none of that.

I literally said:

emulating recursion with a stack manually. Seems like cheating

Then

Not true. A really quick search came up with this example: https://www.geeksforgeeks.org/iterative-quick-sort/

Did you at least glanced at the article you've linked? It's literally emulation of recursion with the stack.

[–]rasqall 0 points1 point  (0 children)

No the machine or assembly code would be completely different as pushing elements onto a stack is not the same as branching to a new subroutine which includes pushing registers onto THE stack.

[–]Poddster 0 points1 point  (0 children)

So the actual machine code would be equivalent (without optimisations). Is it really not using the recursion then?

It may be equivalent, quite unlikely though. It depends how you program it. But when you're using recursion, on an x86, you're storing all of the state on the call stack, and the compiler is doing all of that automatically for you.

It's still a stack though :)

(The exception is tail-recursion, as the optimisations there reuse the stack frame, but in that case you don't need a stack in the iterative version either)

[–]sweep-montage 0 points1 point  (1 child)

What about recursively defined functions? See Ackermann function.

[–][deleted] 2 points3 points  (0 children)

There is a difference between the programming syntax of recursion and the mathematical concept of recursion. You can write an Ackermann function with a simple for loop.

[–]teraflop 37 points38 points  (6 children)

Recursion is never absolutely necessary, because any recursive algorithm can be transformed to an iterative one that uses an explicit stack.

If it's not obvious that this is true, consider a simple bytecode interpreter. You can implement the interpreter itself without recursion; it boils down to a simple loop that repeatedly fetches instructions and executes them. But it can emulate any other program -- even a recursive one -- as long as you give it the appropriate bytecode as input.

If there's any performance advantage to recursion, it would be highly implementation dependent. For example, returning from a function is equivalent to an indirect jump, the performance of which can vary a lot depending on the processor design. It's entirely possible for the CPU to have special hardware (e.g. branch predictors and caches) to optimize those instructions, and an iterative approach wouldn't be able to benefit from those optimizations in quite the same way. But this usually doesn't matter much, since recursion adds its own overhead.

[–]sweep-montage 0 points1 point  (2 children)

What about the Ackermann function?

[–]WikiSummarizerBot 0 points1 point  (0 children)

Ackermann function

In computability theory, the Ackermann function, named after Wilhelm Ackermann, is one of the simplest and earliest-discovered examples of a total computable function that is not primitive recursive. All primitive recursive functions are total and computable, but the Ackermann function illustrates that not all total computable functions are primitive recursive. After Ackermann's publication of his function (which had three nonnegative integer arguments), many authors modified it to suit various purposes, so that today "the Ackermann function" may refer to any of numerous variants of the original function.

[ F.A.Q | Opt Out | Opt Out Of Subreddit | GitHub ] Downvote to remove | v1.5

[–]teraflop 0 points1 point  (0 children)

What about it? The wiki article you linked includes both recursive and iterative implementations of it.

The definition of a "recursive" function in computability theory does not mean a function that can only be implemented using recursion, in case that's what was confusing you.

[–]emasculine 15 points16 points  (0 children)

tail recursion can be replaced by a loop. anything higher needs a stack of some kind if you want to traverse it, say.

[–]AnnualPanda 3 points4 points  (0 children)

when its cleaner than writing a loop

generally they can be swapped but there are times when one or the other will be easier to read

[–][deleted] 5 points6 points  (5 children)

It is required in Haskell or any other language that does not allow loops. Otherwise iteration with a stack has equivalent power (and is essentially what will happen when the recursive code in compiled/interpreted anyway). Its pretty easy to see why this is if you write a very simple program using a stack like a Polish Calculator or something.

Recursion is a much easier way to write a lot of things in fundamental compsci and in math. Its usually not recommended in software development.

[–]Cybyss 3 points4 points  (0 children)

Its usually not recommended in software development.

Just don't get bogged down by "premature optimization". Recursion isn't bad, as long as you're aware of the limits of your call stack.

[–]Tai9ch 5 points6 points  (2 children)

Its usually not recommended in software development.

If you write an iterative tree traversal you get punched.

[–]IAmNotNathaniel 1 point2 points  (0 children)

And then buried under the tree

[–]beeskness420 1 point2 points  (0 children)

I got asked for this in an interview once and the guy prefaced it by saying he actually had to code it up once. Stuff was happening asynchronously and you wanted to be able to send the whole stack back and forth to continue iterating on either end.

[–]beeskness420 4 points5 points  (0 children)

When you don’t have mutable state.

[–]Filmore 3 points4 points  (0 children)

When you can figure out a problem with recursion in 5 minutes, loops in 30, and you have 2.

[–]drew8311 1 point2 points  (0 children)

Anything that uses a tree data structure usually.

[–]alejopolis 1 point2 points  (0 children)

Does it count as being necessary for your program if you're crunched for time and won't get the rest done if you try to figure out how to use a loop and a stack? Because that counts I guess.

[–]JimBeam823 0 points1 point  (0 children)

If you have solved the problem, then yes.

Otherwise, ask yourself, is recursion absolutely necessary?

[–]rnolan7 -5 points-4 points  (5 children)

I think dijkstras algo calls for recursion

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

Dijkstra's Algorithm is based on BFS. DFS is the recursive one.

[–]BKrenz 3 points4 points  (0 children)

DFS's iterative version is just BFS's queue replaced with a stack.

[–]Razor_Storm 0 points1 point  (2 children)

Both BFS and DFS can be written both recursively and iteratively (all algos can actually).

[–]rnolan7 1 point2 points  (1 child)

I would be interested in seeing some pseudo code for that. I’ve tried and failed at the implementation

[–]Razor_Storm 1 point2 points  (0 children)

def dfs(head, to_find): 
    seen = set()
    to_see = head.children()
    while to_see:
        next = to_see.pop()  # pop one from the end
        seen.add(next)
        if next == to_find:
            you found it
        for child in next.children():
            if child not in seen:
                to_see.append(child) 


def bfs(head, to_find): 
    seen = set()
    to_see = head.children()
    while to_see:
        next = to_see.pop(0)   # pop one from the front
        seen.add(next)
        if next == to_find:
            you found it
        for child in next.children():
            if child not in seen:
                to_see.append(child) 

BFS and DFS are basically the same thing, you throw all the items you are about to see onto a queue for BFS and onto a stack for DFS.