Functions

Don’t write the same code twice.

  1. Define and call a function.
    Secret Agent Man: lyrics and audio.
    1. function1.C, function1.txt
      This program has all its code in the main function.
    2. function2.C, function2.txt
      This program has one function named chorus, in addition to the main function.
      The empty parentheses () means that we are not attempting to carry any information from the main function down to the chorus function.
      The void means that we are not attempting to carry any information back from the chorus function back up to the main function.
    3. Colored versions. The code that belongs down in the function (and the expressions that call the function) is in orange.
      1. function1.html
      2. function2.html

Factorials

The product of all the integers from 1 to n is called n factorial, and is written n! with an exclamation point. Here are three examples:

3! = 1 × 2 × 3 = 6                
4! = 1 × 2 × 3 × 4 = 24        
5! = 1 × 2 × 3 × 4 × 5 = 120

One use of a factorial is to tell us how many orders n objects can be arranged in. n objects can be arranged in n! different orders. For example, the three objects a, b, c can be arranged in 6 different orders, and the four objects a, b, c, d can be arranged in 24 different orders:

  1. a, b, c
  2. a, c, b
  3. b, a, c
  4. b, c, a
  5. c, a, b
  6. c, b, a
  1. a, b, c, d
  2. a, b, d, c
  3. a, c, b, d
  4. a, c, d, b
  5. a, d, b, c
  6. a, d, c, b
  7. b, a, c, d
  8. b, a, d, c
  9. b, c, a, d
  10. b, c, d, a
  11. b, d, a, c
  12. b, d, c, a
  13. c, a, b, d
  14. c, a, d, b
  15. c, b, a, d
  16. c, b, d, a
  17. c, d, a, b
  18. c, d, b, a
  19. d, a, b, c
  20. d, a, c, b
  21. d, b, a, c
  22. d, b, c, a
  23. d, c, a, b
  24. d, c, b, a

Scope and allocation

  1. automatic.C, automatic.txt
    Create a variable f within the {curly braces} of a function body.

Scope

In the above program, we created the variable f within the {curly braces} of the factorial function. When we create a variable within any pair of {curly braces}, we can mention the name of the variable only within those curly braces. We therefore say that the variable has local scope. In the above program, for example, the variable f could not be mentioned in the main function.

Allocation

In the above program, we created the variable f within the {curly braces} of the factorial function. When we create a variable within any pair of {curly braces} as the computer executes the program, the variable stays alive only until the computer reaches the closing curly brace }; at that point the variable dies. (Here’s what “death” means: when a variable dies, it stops holding a value.) We therefore say that the variable is automatically allocated. In the above program, for example, the variable f dies as we return form the factorial function to the main function.

If the computer executes the code within the {curly braces} a second time, then the variables created within the braces are reincarnated. In the following excerpt, for example, a variable named j is born holding the value 2. After it dies, another variable named j is born holding the value 4. The two j’s are two different variables, living at different times and holding different values. They just happen to have the same name and data type.

	for (int i {1}; i <= 2; ++i) {   //The loop iterates 2 times.
		int j {2 * i};
		cout << j << "\n";
	}

