Motivation

Programs tend to have many lines of code and parts of it often implement complex operations. In addition, a task can be performed several times in different parts of the program. Consequently, similar pieces of code performing conceptually the same operation appear repeatedly in the program.

Example 1: Write a program that reads course grades, for two classes of students (class A and class B). For each of the classes, the program should then display the number of grades that fall in each of the following $11$ clusters (or intervals): $[0,9]$, $[10,19]$, $[20,29]$, $\cdots$, $[90,99]$, $[100,100]$. The grades are integers between zero and $100$. The user ends each list of grades with an integer outside the range $[0,100]$ (e.g. $-1$). An example of the program execution is given next.

Cluster grades example.

A program solving this problem is shown below. Take a first quick look at it. It’s somewhat lengthy and complicated, isn’t it? Notice how the code from line $29$ to $40$ is similar to the code in lines $45$ to $56$. Code from line $22$ to $4$ is also almost a copy of the code in lines $12$ to $14$.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <iomanip>
#include <vector>

int main() {
    // Create a vector with 11 clusters initialized with zero, for class A
    std::vector<int> clustersA(11, 0);

    std::cout << "Enter grades for  class A:\n";

    // 1. Read grades for class A and update the clusters counters
    for (int grade; std::cin >> grade && grade >= 0 && grade <= 100;) {
        ++clustersA[grade/10];
    }

    // Create a vector with 11 clusters initialized with zero, for class B
    std::vector<int> clustersB(11, 0);

    std::cout << "Enter grades for  class B:\n";

    // 2. Read grades for class A and update the clusters counters
    for (int grade; std::cin >> grade && grade >= 0 && grade <= 100;) {
        ++clustersB[grade/10];
    }

    // 3. Display cluster counters, for class A
    std::cout << "\nClass A\n";

    int counter = 0;
    for (int c : clustersA) {
        if (counter == 10) {
            std::cout << "[" << 100 << "," << 100 << "]: ";
        } else {
            std::cout << "[" << counter * 10 << "," << (counter + 1) * 10 - 1 << "]: ";
        }

        std::cout << c << "\n";

        ++counter;
    }

    // 4. Display cluster counters, for class B
    std::cout << "\nClass B\n";

    counter = 0;
    for (int c : clustersB) {
        if (counter == 10) {
            std::cout << "[" << 100 << "," << 100 << "]: ";
        } else {
            std::cout << "[" << counter * 10 << "," << (counter + 1) * 10 - 1 << "]: ";
        }

        std::cout << c << "\n";

        ++counter;
    }
}

Long programs, possibly with blocks of similar code repeated several times, are difficult to understand, prone to bugs, and costly to maintain when small changes are required. Functions help the programmer to keep the code more organized and, therefore, easy to read. Functions are small reusable building blocks of programs and their use has the following advantages:

  • Improved readability
  • Easier to debug the code
  • Easier to maintain the code
  • Code reusability (i.e. same code can be re-used in several parts of the program)

Consider again the main function of the example above and compare it with the main of the program given below (which solves the same problem as in example $1$). The program below is structured with the help of two functions, read_grades and display_cluster_counters. The details of these functions are less important now and they will be discussed in section Functions and vectors.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <iomanip>
#include <vector>

// Read a list of grades
std::vector<int> read_grades();

//Display the cluster counters in C
void display_cluster_counters(const std::vector<int>& C);

int main() {
    // Read grades for class A and create the clusters counters
    std::cout << "Enter grades for  class A:\n";
    std::vector<int> clustersA = read_grades();

   // Read grades for class B and create the clusters counters
    std::cout << "Enter grades for  class B:\n";
    std::vector<int> clustersB = read_grades();

    // Display cluster counters, for class A
    std::cout << "\nClass A\n";
    display_cluster_counters(clustersA);

    // Display cluster counters, for class B
    std::cout << "\nClass B\n";
    display_cluster_counters(clustersB);
}

The important at this point is that you notice how the code became simpler to read and repeated blocks of similar code have disappeared from the program. Note that a function, such as display_cluster_counters, can be used in more than one place of the program (see lines $22$ and $26$).

Good programming practice: Write readable code. Avoid repeating blocks of code wich perform the same task. Use functions to keep your code organized and easy to understand.