Recursion vs loops

RecursionLoops

Recursion Problem Overview


I'm facing a problem where both recursion and using a loop seem like natural solutions. Is there a convention or "preferred method" for cases like this? (Obviously it is not quite as simple as below)

##Recursion

Item Search(string desired, Scope scope) {
    foreach(Item item in scope.items)
        if(item.name == desired)
            return item;

    return scope.Parent ? Search(desired, scope.Parent) : null;
}

##Loop

Item Search(string desired, Scope scope) {
    for(Scope cur = scope; cur != null; cur = cur.Parent)
        foreach(Item item in cur.items)
            if(item.name == desired)
                return item;

    return null;
}

Recursion Solutions


Solution 1 - Recursion

I favor recursive solutions when:

  • The implementation of the recursion is much simpler than the iterative solution, usually because it exploits a structural aspect of the problem in a way that the iterative approach cannot

  • I can be reasonably assured that the depth of the recursion will not cause a stack overflow, assuming we're talking about a language that implements recursion this way

Condition 1 doesn't seem to be the case here. The iterative solution is about the same level of complexity, so I'd stick with the iterative route.

Solution 2 - Recursion

If performance matters, then benchmark both and choose on a rational basis. If not, then choose based on complexity, with concern for possible stack overflow.

There is a guideline from the classic book The Elements of Programming Style (by Kernighan and Plauger) that algorithm should follow data structure. That is, recursive structures are often processed more clearly with recursive algorithms.

Solution 3 - Recursion

Recursion is used to express an algorithm that is naturally recursive in a form that is more easily understandable. A "naturally recursive" algorithm is one where the answer is built from the answers to smaller sub-problems which are in turn built from the answers to yet smaller sub-problems, etc. For example, computing a factorial.

In a programming language that is not functional, an iterative approach is nearly always faster and more efficient than a recursive approach, so the reason to use recursion is clarity, not speed. If a recursive implementation ends up being less clear than an iterative implementation, then by all means avoid it.

In this particular case, I would judge the iterative implementation to be clearer.

Solution 4 - Recursion

If you are using a functional language (doesn't appear to be so), go with recursion. If not, the loop will probably be better understood by anyone else working on the project. Of course, some tasks (like recursively searching a directory) are better suited to recursion than others.

Also, if the code cannot be optimized for tail end recursion, the loop is safer.

Solution 5 - Recursion

Use the loop. It's easier to read and understand (reading code is always a lot harder than writing it), and is generally a lot faster.

Solution 6 - Recursion

Well I saw tons of answers and even accepted answer but never saw the correct one and was thinking why...

Long story short :

Always avoid recursions if you can make same unit to be produced by loops!

How does recursion work?

• The Frame in Stack Memory is being allocated for a single function call

• The Frame contains reference to the actual method

• If method has objects, the objects are being put into Heap memory and Frame will contain reference to that objects in Heap memory.

•These steps are being done for each single method call!

Risks :

• StackOverFlow when the stack has no memory to put new recursive methods.

• OutOfMemory when the Heap has no memory to put recursive stored objects.

How Does loop work?

• All the steps before, except that the execution of repeatedly code inside the loop will not consume any further data if already consumed.

Risks :

• Single risk is inside while loop when your condition will just never exist... Well that won't cause any crashes or anything else, it just won't quit the loop if you naively do while(true) :)

Test:

Do next in your software:

private Integer someFunction(){

return someFunction();
}

You will get StackOverFlow exception in a second and maybe OutOfMemory too

Do second:

while(true){

}

The software will just freeze and no crash will happen:

Last but not least - for loops :

Always go with for loops because this or that way this loop somewhat forces you to give the breaking point beyond which the loop won't go, surely you can be super angry and just find a way to make for loop never stop but I advice you to always use loops instead of recursion in sake of memory management and better productivity for your software which is a huge issue now days.

References:

Stack-Based memory allocation

Solution 7 - Recursion

It is provable that all tail-recursive algorithms can be unrolled into a loop, and vice versa. Generally speaking, a recursive implementation of a recursive algorithm is clearer to follow for the programmer than the loop implementation, and is also easier to debug. Also generally speaking, the real-world performance of the loop implementation will be faster, as a branch/jump in a loop is typically faster to execute than pushing and popping a stack frame.

Personally speaking, for tail-recursive algorithms I prefer sticking with the recursive implementation in all but the most-performance-intensive situations.

Solution 8 - Recursion

I prefer loops as

  • Recursion is error-prone
  • All the code remains into one function / method
  • Memory and speed savings

I use stacks (LIFO schema) to make the loops work

In java, stacks is covered with Deque interface

