Computer Science I -- Week 6

Introduction

Tonight we will cover functions, an important concept in programming.

Note that the links from this page are to handouts that will be distributed the night of class.  Only print out those handouts if you could not attend class.

Main topics this week:

Programming Assignment 1 Code Due
What is a function?
How to design a function
How to write a function
Calling a function
Module 2 Assessment
Function Prototypes
Local Variables
Global Variables
Function Writing Exercise
Pass by Value
Math Functions
Random Numbers
Enumerations
Next Week

Programming Assignment 1 Code Due

Your programming assignment 1 code is due today.  This is the last day you can turn in the programming assignment 1 code.

What is a function?

A function is just a piece of code that can be reused many times.  Imagine that you had to write a piece of code to display six strings to the screen:

cout << "This is a sample output" << endl;
cout << "that we want to display" << endl;
cout << "several places in a program." << endl;
cout << "One way is to simply duplicate" << endl;
cout << "these six lines of code." << endl;
cout << "Another option is to use a function." << endl;

If we want to display those six strings in several places in the program, we could certainly simply duplicate the six lines of code.  This has several problem, though.

  1. Our code becomes larger and takes up more space
  2. Our code becomes harder to read because it is larger
  3. If we need to fix something in those six lines, we now need to fix it in many places

(do paper example with multiple students)

For those reasons, instead of duplicating code, we prefer to use functions.  A function is a way of saying, execute these six lines of code whenever I ask you to.

(do paper example with one student being the function)

So when we use a function, we're simply putting code into a place where we can easily reuse it from multiple places.

How to design a function

The first step in writing a function is to figure out three things:

  1. What the function is supposed to do
  2. What information the function needs to do its job
  3. What information the function will give back to us

What the function is supposed to do

This is where you decide what you want the function to do.  You'll look at your algorithm and look for pieces that repeat themselves, or pieces that are complicated to understand.  If a piece is complicated to understand, we can make the program easier to read by hiding that piece inside a function.

Based on what the function is supposed to do, we will come up with a name for the function.  Let's say that the function is supposed to display a message to the user.   We might name our function displayMessage.  Or if our function is supposed to input an integer from the user, we might name our function getIntegerFromUser.   The name of a function should always describe what the function is doing. 

A function should always do one logical task.  We'll look more at this as we talk about functions.

What information the function needs to do its job

Sometimes functions need information to be given to them.  For example, consider a function that calculates the average of two numbers (the function might be named calculateAverage).   We do not want to write the function so it always calculates the average of the same two numbers, we want to make it so the function can calculate the average of any two numbers.  We do this by providing the function with the numbers to use. 

Information we provide to functions is called a parameter, or argument.  So the calculateAverage function might take two parameters, which are the numbers to calculate an average for. 

(show paper version of passing parameters to a function)

What information the function will give back to us

Sometimes the function will need to give a result back to us.  This is known as a return value.  For example, the calculateAverage function is going to take two parameters and calculate the average of the parameters.  If we want to do anything with the average, it must be returned from the function.

(do paper example of returning a value)

You can return more than one value from a function.  We'll see how this works later.

How to write a function

So now we have a function designed, and we want to write code for the function.   The general form of a function definition is:

returnvalue functionName (parameter list)
{
    function body
}

Let's take the calculateAverage function as an example.  We know the name of the function is calculateAverage, and we know that the function must take in two parameters (the numbers to calculate an average for).  The function must also return the average.  Let's say that the parameters and the return value are integers.  So our function would look like this:

int calculateAverage (int a, int b)
{
}

We can see that it returns an int.  It also takes two integer parameters.   The names of the parameters are a and b, and they become variables that can be used inside the function.

We still need the body of the function, the part that does the work.  The completed function might look like this:

int calculateAverage (int a, int b)
{
    return (a + b) / 2;
}

When we return a value from a function, we use the return keyword.  This ends the function and returns the value back to whoever called the function.  If you have a return type other than void, you must include a return statement in your function.   If you have a return type of void, you may use return with nothing following it to return early from the function.  For example:

void example (int x)
{
    if (x < 5)
        return ;

    cout << "x >= 5" << endl;
}

This function only displays the string "x >= 5" if the if condition is not true. 

A function that takes no arguments

If a function doesn't take any arguments (for example, our displayMessage function), then you simply leave out the parameters list, like this:

int displayMessage ()
{
    ...some c++ code here...
}

A function that returns nothing

If a function doesn't return any value (for example, the displayMessage function probably does not return an int), then you use the return type keyword, void.  