Arguments and return value

  1. argument.C, argument.txt
    Pass an argument to a function.
    A formal argument vs. an actual argument.
    A function should begin by checking the values of its formal arguments.

  2. A function with more than one argument.
    1. graphpaper1.C:, graphpaper1.txt
      Pass four arguments from the main function to the graphPaper function.
    2. graphpaper2.C, graphpaper2.txt
      The main function calls the graphPaper function, which calls the line function.
      Error checking omitted for brevity.

  3. Return a value from a function.
    retval.C, retval.txt
    Exercise.
    In the main function of retval.C, call the factorial function 13 times in a loop
            for (int i {0}; i <= 12; ++i) {
                    cout << setw(2) << i << " " << setw(9) << factorial(i) << "\n";
            }
    
    to output the following table of factorials. Remember to #include <iomanip> for the i/o manipulator setw.
     0         1
     1         1
     2         2
     3         6
     4        24
     5       120
     6       720
     7      5040
     8     40320
     9    362880
    10   3628800
    11  39916800
    12 479001600
    
    To go all the way to 20!, we would have change the data type of the return value of the function to long int and set the width to 20. The biggest number we can store in a plain old int on our machine is only

    numeric_limits<int>::max() = 2,147,483,647

    while the biggest number we can store in a long int on our machine is

    numeric_limits<long int>::max() = 9,223,372,036,854,775,807

     0                   1
     1                   1
     2                   2
     3                   6
     4                  24
     5                 120
     6                 720
     7                5040
     8               40320
     9              362880
    10             3628800
    11            39916800
    12           479001600
    13          6227020800
    14         87178291200
    15       1307674368000
    16      20922789888000
    17     355687428096000
    18    6402373705728000
    19  121645100408832000
    20 2432902008176640000
    
    Back in 1968, Donald Knuth wrote, “It is helpful to keep the value
    10! = 3,628,800
    in mind; one should remember that 10! is about 3½ million. In a sense, the number 10! represents an approximate dividing line between things which are practical to compute and things which are not. If an algorithm requires the testing of more than 10! cases, chances are it may take too long to run on a computer to be practical. On the other hand, if we are to test 10! cases and each case requires, say, one millisecond of computer time, then the entire run will take about an hour. These comments are very vague, of course, but they can be useful to give an intuitive idea of what is computationaly feasable.” —The Art of Computer Programming, 2nd ed., Vol. 1, pp. 45–46.
    A millisecond is one thousandth of a second, so
    1 hour = 60 × 60 seconds = 3600 seconds = 3,600,000 milliseconds ≈ 3,628,800 milliseconds = 10! milliseconds
  4. pi.C, pi.txt
    A function named pi that returns an approximation of the value of π. The argument n of the function specifies how much work the function should do; more work gives a better approximation. We saw this algorithm in the old pi.C; now it is neatly packaged as a function. The main function concentrates on formatting the nice output table; the pi function concentrates on the numerical computation.

  5. queue.C
    As people enter and leave the queue, we are constantly incrementing and decrementing the variables vp and qp that hold the subscripts. Our queue is stored in an array of 10 strings, so the subscripts must remain in the range 0 to 9 inclusive. When we increment a subscript that is already 9, we must wrap it around to 0 so that it does not go beyond the end of the array. And when we decrement a subscript that is already 0, we must wrap it around to 9 so that it does not go beyond the beginning of the array. The code that keeps the subscripts within legal bounds has been packaged as a function named f. It receives a subscript and returns the subscript confined to the range 0 to 9 inclusive.

    To make a variable acessible to (i.e., mentionable by) more than one function, declare the variable at the top of the file. See the n in this program.

Use functions to make the code more localized.

  1. A function that returns true if a year is leap.
    1. leap1.C, leap1.txt
      write all the code in the main function.
    2. leap2.C, leap2.txt
      Make a separate function named is_leap.

  2. Colored versions.
    1. leap1.html
    2. leap2.html

  3. A stack of strings: last hired, first fired.
    1. stack1.C: a simplified version of the stack.C we saw earlier, with all the code written in the main function.
    2. stack2.C: separate functions to push and pop the stack. The contents of the stack are held in variables of global scope and static allocation. These variables live as long as the program is running.

  4. Colored versions.
    1. stack1.html
    2. stack2.html

  5. A function that returns the current hour of the day, in the range 0 to 23 inclusive.
    “If you have a mother-in-law with only one eye and she has it in the center of her forehead, you don’t keep her in the living room.” —Lyndon Johnson
    1. 12hour.C, 12hour.txt
      With the code for the real time clock written in the main function.
    2. 12hour2.C, 12hour2.txt
      With the code for the real time clock packaged as a separate function named gethour.
    3. Colored versions.
      1. 12hour.html
      2. 12hour2.html

  6. range.C
    A function that gives the user another chance if the attempt at input fails.
    eof, clear, and ignore are function that “belong to” the “object” cin.
    Next semester, we will learn that a function that belongs to an object is called a member function of that object.
    A variable that counts how many chars have been input from cin, or output to cout (or to cerr), should be of data type streamsize.

Combine local scope with static allocation.

  1. Statically allocated variables. Count how many times a function has been called.
    1. static1.C, static1.txt
      The function f has amnesia every time we return from it.
    2. static2.C, static2.txt
      Introduce a static variable to avoid amnesia.

