Looping the Right Way: Making the Case for While loops over Recursion

Looping the Right Way: Making the Case for While loops over Recursion

Two constructs that seem quite challenging, especially to beginners, are recursion and while loops. Although both of these constructs are used for repetitive tasks, they perform differently under different conditions. This article, with the help of JavaScript code examples, will illustrate why while loops are generally a better choice than recursion for iterative tasks (looping).

Comparative Case Study

Imagine we are tasked with finding the Longest number in a string.
This problem can be tackled using either recursion or a while loop.
Let's illustrate both solutions:

Firstly, let's use recursion:

function longestNumberWithRecursion(str, index = 0, num = "") {
  if (index === str.length) return num;

  if (/\d/.test(str[index])) num += str[index];
  else num = "";

  const nextNum = longestNumberWithRecursion(str, index + 1, num);

  return num.length > nextNum.length ? num : nextNum;
}

console.log(longestNumberWithRecursion("123abc4567890abc12")); // Output: '4567890'

Now, let's solve the same problem using a while loop:

function longestNumberWithWhileLoops(str) {
  let longest = "";
  let num = "";
  let index = 0;

  while (index < str.length) {
    if (/\d/.test(str[index])) num += str[index];
    else {
      if (num.length > longest.length) longest = num;
      num = "";
    }
    index++;
  }

  if (num.length > longest.length) longest = num;

  return longest;
}

console.log(longestNumberWithWhileLoops("123abc4567890abc12")); // Output: '4567890'

When you run both these functions on a fairly small string, "123abc4567890abc12", they perform similarly. Both functions return the correct output of '4567890', demonstrating that for small inputs, recursion and while loops can accomplish the same task with comparable efficiency.

However, when we call these functions on a significantly longer string like:

const longString = "1".repeat(10000);

The differences become apparent.

The while loop version runs without error even with the long string. However, if you try calling the recursive function with longString as an argument, you encounter a "Maximum call stack size exceeded" error.

Let me Explain...

For recursion:
Imagine you have a small box and you're filling it up with toy blocks. Each block represents a task your program needs to do. With recursion, each time you ask the program to do a task, it adds a block into the box.

Now, if you only have a few blocks, everything is fine. The box can handle it. But what if you have a giant pile of blocks - like, a thousand or ten thousand blocks? Eventually, your box will fill up and won't be able to fit any more blocks. In programming, this is like a system crash - when your box gets too full, the program just can't handle it and stops working. This is called a "stack overflow error".


For while loops:
Think about a while loop like playing fetch with a dog. You throw a ball (a task), the dog runs and gets it, then brings it back before you throw it again. There's only ever one ball being dealt with at a time, so it doesn't matter if you want to play fetch a few times or a thousand times. The dog (like a while loop) can handle it.

So, when you've got a small job, recursion (like our box of blocks) and while loops (like our game of fetch) can both handle it pretty well. But when you've got a huge job, while loops are usually a better choice because they won't get overwhelmed like recursion might. Just like you'd probably prefer to play fetch with your dog rather than try to stuff a thousand blocks into a small box!

If you wanna get more technical:

This error occurs because JavaScript, along with many other languages, limits the amount of stack space available to prevent excessive use of memory, which could lead to a system crash. In recursion, each recursive call adds a layer to the call stack. If the input is too large, this stack space gets filled up, causing the program to crash with a stack overflow error. On the contrary, while loops don't add to the call stack, making them immune to this type of error.

As a rule of thumb personally, I try to avoid using recursion. However, if I am feeling like writing cleaner-looking code than while loops may offer, I use a recursive solution if I am sure that the number of iterations or loops is less than 1000, and my function is not called very frequently.

Conclusion

This example clearly highlights that although recursion and while loops can handle tasks similarly when the task is small, their performance varies significantly with larger tasks. Therefore, understanding when to use either construct is useful in your quest to become a better programmer.