// Get all the writable folders under one folder
// java-like pseudocode
void searchWritableDirs(Folder rootFolder){
    List<Folder> response = new List<Folder>(); // Results
    Deque<Folder> folderDeque = new Deque<Folder>(); // Stack with elements to inspect
    folderDeque.add(rootFolder);
    while( ! folderDeque.isEmpty()){
        Folder actual = folder.pop(); // Get last element
        if (actual.isWritable()) response.add(actual); // Add to response
        for(Folder actualSubfolder: actual.getSubFolder()) { 
            // Here we iterate subfolders, with this recursion is not needed
            folderDeque.push(actualSubfolder);
        } 
    } 
    log("Folders " + response.size());
 }

Less complicated, more compact than

// Get all the writable folders under one folder
// java-like pseudocode
void searchWritableDirs(Folder rootFolder){
    List<Folder> response = new List<Folder>(); // Results
    rec_searchWritableDirs(actualSubFolder,response);
    log("Folders " + response.size());
}

private void rec_searchWritableDirs(Folder actual,List<Folder> response) {
   if (actual.isWritable()) response.add(actual); // Add to response
   for(Folder actualSubfolder: actual.getSubFolder()) {
       // Here we iterate subfolders, recursion is needed
       rec_searchWritableDirs(actualSubFolder,response);
   }                
}

The latter has less code, but two functions and it's harder to understand code, IMHO.

Solution 9 - Recursion

I would say the recursion version is better understandable, but only with comments:

Item Search(string desired, Scope scope) {
    // search local items
    foreach(Item item in scope.items)
        if(item.name == desired)
            return item;

    // also search parent
    return scope.Parent ? Search(desired, scope.Parent) : null;
}

It is far easier to explain this version. Try to write a nice comment on the loop version and you will see.

Solution 10 - Recursion

I find the recursion more natural, but you may be forced to use the loop if your compiler doesn't do tail call optimization and your tree/list is too deep for the stack size.

Solution 11 - Recursion

If the system you're working on has a small stack (embedded systems), the recursion depth would be limited, so choosing the loop-based algorithm would be desired.

Solution 12 - Recursion

I usually prefer the use of loops. Most good OOP designs will allow you to use loops without having to use recursion (and thus stopping the program from pushing all those nasty parameters and addresses to the stack).

It has more of a use in procedural code where it seems more logical to think in a recursive manner (due to the fact that you can't easily store state or meta-data (information?) and thus you create more situations that would merit it's use).

Recursion is good for proto-typing a function and/or writing a base, but after you know the code works and you go back to it during the optimization phase, try to replace it with a loop.

Again, this is all opinionated. Go with what works best for you.

Solution 13 - Recursion

You can also write the loop in a more readable format. C's for(init;while;increment) have some readability drawbacks since the increment command is mentioned at the start but executed at the end of the loop.

Also YOUR TWO SAMPLES ARE NOT EQUIVALENT. The recursive sample will fail and the loop will not, if you call it as: Search(null,null). This makes the loop version better for me.

Here are the samples modified (and assuming null is false)

##Recursion (fixed and tail-call optimizable) Item Search(string desired, Scope scope) {

    if (!scope) return null

    foreach(Item item in scope.items)
        if(item.name == desired)
            return item;

    //search parent (recursive)
    return Search(desired, scope.Parent);
}

##Loop Item Search(string desired, Scope scope) { // start Scope cur = scope;

    while(cur) {
        foreach(Item item in cur.items)
            if(item.name == desired)
                return item;

        //search parent
        cur = cur.Parent;

    } //loop

    return null;
}

Solution 14 - Recursion

If your code is compiled it will likely make little difference. Do some testing and see how much memory is used and how fast it runs.

Solution 15 - Recursion

Avoid recursion. Chances are that piece of code will have to be maintained eventually at some point and it'll be easier if it is not done with recursion. Second, it'll most likely have a slower execution time.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionzildjohn01View Question on Stackoverflow
Solution 1 - RecursionJohn FeminellaView Answer on Stackoverflow
Solution 2 - RecursionRBerteigView Answer on Stackoverflow
Solution 3 - RecursionTyler McHenryView Answer on Stackoverflow
Solution 4 - RecursionEd S.View Answer on Stackoverflow
Solution 5 - RecursionEmil HView Answer on Stackoverflow
Solution 6 - RecursionAndroid DeveloperView Answer on Stackoverflow
Solution 7 - RecursionNot SureView Answer on Stackoverflow
Solution 8 - Recursionuser898384View Answer on Stackoverflow
Solution 9 - RecursionypnosView Answer on Stackoverflow
Solution 10 - RecursionSvanteView Answer on Stackoverflow
Solution 11 - RecursionswitchmodeView Answer on Stackoverflow
Solution 12 - Recursionuser865927View Answer on Stackoverflow
Solution 13 - RecursionLucio M. TatoView Answer on Stackoverflow
Solution 14 - RecursionJayView Answer on Stackoverflow
Solution 15 - RecursionSkynightView Answer on Stackoverflow