void displayMessage ()
{
    ...some c++ code here...
}

By now you should recognize that main is a funtion that takes no parameters and returns an integer.  You do not call the function main, that is called when your program starts executing.

Calling a function

Once you have written a function, you need to call it before it will be executed.   To call a function, you use the function name and any parameters you want to pass.   Let's use displayMessage as a simple example:

int main ()
{
    displayMessage ();
    return 0;
}

This calls the displayMessage function from main.  Note that even though displayMessage does not take any parameters, we still need to use the parentheses.   Without the parentheses, you are not calling a function and will get a strange compile error. 

Passing parameters

To pass parameters to a function, you pass in constants or variables to the function.   For example:

int main ()
{
    int first = 20;
    int second = 30;

    calculateAverage (10, 20);
    calculateAverage (first, second);
    return 0;
}

In the first call to calculateAverage, we pass in constant values.  So the parameter a will be set to 10 and the parameter b will be set to 20.  In the second call, we pass in variables.  The parameter a will be set to the value of the variable first (in this case, 20) and the parameter b will be set to the value of the variable second (in this case 30). 

Using the return value

The calculateAverage function returns an int, but in our previous example we did not use the return value.  That is legal...you can ignore the return value of a function.   Most of the time, though, you do not want to ignore the return value, you want to do something with it.  Since the return value is an int, we can put it into an integer variable:

int main ()
{
    int result;

    result = calculateAverage (10, 20);

    cout << "Result is = " << result << endl;

    return 0;
}

In this case, we put the return value into the variable result, and then display the variable result.  If the only thing we wanted to do with the return value was to display it, we could have simply used the function call in the cout statement.  The function call will evaluate to the value of the return value. 

int main ()
{
    cout << "Result is = " << calculateAverage (10, 20) << endl;

    return 0;
}

The cout statement will call the function in order to evaluate what to display.   The function will return the average of 10 and 20 (in this case, 15), and the cout statement will display the value 15.

Remember also that a function can be called from within another function. 

Module 2 Assessment

This will be a short quiz on the module 2 material.  You will need to translate a simple algorithm to C++.  Do not use functions.

Function Prototypes

We're still not done yet with our functions.  Before we can call a function, we must tell C++ what to expect...what the return type of the function is, what its name is, and what parameters it will take.  We do that with a function prototype.  The function prototype looks a lot like the first line of a function definition.  For example, here are the prototypes for the displayMessage and calculateAverage functions:

void displayMessage ();
int calculateAverage (int, int);

Note that we do not need to put the names of the parameters into the prototype.   You can if you want, but it is not required by C++.

To put it all together, here's a simple program that calls the calculateAverage function:

#include <iostream>

using namespace std;

int calculateAverage (int, int);

int main ()
{
    cout << "Average of 10 and 20 = " << calculateAverage (10, 20) << endl;
}

int calculateAverage (int a, int b)
{
    return (a + b) / 2;
}

Note that the prototypes for all functions are usually put just after the include files, before main.  Function definitions usually go after main.

Local Variables

