Recursion Notes

Recursion in its simplest form means solving a problem by repeating a process until the answer is found.  A possible analog is if you are trying to figure out where you left your car keys, you can work by tracing your steps backward.  You can work like this:

Step 1 - Where was I 10 minutes ago?
Step 2- Did I have my keys?  
Step 3- If I did, I know where they are now, if I didn't, I need to look 10 minutes further back, and go back to Step 1

This will continue until you figure out where you found your keys (very hopefully)

Computers are somewhat the same in that a similiar principle is at play.  
Let me give a very very simple example:

Let us imagine that computers could not add well.  (which should not be a huge stretch of anyone's imagination)  Let us say that for some odd reason they could not add more than 1 to any given number at any given time, meaning that they could figure out what 1 + 7 is, but not what 4+4 was.  In this bizarro world, you could help yourself by writing a recursive method to fix this problem.  It could look like this:

public int fixadd(int a, int b) {
    if (a == 1) return 1+b;
    else return 1 + fixadd(a - 1, b);
}

What this is saying is if we're dealing with a situation we know about for sure (adding 1 to a number), we can just do that and be done, otherwise, we need to get closer to the situation we know about (by making a one less), and adding 1 to the front.  We're using the mathematical princple that a+b = 1+(a-1)+b

This illustrates the basic principles of recursion:
A)    You need a stop condition.  This is also often called a base case.  This is a situation that when reached you can stop recursing because the problem is very simple at this point.  In this case, this is a situation that our bizarro computer can handle on its own.
B)    You need a recursive step, this is a step designed to do what work you can in an unknown situation and try to work CLOSER to the base case.  In this case, we're making a 1 less at our recursive step, and assuming that a starts off as a positive number, making a 1 less will get us closer.

Let me give a slightly more complicated example:

There is a mathematical series called Fibonacci's Sequence.  This is a number series that starts like this: 1 1 2 3 5 8 13 21 34 and so forth.  You can see that all numbers are simply the sum of the two that come before it.  (as long as the 1st two numbers are 1)
If we wanted to figure out what any value in this series is just by specifying its position in the series, we could do it as follows.
The base case here could be the simple knowledge that the 1st & 2nd position in the series must be 1.
The recursive case could be that all other numbers are the same thing as the sum of the two before it.

So possible code for this can look as follows:
// precondition: assume position is 1 or greater
public int fibonacci(int position) {
    if (position < 3) return 1;
    else return fibonacci(position - 1) + fibonacci(position - 2);
    }

Try running that code!

Finally, you already somewhat know a simplified version of recursion.  Most for loops can be rewritten as recursive methods.  
For example, the following for loop will count up the number of times that 'a' occurs in a String, you can rewrite it quite easily as a recursive method:

int count = 0;
for (int b = 0; b < str.length(); b++)
    if (str.substring(b,b+1).equals("a") count++;

the recursive version would be as follows (assuming that position and count starts at 0)

public static int counta(int position, String str, int count) {
    if (position == str.length()) return count;
    else if (str.substring(a,a+1).equals("a")) {
        return counta(position+1, str, count + 1);
        }
    else return counta(position+1, str, count);
    }

What this is saying is that if we're at the end of the string, we're done with work (and we don't want to modify count), otherwise, we want to see if the character at this position is 'a', if it is we want to continue running through the string with a count that's one higher, otherwise we just want to continue running through the String.  Note how at every step we're moving position up 1, thus getting closer to the base case.

An issue we've been skirting around has been the issue of the fact that there are two major forms of recursion.  For the AP it appears that you will not need to be able to identify which is which, or code them one way or another (at least they won't EXPLICITLY require you to do so), but it is still very very useful for you to know the beast (recursion) as best as you can in order to be as prepared for this exam as you possibly can.  (and you know, for educational reasons as well)

Embedded Recursion:
    Embedded recursion is the form of recursion where the result is computed within the return statement, and thus you can't figure out what the answer is until the very end when all of the recursive calls are unfolded.  An example of code that works that way is as follows:
(the stupid adder example)
int stupidadd(int a, int b) {
    if (a == 1) return 1 + b;
    else return 1+stupidadd(a-1, b);
}

if we were to run this with an input of 3, 4 the program would first try to figure out what stupidadd (sa for short) (3,4) is

since 3 is not 4, it would say "ah, it's the same as 1+sa(2,4)
the problem is it doesn't know what sa(2,4) is so it holds back the 1, and figures out what sa(2,4) is.  2 is not 1, so it knows that sa(2,4) is the same as 1+sa(1,4), but it doesn't know what sa(1,4) is. So then it realizes that 1 IS the same as 1 (clever, ain't it?) and thus sa(1,4) is 5, once it knows that, it realizes that sa(2,4) is 1+ 5 or 6, and thus sa(3,4) is 1+6 or 7, and thus at the VERY END it can figure out things.  
The point of this is that until the very end, you have no idea what the answer is.  This is probably the purer form of recursion, and it has some advantages, for instance, you can have a recursive step with many calls back to the method in question.  (for instance, return fibonacci(n-1)+fibonacci(n-2))

Tail recursion:
The other form of Recursion is called tail recursion.  This is perhaps a little more comfortable for you as it doesn't have the magical step in which you say you want to return the value of the recursive call as the value of the current call.  (return fibonacci(n-1)+fibonacci(n-2) as the return value of fibonacci(n))  Under this form you add an extra argument to help you build up the final result as you go along.  This gives you the advantage of always knowing what the current answer is.  For instance, the above stupidadder would be written as follows as a tail recursive method:
// precondition: result = 0
int stupidadd(int a, int b, int result) {
    if (a == 1) return result + 1 + b; // yes, this is a violation of how the stupidadder is supposed to work
    else {
    result += 1;
    return stupidadd(a-1, b, result);
    }
}

However, there is one sort of catch with tail recursion: it adds an extra parameter...

Sometimes in life (and on the AP or my classwork) they will demand you write a method that must fit a particular prototype and you realize you can't get your tail recursive (or embedded recursive) method working with only those parameters... but if you had an extra one it would be a cinch.

In those cases, unless told otherwise, you can just write another method!  Let's say for the stupidadder method one were to require you to have it match this format:
int sa(int a, int b);

well you could always write a new method like this:

int sa(int a, int b) {
    int result = 0;
    return stupidadd(a, b, result);
}

This new method is called a helper method.  In this case it helps you write the kind of method you want to.  (assuming you prefer tail recursion)

For most cases, you should code whichever you feel more comfortable with.