Vocabulary

Before starting new material, it is worth taking a moment to stop and make sure that we are clear on some of the basic vocabulary used to talk about functions in programs. This was covered in the previous lesson. Hopefully the following annotated program will clarify things a bit.

Functions and Top-Down design

The essence of programming is:
Break down a single, large, complex problem into many simple pieces that can be attacked independently.

Functions help us to do this, and that is one of their most important features. They help us write programs in a top down manner, essentially be letting us write our program using a "wish-list" of functions that we can actually go back and implement later. An example should make this clearer.

Example problem:
You have a bank account whose annual interest rate depends on the amount of money you have in your account at the beginning of each year. Write a program that simulates 5 years under this system, interactively querying the user for transactions at the beginning of each year, and returning the balance at the end of the 5th year.

There's a lot to this problem. However, imagine how much easier it would be to solve if the following functions were available:

With this "wish list" of functions, we could write the program quite easily:

double B = 0.0;
for(int i = 0; i < 5; i++)
{
  B = transaction(B);
  double r = rate(B);
  B = B*compound(r);
}
cout << B << endl;
This is a pretty easy program!

Of course, the 3 functions in our wish list do not exist, so we'll have to implement them for ourselves.


double transaction(double B)
{
  // Get type of transaction
  char act;
  cout << "Enter w: withdrawl d:deposit ";
  cin >> act;

  // Get amount of transaction
  double A;
  cout << "Enter amount: ";
  cin >> A;

  // Get new Balance figure
  if (act == 'w')
    B = B - A;
  else
    B = B + A;

  return B;
}

double rate(double B)
{
  // Get # of thousands
  int T = B/1000;

  // Calc rate
  double r = 3 + T*0.5;
  if (r > 8)
    r = 8;

  return r;
}

double compound(double r)
{
  // Sim. year with monthly compounding..  
  double R = r/100, total = 1.0;
  for(int i = 0; i < 12; i++)
    total = total*(1 + R/12);

  return total;
}
The key here is that each of these functions can be implemented independently. When I implement transaction, I don't need to worry about any other aspect of the program - it's like transaction is its own little (easy!) program to write. Take a look at the complete program.

Multi-Parameter Functions

Functions get infinitely more interesting when they have more than one argument or parameter. Specifying multiple parameters for a function is just like specifying several single parameters in a comma-separated list.

For example, suppose you wanted to define a function max that looked at two ints and returned the larger of the two. It's prototype would be


int max(int,int);
The definition would look like this:

int max(int a, int b)
{
  if (a < b)
    return b;
  else
    return a;
}
Note that this example shows you that you can return from anywhere within a function (e.g., it returns b in the middle of the function definition, if a is less than b), just like you can return from anywhere within main.

Remarks on the arguments

Suppose you have a function

void rep(int,char);
The function function name rep stands for repetition. That is, it prints the char argument to the screen the number of times given by the int.

Order of arguments. It's important to note that the order of arguments is matters. For example, If I want to print a # symbol 42 times, I need to be sure to say rep(42,'#'), because the function expects the int object first.

Q: If I said rep('#',42) instead, do you know what'd happen?

A: I'd print *, 35 times!

Why? Because the same kind of implicit type conversions that go on inside expressions go on with function arguments!

Arguments' data type. When implicit conversions aren't possible, the compiler gives you an error message.

Q: If you tried to call rep("#",42), what would happen?

A: The compiler would give you an error saying:

You'll likely see lots of these messages in your life!

Predicates

What are predicates?
Functions which return the type bool are traditionally referred to as predicates.

Imagine that you want to know whether an integer is a prime number or not. We could write a function isprime to do this. The prototype would be as follows:


bool isprime(int);
We can define the function as follows:

bool isprime(int n)
{
  if (i <= 1)
    return false;

  for (int i=2; i < n; i++)
  {
    if (n % i == 0 ) 
      return false;
  }
  
  return true;
}
With the definition of this predicate, you can use the function as follows:

if( isprime(n) )    // isprime returns true or false
  cout << n << " is a prime number!" << endl;

Really there's no need to bring predicates up as a special subject, since functions that return bools are not any more special than functions that return, say, strings. However, you might not have thought much about the use of having such functions.

Problems

  1. Surveying Problem This gives you another simple example of top-down design.
  2. Drawing a triangle with *'s This is a simple multi-parameter function problem.
  3. Distance between points This is another simple multi-parameter function problem.
  4. What percentage of numbers are prime? This is a simple example using a predicate.