Homeworks

  1. Search an array of structures.
    1. betterpgm.C
    2. betterpgm.C
      Pass a lambda function to the algorithm find_if.

  2. Search an array of structures.
    1. newweight.C
      Looks like the expression gravity * 9.8 is in the denominator.
    2. newweight.C
      Pass a lambda function to the algorithm find_if.
      The function convert now has a return value.

  3. Count how many times a value appears in an array of structures.
    1. jackson.C
    2. jackson.C
      Pass a lambda function to the algorithms count_if, transform, find_if.
      Dress up cout to make it look like the beginning of an array, and pass it to the algorithm transform.
  4. Convert a line of English to Morse Code.
    1. WIPMorseTranslator.C
    2. morse.C, morse.txt
      Pass a lambda function to the algorithms transform and find_if.
      Exercise.
      Output a sound file in Morse Code beeps.

A “reference” is another name for the same variable.
“Pass-by-value” vs. “pass by-reference”

  1. In the following pair of programs, r is a reference to (i.e., another name for) the variable i.
    1. reference.C, reference.txt
      Using the reference r, this program can read and write (i.e., use and change) the value of the variable i.
    2. constreference.C, constreference.txt
      Using the const reference r, this program can read but not write (i.e., use but not change) the value of the variable i.
      We say thet r gives us “read-only” access to the value of i.

  2. passby.C, passby.txt
    This program manufactures a copy of the value of a, and then passes this copy to the function f.
    The name of the copy is i.
    The function can change the value of i (which it does with an increment), but this has no effect on the value of a.

    The most common use of a reference in C++ is to pass a variable to a function without making a copy of the value of the variable.
    The r received by the function is just a reference to (i.e., an alternative name for) b, not a copy of the value of b.
    No copy of the value of b is manufactured.
    Using this reference, the function can access the value of b and can also change the value of b.
    The official jargon is: the variable a is passed by value, and the variable b is passed by reference.


  3. speed.C, speed.txt
    There are two reasons to pass a variable by reference to a function.
    One reason is illustrated by the variable b in passby.C and speed.C:
    to allow the function to change (as well as to access) the value of the variable.

    The other reason is to avoid the expense of manufacturing a copy of the value of the variable.
    For example, if the variable is big (like the bigstruct in speed.C), it would take too much time to manufacture a copy of the value of the variable.
    We pass bigstruct by refernce to avoid manufacturing a copy of it,
    and the reference is const to ensure that the function can not accidentally use the reference to damage the value of bigstruct.
    If the variable is comparatively small and simple (like the int variable a in passby.C),
    manufacturing a copy of its value is so fast that we don’t worry about it.

    threeways.C, threeways.txt
    A string or a struct is expensive to copy, so it should be passsed by reference.

  4. referencedate.C:
    Using three reference arguments, a function can return three answers.
    Also, more examples of static variables inside a function.

    Exercise.
    In referencedate.C, make the last argument of the function travel optional.
    Change the function declaration at the top of the program to

    void travel(int& month, int& day, int& year, int distance = 1);
    
    Then change the statement that calls this function to
    	travel(month, day, year);   //distance defaults to 1
    
    Only trailing arguments can be made optional.

    Exercise.
    Instead of going a specified number of days into the future from the current date, change referencedate.C so that it goes a specified number of seconds into the future from the current time.
    Since every minute has the same number of seconds, and every hour has the same number of minutes, the travel function in referencetime.C can do its job without loops and if statements.
    For example, let’s say it’s 6:00 a.m.:

    hour minute second
    6 0 0
    and we want to go 600 seconds (= 10 minutes) into the future.
    First, the second += distance; adds 600 to seconds:
    hour minute second
    6 0 600
    The 600 in seconds is obviously much too large.
    The second %= 60; knocks it down to therange 0 to 59 inclusive.
    But first, the second / 60 computes how many minutes are in these 600 seconds.
    The minute += (second / 60); adds this number to minutes.
    hour minute second
    6 10 0

auto variables

Examples.

Algorithms in the C++ STL (Standard Template Library)