When we talked about variables, we said that a variable can be declared inside any block of code.  Well, a function body is a block of code, so we can certainly declare variables inside of a function (in fact, you've already done this inside the main function).  Functions declared inside blocks like this are called local variables.   We say that the variable's scope is local.  Scope is simply a term that means where can we use the variable.  If the scope is local, that means we can only use the variable inside the current block. 

For example, we could have written our calculateAverage function like this:

int calculateAverage (int a, int b)
{
    int result = (a + b) / 2;

    return result;
}

The variable result is local to the function calculateAverage.  We can use it inside the function, but we cannot use it outside the function. 

We can use a local variable in another function with the same name, but it is not the same variable.  For example:

#include <iostream>

using namespace std;

int calculateAverage (int, int);

int main ()
{
    int result = calculateAverage (10, 20);

    cout << "Average of 10 and 20 = " << result << endl;
}

int calculateAverage (int a, int b)
{
    int result = (a + b) / 2;

    return result;
}

We have two local variables named result.  These are different variables, because they are in different scopes.  They can contain entirely different values.   Think of them as different pieces of paper, and don't let the fact that they have the same name confuse you.

Global Variables

There is another type of variable, called global variables.  A global variable is not declared inside of a function, but outside of a function.  A global variable can be used inside any function.  The only type of variables you should declare global are constants.  For example:

#include <iostream>

using namespace std;

void aFunction (int);

const int NUM_TIMES = 4;

int main ()
{
    for (int i = 0; i < NUM_TIMES; i++)
    {
        aFunction (i);
    }

    return 0;
}

void aFunction (int x)
{
    if (x == NUM_TIMES / 2)
        cout << "Halfway done!" << endl;
}

The global constant NUM_TIMES can be used in both main and aFunction. 

DO NOT use global variables that are not constant.  That is considered to be extremely bad programming practice, for reasons that are too advanced to get into here.

Pass by Value

When we pass parameters to a function, we say that they are passed by value.  Pass by value simply means that what we are passing to the function is a copy of the value.  

(use calculateAverage with paper variables as example)

This means that if we change a parameter inside of a function, the change will not be seen in main.  For example:

#include <iostream>

using namespace std;

void doSomething (int);

int main ()
{
    int x = 10;

    doSomething (x);

    cout << "x = " << x << endl;

    return 0;
}

void doSomething (int a)
{
    a = 50;
}

Because the paramter a is passed by value, we make a copy of the value in x.  When we change a to 50, we are changing the copy, we are not changing x.  Then, when we display the value of x, we get the original value of 10. 

Function Writing Exercise

I will pass out an exercise.  There are two parts to this exercise.  You must first write a function, and then you must write a complete C++ program to use that function

Math Functions

The C++ library provides some useful mathematical functions for you.  To use these functions, you must include the header file cmath (#include <cmath>).  Your book has a list of some of the math functions on page 161.  For example, to calculate the square root of a number:

#include <iostream>
#include <cmath>

using namespace std;

int main ()
{
    double result = sqrt (100);

    cout << "Square root of 100 = " << result << endl;

    return 0;
}

Random Numbers

There are many times in a program where we would like to generate a random number.   For example, if we are writing a program to play a card game, we may want to randomly select a card. 

The C++ library contains functions for generating random numbers.  To use these functions, you must include the cstdlib header file (#include <cstdlib>).  The two functions are srand and rand.

srand -- The srand function is used to seed the random number generator.   The random numbers are generated by a mathematical formula that must be given an initial value.  The formula will generate the same random numbers for the same initial value.  For example, if you give it a seed of 50, and the first three random numbers are 33, 29, 103, then everytime you give it a seed of 50 you will get those same random numbers.

Usually we do not want the same numbers, we want different numbers everytime we run the program.  Therefore we want to see the random number generator with a different number each time we run the program.  The most common way of seeding the random number generator is to pass in the current time.  To get the current time, we use the time function.  For example:

#include <cstdlib>

int main ()
{
    srand (time (0));

    return 0;
}

This seeds the random number generator with the current time as soon as the program starts. 

You only need to seed the random number generator ONCE.  Do not call srand for every random number you want to generate.

rand -- The rand function is used to actually get the random number.  It returns a random integer, between 0 and the maximum value of an integer.  Usually, we want a random number in some other range (between 1 and 52 for cards, between 1 and 100 for probabilities, etc). 

Therefore we need to use the random number we get back and do some calculations to get it into a different range.  The generic calculation, where low is the lowest value we want and high is the highest value we want is:

int result = low + (rand () % (high - low + 1));

So if low is 5 and high is 10, result will be a random number from 5 to 10. 

Enumerations

An enumeration is a type of variable that can contain one of a set of values.  For example, let's say that you want an integer that can only be one of three values.   The three values represent the result of a function deciding if the user has won a game of cards, lost a game of cards, or tied.  We could use an integer variable and assign special meanings to the values 0, 1, and 2.  But that doesn't make our code very easy to read. 

A better solution is to use an enumeration:

enum Status { WON, LOST, TIED };

The keyword enum just tells C++ that this is an enumeration.  The name of the enumeration comes next, and in this case is Status.  The name of the enumeration becomes a data type.  The legal values for the enumeration follow inside the curly braces. 

Since the name of the enumeration becomes a data type, we can declare variables of that type, and functions can return the type.

Status calculateStatus (int value)
{
    Status result = WON;

    if (value < 100 && value > 50)
        result = TIED;

    if (value < 50)
        result = LOST;

    return result;
}

In this game, the player wins if the value is greater than 100, they tie if the value is between 50 and 100, and they lose if the value is less than 50. 

When we use enumerations, we make the code easier to read.  We can clearly tell by reading this function that the return value means the player WON, LOST, or TIED.   This would not be so clear if we were simply returning integer values.

Next Week

Next week we will be covering more about functions, and reviewing any material from this week that is needed.  Also, your algorithm for programming assignment 2 is due next week.