.. _recursion-revisited: Functions That Call Themselves ############################## This idea is a bit shocking to beginners. Suppose you iare writing the code for a function, and it occurs to you that using the same funciton you are writing to solve the problem you are trying to solve would help. How would that work? Let's consider the classic first example of this. Factorial ********* You should have learned about this at some point in your math classes: .. math:: 5! = 5 * 4 * 4 * 2 * 1 n! = n * (n-1) * (n-2) ... * 1 But wait! .. math:: n! = n * (n-1)! This looks interesting. However, there is a catch to this formula. It does not work for any value of ``n``, and we need a way to stop it. That way is found by noting that 1! is simply 1. That is called a ``base case``. The factorial of any other (positive) value of ``n`` can be figured out using that simple "recursive" formula. But what does it look like in code? .. literalinclude:: code/ex1/main.cpp :linenos: :lines: 3-6 :caption: main.cpp That is about as simple as it gets! We have produced a short chunk of code that should work. It is hard to mess up a piece of code this short. But does it work? Here is the entire program: .. literalinclude:: code/ex1/main.cpp :linenos: :caption: main.cpp .. command-output:: g++ -o demo main.cpp :cwd: code/ex1 .. command-output:: ./demo :cwd: code/ex1 Tilt! There is a problem with that last value. We exceeded the capacity of a simple ``int`` and generated a number that would not fit into the container. C++ let us do that, and the result displayed as a negative number. (Why thta happened is something oyu will earn about in your architecture class!) Let's try that again, changing the return data type to a ``long long``: .. literalinclude:: code/ex2/main.cpp :linenos: :lines: 3-6 :caption: main.cpp .. command-output:: g++ -o demo main.cpp :cwd: code/ex2 .. command-output:: ./demo :cwd: code/ex2 Much better. That ``long long`` data type sure let's us calculate some bit numbers! If that is not big enough, clever programmers have found ways to work with huge numbers! All it takes is some "class" programming! Here is another classic recursive problem: The formula for a ``Fibonacci Number`` for some parameter "n" is defined as: * F(0) = 0 * F(1) = 1 * F(n) = F(n-1) + F(n-2) In this case, there are two ```base cases``, one for ``1`` and one for ``0``. Here is the code: .. literalinclude:: code/ex3/main.cpp :linenos: :lines: 3-7 :caption: main.cpp here is this code in action: .. command-output:: g++ -o demo main.cpp :cwd: code/ex3 .. command-output:: ./demo :cwd: code/ex3 Using Recursion on Linked Lists ******************************* If you think about it, a linked list is a recursive thing as well. We start off with an empty list, then add a node. If we are using a simple node object, with a number and a pointer to the next node in the list, that "next node" pointer is NULL, indicating that it is the last one in the list. If we add one more node to the end, we have yet another linked list, this one consisting of a head object, and what can be considered as another linked list (with one item in it). By making use of this fact, we can write routines that access our list using recursion. As a refresher, here are the original routines we used to build a simple linked list of names: .. literalinclude:: code/ex4/main.cpp :linenos: :caption: main.cpp .. literalinclude:: code/ex4/LinkedList.h :linenos: :caption: LinkedList.h .. literalinclude:: code/ex4/LinkedList.cpp :linenos: :caption: LinkedList.h .. literalinclude:: code/ex4/Node.h :linenos: :caption: Node.h .. literalinclude:: code/ex4/Node.cpp :linenos: :caption: Node.cpp In this example, we add new nodes to the head of the list, but we could modify our ``LinkedList`` management class and keep track of the end (tail) of the list easily, and generate routines to add new nodes to the end if that makes more sense to your problem. The routines we wrote to access the list, specifically, the ``LinkedList::print`` and ``LinkedList.count`` methods, both use a while loop to walk down the list. The first routine prints out the names in each node, and the second routine simply counts the number of nodes in the list. .. command-output:: g++ -o demo main.cpp LinkedList.cpp Node.cpp :cwd: code/ex4 .. command-output:: ./demo :cwd: code/ex4 Resursive Replacements ********************** Let's replace both of these iterative (looping) routines with methods that use recursion to do the same job: To make these routines recursive, we will need to change the prototypes for both methods, and hand them a ``Node`` pointer. Here are the new method prototypes in ``LinkedList.h``: .. literalinclude:: code/ex5/LinkedList.h :linenos: :caption: LinkedList.h And here are our modified methods: .. literalinclude:: code/ex5/LinkedList.cpp :linenos: :lines: 13-35 :caption: LinkedList.cpp: You need to look closely at these routines. The ``print`` method wakes up with a parameter that points to some Node. If that pointer is ``nullptr``, we have hit the end of the list, so we have nothing to print. If the pointer is not ``nullptr``, then we print the current name in that node, and recursively call the same method passing in the address of the next node in the list. It should be obvious that this will generate the same list of names we saw in our original code that used a while-loop. The count routine works in a similar way. If the pointer we wake up with is ``nullptr``, we return a zero. If it is not ``nullptr``, we return 1 plus whatever is returned from the recursive call to the same routine which we hand the address of the next pointer to as a parameter. Once again, this gives the same answer as the original code. .. command-output:: g++ -o demo main.cpp LinkedList.cpp Node.cpp :cwd: code/ex5 .. command-output:: ./demo :cwd: code/ex5 Just for fun, here is a recursive routine that prints out the list of names in reverse order. See if you can see how it works: .. literalinclude:: code/ex6/LinkedList.cpp :linenos: :lines: 29-35 :caption: LinkedList.cpp The slight variation in this print routine is enough to print our the list of names in reverse order! Cute! Here is the new ``main.cpp`` we need to demonstrate these new methods: .. literalinclude:: code/ex6/main.cpp :linenos: :caption: main.cpp Here is what we get when we run this new version: .. command-output:: g++ -o demo main.cpp LinkedList.cpp Node.cpp :cwd: code/ex6 .. command-output:: ./demo :cwd: code/ex6 Comparing the methods ********************* In general, once you get the hang of writing recursive routines to access these dynamic data structures, I think you can see that the code is a bit easier to write (and is certainly shorter). However, there is a price to pay for this simplicity. Every time you call a method, space is allocated for that method on the system "stack" (yet another data structure that lets us use procedures). That space is occupied until the method returns, then it is released for other methods to use. If your method recurses (!) too deeply, you can run out of memory. This is not a problem, if your write the routine using loops instead. So Which is better? ******************* For every recursive routine you write, there is an equivalent non-recursive method that will do the same thing. In general, the non-recursive routine will be harder to write, and may be easier to write wrongly. So, most of the time, it is probably better to start off writing recursive routines if you can, then rewrite them if you need more speed, and need to use less memory to get your job done. As you study more complex data structures, you will find many opportunities to write simple recursive routines to access the data you store in these structures. This introduction should help you get started in that part of your learning! Data structures are extremely important in writing real, complex, programs!