The word “algorithm” has more than one meaning in Computer Science.
In this course, an algorithm is a function in the C++ STL that takes as its arguments the beginning and the end of an array.
The algorithm probably contains a for loop.
In many cases, you can call the algorithm instead of writing your own for loop.

  1. Fill an array with copies of the same value.
    1. fill1.C, fill1.txt
      Fill the array with an assignment statement inside of a for loop.
    2. fill2.C, fill2.txt
      Fill the array by calling the fill algorithm.
      Instead of wriing a declaration for this function, remember to #include the header file algorithm.h.

  2. Copy one array into another array.
    1. copy1.C, copy1.txt,
      Copy the array with an assignment statement inside of a for loop.
    2. copy2.C, copy2.txt,
      Copy the array by calling the algorithm copy.

  3. Add up all the numbers in an array.
    1. accumulate1.C, accumulate1.txt
      Add up all the numbers in the array with a += assignment statement inside of a for loop.
    2. accumulate2.C, accumulate2.txt
      Add up all the numbers in the array by calling the algorithm accumulate.
      Remember to #include <numeric> this time.
    3. accumulate3.C, accumulate3.txt
      Concatenate all the strings in the array by calling the algorithm accumulate.
      The expression string {} creates an empty object of class string. It plays the same rôle as the third argument 0 of the call to accumulate in accumulate2.C.

  4. Count how many times a given value appears in an array.
    1. count1.C, count1.txt
      Count the values with a ++ inside of an if inside of a for loop.
    2. count2.C, count2.txt
      Count the values by calling the algorithm count.
      It would make sense for this call to count to return a value of data type size_t, but unfortunately it returns a value of type long int.
      (To make matters worse, size_t is just another name for the data type long unsigned int on our machine.)

  5. Find the first place where a value appears in an array, or report that the value is absent.
    1. find1.C, find1.txt
      Search the array with an if statement inside of a for loop.
    2. find2.C, find2.txt
      Search the array by calling the algorithm find.
      An iterator is a variable that marks a position in an array.
      The function distance returs the distance from the start of the array to the position marked by the iterator.

  6. Find the smallest value in an array, or report that the array is empty.
    If the smallest value appears more than once, find the first occurrence.
    1. min_element1.C, min_element1.txt
      Search the array with an if statement inside of a for loop.
    2. min_element2.C, min_element2.txt
      Search the array by calling the algorithm min_element.
      (There’s also a max_element algorithm.)
      The operator * gives us the value (in this case, 28) at the position marked by the iterator.

  7. Sort the numbers in an array.
    1. sort1.C, sort1.txt
      Sort the numbers with nested for loops.
    2. sort2.C, sort2.txt
      Sort the numbers into increasing order by calling the algorithm sort.
      By default, given any pair of values, this algorithm decdes which value should go first by comparing them with the < operator.
      That’s why we get increasing order.
    3. sortstrings.C, sortstrings.txt
      Sort an array of strings into alphabetical order by calling the algorithm sort.
      It works because we use the < operator compare a pair of strings.

  8. Randomly shuffle the values in an array.
    1. random_shuffle.C, random_shuffle.txt
      Randomly shuffle the values by caling the algorithm random_shuffle.

Sort an array with the sort algorithm and a C++ “lambda function”.

  1. sort2.C, sort2.txt
    Sort the numbers by calling the algorithm sort.
    By default, given any pair of values, this algorithm decdes which value should go first by comparing them with the < operator.
    That’s why we get increasing order.
  2. sortdecreasing.C, sortdecreasing.txt
    Sort the numbers into decreasing order by calling the algorithm sort.
    Each time the algorithm wants to compare two values in the array, it does so by passing the two values to the greater_int function.
    If greater_int returns false, the algorithm swaps the two values.
  3. sortlambda.C, sortlambda.txt
    Sort the numbers into decreasing order by calling the algorithm sort.
    Instead of passing a little function named greater_int to the algorithm, we pass a little function with no name to the algorithm.
    The following expression (called a lambda expression) is a little function with no name that does the same thing as the function greater_int in sort3.C.
    Namely, the function receives two arguments, and returns true if its first argument is greater than the second.
    	[](int a, int b) {return a > b;}
    
  4. sort5.C, sort5.txt
    When we sort an array of structures with the sort algorithm, we must always pass a comparison function to the algorithm (until next semester).
    The comparison function can have a name (like the greater_int function in sortdecreasing.C), or it can be a lambda function (like the one in sortlambda.C).
    We pass the two structures to the comparison function by reference, to avoid the expense of creating copies of the two structures.
    We pass the two structures as const references to make sure that the comparison function can’t damage the two structures.
    1. Exercise.
      Sort the array of structures into reverse alphabetical order by changing the lambda function from
      [](const month& a, const month& b) {return a.name < b.name;}
      
      to
      [](const month& a, const month& b) {return a.name > b.name;}
      
    2. Exercise.
      Sort the array of structures into increasing numerical order by changing the lambda function to
      [](const month& a, const month& b) {return a.length < b.length;}
      
    3. Exercise.
      Sort the array of structures into decreasing numerical order by changing the lambda expression to
      [](const month& a, const month& b) {return a.length > b.length;}
      
    4. Exercise.
      Sort the array of structures into “increasing length of name” order by changing the lambda expression to
      [](const month& a, const month& b) {return a.name.size() < b.name.size();}
      
  5. sort6.C, sort6.txt
    Sort an array of dates into chronological order.
    The lambda function returns true if its first argument is an earlier date than its second argument.
    A similar example is sortpoint.C, sortpoint.txt.
    Its lambda function returns true if its first argument is closer to the origin than its second argument.

  6. Add up one column in an array of structures.
    1. calories.C, calories2.txt
      Add the numbers with a += inside a for loop.
    2. calories.C, calories.txt
      The lambda function specifies which field of each structure gets added onto the running total.
      [](int total, const food& f) {return total + f.calories;}
      

