Introduction
In the previous tutorial, we learned how to make our code more robust by using while loops to screen out bad user input. In this tutorial, we are going to learn how to make our code more modular and reusable by utilizing functions. Functions are a way of abstracting out common tasks. A great example of this would be something like computing the factorial of an integer. It's a common mathematical operation, so you just want to take an integer and compute the factorial of it. If you had to write the same 5 lines of code (or more) every time you wanted to compute a factorial, it would be a bit tedious. Functions allow us to write the code once and reuse it in many places.
Code
So here's an example of a factorial function:
int Factorial(int n)
{
int ret = 1;
for(int i = n; i > 1; i--)
{
ret = ret * i;
}
return ret;
}
We have seen something similar to this in the first tutorial, with the main function. The first line specifies the output type (int), the function name (Factorial), and the input arguments (one input argument of type int and its name is n). This first line contains all of the information that the caller of the function cares about: what goes in, what comes out, and the functions name. The rest of the function does the transformation from the input to the output. The factorial function in math is defined as:
n! = n * (n - 1) * (n - 2) * ... * 3 * 2 * 1
so as an example:
5! = 5 * 4 * 3 * 2 * 1 = 120
So, we can readily see how the function given above computes the factorial of a number using a for loop. This is very powerful because now that we've written the function, we no longer need to remember the internal details about how to compute a factorial, we can just call the function that we created and get the right answer. Also, this will greatly reduce the amount of code that we need to write, in the case that we need to compute multiple factorials.
Take for instance the combination function from probability. This is used to determine how many combinations of x selections can be made from n total items ignoring order (n choose x), and the formula looks like this:
C(n, x) = n! / (x! * (n - x)!)
A concrete example is how many two letter combinations can be made from the four letters A, B, C, and D. The answer is:
C(4, 2) = 4! / (2! * (4 - 2)!) = 4! / (2! * 2!) = 24 / (2 * 2) = 24 / 4 = 6
Which we can also show by listing out the combinations:
AB
AC
AD
BC
BD
CD
So, if we wanted to code this using our Factorial function, it would look like:
int Combination(int n, int r)
{
return Factorial(n) / (Factorial(r) * Factorial(n-r));
}
This is much better than the alternative that doesn't use our function:
int Combination2(int n, int r)
{
// Factorial(n)
int fn = 1;
for (int i = n; i > 1; i--)
{
fn = fn * i;
}
// Factorial(r)
int fr = 1;
for (int i = r; i > 1; i--)
{
fr = fr * i;
}
// Factorial(n-r)
int fnr = 1;
for (int i = n - r; i > 1; i--)
{
fnr = fnr * i;
}
return fn / (fr * fnr);
}
Look at how much more code that is! Imagine if we had made a mistake in computing the factorial, then we would have to look throughout our code and find all of the places that we had copied and pasted the factorial computation and fix them, rather than if we created a function, we would only have to fix it in one place. This can be a life saver.
Something else interesting to note from the previous example is that you can call a function from within a function. This helps build up complexity in a manageable way. What might not be obvious or intuitive a first is that you can call the same function from within a function. This is called recursion, and it looks like this:
int Factorial(int n)
{
if(n < 2)
{
return 1;
}
return n * Factorial(n - 1);
}
Notice that the factorial function calls itself with the last line. This is essentially doing:
n! = n * (n-1)!
It keeps doing this until n is less than 2, and then it returns one, because 1! = 1 and 0! = 1. Recursion is an interesting way to create a loop without using a for or a while loop. In my experience, recursion isn't very common, but it is still good to know about in case you run across it one day.
Summary
In this tutorial, we learned how to make our code more reusable by creating functions. We then went on to discuss how you can use functions to help manage complexity by breaking it down into smaller pieces.
In the next tutorial, we go over the various data types and the strengths and weaknesses of each data type.
If you have any questions or comments about what was covered here, post them to the comments. I watch them closely and will respond and try to help you out.