1. Recursion
What is recursion?
Recursion occurs when a function calls itself. This may seem counterintuitive at first, because you may think there can be only one copy of a function running at any particular time. However, each time you call a function, a separate instance of that function is created. Let's illustrate this with an example:
public class MyProgram { public static void main(String[] args) { example(1); System.out.println("goodbye"); } static void example(int x) { if( x == 1 ) { System.out.println("first time!"); example(2); //the "recursive call" System.out.println("done with first time!"); } else if( x == 2 ) { System.out.println("second time!"); } //you don't need to write return; because void functions auto return at bottom } }
When we trace the previous program, we see the following path: First, we start in main(). Then we run example(1). It outputs "first time!". Next, we run example(2), which outputs "second time!". Then, we return to example(1), because that is what called example(2). We print "done with first time!" here in example(1). Finally, we return to setup, where it prints "goodbye".
Remember the call stack from the previous section. You can picture it as a stack of papers. In computer science, we say that adding something to a stack is called a "push", while removing something from the top of a stack is called a "pop". Thus the previous program pushes main(), then pushes example(1), then pushes example(2), then pops example(2), then pops example(1), then finally pops setup(), which ends the program.
You might ask: what can we do with recursion that we can't already do with loops? Technically, nothing. However, some things are much harder to program without recursion than with recursion. Advanced programmers may choose to replace all recursion with loops and custom "stacks". In this chapter, we will first look at a few contrived examples where recursion is not recommended. Then, we will look at examples where recursion is preferred.
The mystery function example
Let's look at this mysterious recursive function. Don't run it yet! Can you guess what mystery(0) outputs? What about mystery(1)? mystery(5)?
public class MyProgram { public static void main(String[] args) { System.out.println(mystery(0)); System.out.println(mystery(1)); System.out.println(mystery(5)); } static int mystery(int x) { if( x == 0 ) { return x; } return 1 + mystery(x-1); } }
The answer to mystery(0) is simply 0, because in mystery if x is zero, it returns 0. When a function reaches a return statement, it exits immediately with that value
The answer to mystery(1) is 1. We can trace the call to see mystery(1) becomes 1 + mystery(0), which becomes 1 + 0, which is 1
mystery(5) is 5, although the path is much longer. mystery(5) becomes 1 + mystery(4) which becomes 1 + (1+mystery(3)) which becomes 1+(1+(1+mystery(2))) which becomes 1+(1+(1+(1+mystery(1)))) which becomes 1+(1+(1+(1+(1+mystery(0))))) which becomes 1+1+1+1+1+0, which is 5. You can tell from this breakdown that mystery(5) runs mystery six times!
Generally, mystery(x) is x for positive values of x. For negative values, this program crashes with a stackoverflow error, because it tries to do so much recursion that Java thinks we've gotten stuck in an infinite recursion (which we have). Additionally, mystery(x) runs the mystery function x+1 times. So even if we didn't crash, the program would take a very long time to run for large values of x