Recursion: another way of looping

  1. recursion1.C, recursion1.txt
    Output the ints from 1 to 10 inclusive with a for loop.

  2. recursion2.C, recursion2.txt
    Output the ints from 1 to 10 inclusive without any loop at all.
    Note that no variable changes its value: we never use the operators = or ++ at all.

  3. recursion3.C, recursion3.txt
    Let the user specify the ending point as well as the starting point of the series of integers.

  4. The four steps in doing recursion:
    1. Create a separate function to do the work, instead of doing the work in the main function.
    2. In this separate function, do only one part of the job, not the entire job.
    3. Do the rest of the job by having the function call itself, passing it an argument that shows that part of the job has already been accomplished.
    4. In the function, write an if statement that prevents the function from calling itself if the job is already completely finished.

  5. factorial1.C, factorial1.txt
    Output the product of the ints from 1 to 10 inclusive with a for loop.

  6. factorial2.C, factorial2.txt
    Output the product of the ints from 1 to 10 inclusive without any loop at all.

  7. maze.C, maze.txt, maze.html
    Find a path through a maze by using recursion.
    Exercise.
    To output the path with a red background color on the screen of storm.cis.fordham.edu, change the statement
    				cout << a[row][col];
    
    in maze.C to
    				if (a[row][col] == '.') {
    					cout << "\033[48;5;9m.\033[0m"; //dot with red background
    				} else {
    					cout << a[row][col];
    				}
    
    These crazy numeric codes are the xterm-256color control codes.
    For example, octal \033 is the ASCII ESCape character.
    48 means background, 38 would mean foreground.
    5 means 8-bit color, 2 would mean 24-bit color.
    9 means red, 10 would mean green, etc.
    Or instead of editing the C++ program, simply pipe the output of the original program through the Linux “stream editor” sed, and have sed surround every period (.) with the control codes:
    jsmith@storm:~$ compile maze
    jsmith@storm:~$ maze | sed $'s/\./\033[48;5;9m.\033[0m/g'
    

    Exercise.
    Have maze.C display a moving red snake that gets longer and longer as it feels its way through the maze, and that retracts when the algorithm backtracks from a dead end.

    1. The code that displays the array should be moved to a separate function at the bottom of the program.
      (Remember to declare this function at the top of the program.)
      void display()
      {
      	for (int row {0}; row < nrows; ++row) {
      		for (int col {0}; col < ncols; ++col) {
      			cout << a[row][col];
      		}
      		cout << "\n";   //at the end of each row
      	}
      }
      
    2. Add these three things to the display function.
      1. Add this statement at the start of the display function.
        	cout << "\033[H\033[J" << flush;   //Home the cursor, clear the screen.
        
      2. Add this statement at the end of the display function.
        (Remember to #include the header files chrono and thread at the top of the program.)
        We saw the function sleep_for in beer.C.
        	//Sleep for 1/5 of a second.
        	this_thread::sleep_for(chrono::milliseconds(200));
        
      3. In the middle of the display function, make the change we saw in the previous exercise. Change the statement
        				cout << a[row][col];
        
        to
        				if (a[row][col] == '.') {
        					cout << "\033[48;5;9m.\033[0m"; //dot with red background
        				} else {
        					cout << a[row][col];
        				}
        
    3. Call the display function immediately after every statement in the findpath function that changes any character in the array. There are two such statements:
      		a[row][col] = '.';  //step on it.
      		display();
      
      	a[row][col] = ' ';
      	display();
      
    Until you get it working, you can log into storm.cis.fordham.edu and run
    ~mmeretzky/bin/snake
    
    for inspiration. Watch the snake extend and retract. Have fun and good luck. You could change the number of milliseconds, or add more paths and dead ends to the maze.