1.
3 Language Paradigms 7
Over the years, several other languages—most notably BASIC, Pascal, Ada, C, C++,
and Java—were developed. Today, one of the popular high-level languages for system soft-
ware and new application code is C++, which we discuss in this book.
1.3 LANGUAGE PARADIGMS
Computer languages can be categorized according to the approach they use in solving a prob-
lem. A paradigm is a model or a framework for describing how a program handles data. Al-
though there are several taxonomies for dividing current languages into a set of paradigms, we
discuss only four: procedural, object-oriented, functional, and logic, as shown in Figure 1.10.
The figure also shows which language belongs to which paradigm according to our taxonomy.
Note that the C++ language can be used both as a procedural and an object-oriented
paradigm, as we will see in future chapters.
1.3.1 Procedural Paradigm
In a procedural (also called imperative) paradigm, a program is a set of commands. The
execution of each command changes the state of the memory related to that problem. For
example, assume we want to find the sum of any two values. We reserve three memory
locations and call them a, b, and sum. The combination of these three memory locations
comprises a state in this case. Figure 1.11 shows how a procedural paradigm uses four com-
mands to change the state of memory four times. Note that the memory locations in gray
show the original state, wherein the locations are reserved for the program.
To get the value of the first number, a, into memory, we use an input command (input
a). After execution of this command, the computer waits for us to enter a number on the
keyboard. We entered 6. When we press the enter key on the keyboard, the number 6 is
stored in the first memory location and the state of memory is changed. After the second
command, again the state of the memory is changed and now both 6 and 8 are stored in
memory. The third command changes the memory state by adding the values of a and b
and storing the result in sum. Although the last command (output sum) does not look like it
is changing the memory state, it is considered a change because the value of sum is output.
The way we have written the code in Figure 1.11 is very inefficient for two reasons.
First, it contains some commands that can be repeated in the same program or other pro-
grams. Second, if the set of data to be handled is large, we need to handle them one by one.
To remove these two inefficiencies, the procedural paradigm allows packaging commands
and data items.
Computer language
paradigms
Object-
Procedural Functional Logic
oriented
FORTRAN Smalltalk LISP Prolog
COBOL C++ Scheme
BASIC Visual Basic
Ada C#
Pascal Java
C
C++
Figure 1.10 Language paradigms
8 Chapter 1 Introduction to Computers and Programming Languages
a
b
sum
a 6 6
input a b
sum
input b
a 6
sum = a + b 8
b 8
output sum sum
a 6
Program b 8
sum 14
a 6
b 8 14
14
sum 14
States
Figure 1.11 An example of a procedural paradigm
a. If we are writing code for different programs, we can package the code and create
what is called a procedure (or function). A procedure can be written once and then
copied in different programs. The standard procedures can be stored in the language
library and be used instead of rewritten.
b. If we are handling a large set of data items (for example, hundred or thousands of
numbers), we need to store them in a package (called different names such as array
or record) and input them all together, process them all together, and output them
all at the same time. The operation at the background is still done one data item at a
time, but the program can see the data items as packages.
The following shows how a procedural paradigm uses three lines of code to sort a list of
numbers of any size. Of course, we must have already written the procedures and already
packaged data items into a list.
input (list);
sort (list);
output (list);
1.3.2 Object-Oriented Paradigm
In the procedural paradigm, the often-used procedures can be created and saved. We then
apply a subset of these procedures to the corresponding data packages to solve our particular
problem. One thing that is obvious in this paradigm is that there is no explicit relationship
between the set of procedures and the set of data packages. When we want to solve a prob-
lem, we need to choose our data package and then go and find the appropriate procedure(s)
to be applied to it.
The object-oriented paradigm goes further and defines that the set of procedures that
can be applied to a particular type of data package needs to be packaged with the data. The
whole is referred to as an object. In other words, an object is a package containing all pos-
sible operations that can be applied to a particular type of data structure.
This is the same concept we find in some physical objects in our daily life. For exam-
ple, let us think about a dish-washing machine as an object. The minimum operations we ex-
pect from a dishwasher are washing, rinsing, and drying. All of these operations are included
1.3 Language Paradigms 9
Objects
13 17 8 10 list1
sort ( )
reverse ( )
11 17 38 14 list2
search (...)
print (...)
Shared 12 10 91 20 listn
Procedures
Figure 1.12 An example of an object-oriented paradigm
in any typical dish-washing machine. However, each time we load the machine with a dif-
ferent set of dishes (same as a different set of data). We need to be careful, however, not to
load the machine with a load it is not designed for (not to wash clothes, for example, in the
dish-washing machine).
In the real world, all the hardware necessary for an operation is included in an object;
in the object-oriented programming world, each object holds only data, but the code that
defines the procedures is shared. Figure 1.12 shows the relationship between the procedures
and data in the object-oriented paradigm.
1.3.3 Functional Paradigm
In the functional paradigm, a program is a mathematical function. In this context, a func-
tion is a black box that maps a list of inputs to a list of outputs. For example, adding num-
bers can be considered as a function in which the input is a list of numbers to be added and
the output is a list with only one item, the sum. In other words, the functional paradigm is
concerned with the result of a mathematical function. In this paradigm, we are not using
commands and we are not following the memory state. The idea is that we have some primi-
tive functions, such as add, subtract, multiply, divide. We also have some primitive func-
tions that create a list or extract the first element or the rest of the elements from a list. We
can write a program or a new function by combining these primitive functions. Figure 1.13
shows how we add two numbers using the functional paradigm. The code is symbolic, but
each language in this paradigm has its own definition for a function. Note also that in our
code, we distinguish between a number and a list. We have a number as 8, but a list with one
list (6, 8)
first () rest ()
sum (first (6, 8), first (rest (6, 8))) (8)
6 first ()
Code 8
sum ()
14
Visualization
Figure 1.13 An example of a functional paradigm
10 Chapter 1 Introduction to Computers and Programming Languages
Parent (Fay, Tara)
Parent (Tara, Willie) 1. Parent (Willie, Tara)? No
Parent (Tara, Benji) 2. Parent (Fay, Benji)? No
Facts 3. Parent (Tara, Benji)? Yes
4. Grandparent (Tara,Willi)? No
Grandparent (X, Y) 5. Grandparent (Fay, Benji)? Yes
Parent (X, Z) AND Parent (Z, Y) 6. Grandparent (Fay, Willi)? Yes
Queries
Rule
Figure 1.14 An example of a logic paradigm
element is (8). The function first gets a number, but the function rest gets a list. To get
the second number in the list, we first use the function rest to get the list (8) and then use
the function first to get 8.
1.3.4 Logic Paradigm
The logic paradigm uses a set of facts and a set of rules to answer queries. It is based on
formal logic as defined by Greek mathematicians. We first give the program the facts and
rules before asking queries. Figure 1.14 shows a simplified and symbolic version of a logic
paradigm. A fact such as Parent (Fay, Tara) is read as “Fay is the parent of Tara.”
1.3.5 Paradigms in C++ Language
Our discussion of four paradigms might lead you to wonder where the C++ language stands.
C++ is an extension to the C language and is based on the procedural paradigm. However,
the existence of classes and objects allows the language to be used as an object-oriented lan-
guage. In this book we use C++ mostly as a procedural paradigm in early chapters (except
for input/output that are done using objects). However, we use the language as an object-
oriented paradigm after the introductory chapters.
1.4 PROGRAM DESIGN
Program design is a two-step process that requires understanding the problem and then de-
veloping a solution. When we are given the assignment to develop a program, we are given
a program requirements statement and the design of any program interfaces. In other words,
we are told what the program needs to do. Our job is to determine how to take the inputs we
are given and convert them to the outputs that have been specified. To understand how this
process works, let’s look at a simple problem.
Find the largest number in a list of numbers.
How do we go about doing this?
1.4.1 Understand the Problem
The first step in program design is to understand the problem. We begin by reading the
requirements statement carefully. When we fully understand it, we review our understand-
ing with the user. Often this involves asking questions to confirm our understanding. For
example, after reading our simple requirements statement, we should ask several clarifying
questions.
1.4 Program Design 11
What type of numbers are we dealing with (with fractions or without fractions)?
Are the numbers arranged in any special sequence, such as lowest to highest?
How many numbers can we expect?
If we don’t clarify the problem—that is, if we make assumptions about the input or
the output—we may supply the wrong answer. To answer our questions, we need to process
integers arranged in any sequence. There is no limit as to the number of integers.
As this example shows, even the simplest problem statements may require clarifica-
tion. Imagine how many questions must be asked for a program that contains hundreds or
thousands of detailed statements.
1.4.2 Develop the Solution
Once we fully understand the problem and have clarified any questions we may have, we
develop a solution in the form of an algorithm. An algorithm is a set of logical steps neces-
sary to solve a problem. Algorithms have two important characteristics: first, they are inde-
pendent of the computer system. This means that they can be used to implement a manual
system in an office as well as a program in a computer. Second, an algorithm accepts data as
input and processes the data into an output.
To write the algorithm for our problem, we use an intuitive approach, calling not only
on the problem statement but also our knowledge and experience. We start with a small set
of five numbers: Once we have developed a solution for five numbers, we extend it to any
number of integers.
13 7 19 29 23
We begin with a simple assumption: The algorithm processes the numbers one at a
time. We name the algorithm FindLargest. Every algorithm has a name to identify it.
FindLargest looks at each number in turn without knowing the values of the others. As it
processes each number, it compares it to the largest number known to that point and deter-
mines if the new number is larger. It then looks at the next number to see if it is larger, and
then the next number and the next number until all of the numbers have been processed.
Figure 1.15 shows the steps in determining the largest among five integers.
The algorithm requires that we keep track of two values, the current number and the
largest number found. We determine the largest number using the following steps.
∙∙ Step 1: We input the first number, 13. Since largest has no value, we set it to the
value of the first number.
∙∙ Step 2: We input the second number, 7. Since 7 is less than 13, the value of largest
does not need to be changed.
∙∙ Step 3: We input the third number 19. When we compare 19 to the value of largest, 13,
we see that 19 is larger. We therefore set largest to 19.
∙∙ Step 4: We input the fourth number, 29. When we compare 29 to the value of largest,
19, we see that 29 is larger. We set largest to 29.
∙∙ Step 5: We input the fifth number, 23. Because it is smaller than 29, largest does not
need to be changed. Because there is no more input, we are done and we have deter-
mined that the largest value is 29.
∙∙ Step 6: We output the value of largest, which is 29.
12 Chapter 1 Introduction to Computers and Programming Languages
Input data
13 07 19 29 23
Step 1 13 first number 13 largest
Step 2 07 second number 13 largest
Step 3 19 third number 19 largest
Step 4 29 fourth number 29 largest
Step 5 23 fifth number 29 largest
29
Output data
Figure 1.15 Find the largest among five integers
Algorithm Generalization
The algorithm shown in Figure 1.15 does not quite solve our original problem definition
because it only handles five numbers. To make it work for all number series, we need to re-
place steps 2 through 5 to process an undetermined number of values. This requires that we
generalize the statements so that they are the same. We can do this with a minor rephrasing
of the statements as shown below.
If the current number is greater than largest, set largest to the current number.
We then include the rephrased statement in a repeat statement that executes the steps
until all numbers are processed. The resulting algorithm is shown in Figure 1.16.
It is important to realize that the design is done before we write the program. In this
respect, it is like the architect’s blueprint. No one would start to build a house without
a detailed set of plans, yet one of the most common errors of both experienced and new
The find largest algorithm
Input first number
Set largest to first number
While there are more numbers, repeat
Input next number
If next number greater than largest.
set largest to current number
Output largest
Figure 1.16 Algorithm to find largest among n numbers
1.5 Program Development 13
programmers alike is to start coding a program before the design is complete and fully
documented.
This rush to start is partially because programmers think they fully understand the
problem and partially because they are excited about getting on with a new problem to solve.
In the first case, they find that they did not fully understand the problem. By taking the time
to design the program, they raise more questions that must be answered and therefore gain a
better understanding of the problem.
The second reason programmers code before completing the design is just human na-
ture. Programming is a tremendously exciting task. To see your design begin to take shape,
to see your program creation working for the first time, brings a form of personal satisfaction
that is a natural high.
Unified Modeling Language (UML)
The Unified Modeling Language (UML) is a standard tool for designing, specifying, and
documenting many aspects of a computing system. For example, it can be used to design
large complex systems, programs, and objects within a program. It can also be used to show
the relationship between objects in an object-oriented language such as C++. We discuss
UML in future chapters when we learn to design programs.
1.5 PROGRAM DEVELOPMENT
Figure 1.17 shows the general procedure for turning a program written in any language into
machine language. The procedure for a C++ program is a little bit more involved. The pro-
cess is presented in a straightforward, linear fashion, but we need to recognize that these steps are
repeated many times during the development process to correct errors and make improvements
to the code.
Characters Disk
Source code
Editor
Programmer
Commands Disk
Compiled code
Compiler
Programmer Library code
Compiled code
Executable code
Linker
Commands Executable code
Disk
Loader
User
Executable code
Output data
Input data
User
Memory
Figure 1.17 Writing, editing, and executing a program
14 Chapter 1 Introduction to Computers and Programming Languages
It is the job of the programmer to write the program and then to turn it into an execut-
able file. There are four steps in this process:
a. Write and edit the program.
b. Compile the program.
c. Link the program with the required library modules (normally done automatically).
d. Execute the program. From our point of view, executing the program is one step.
From the computer point of view, however, it is two substeps: load the program and
run the program.
1.5.1 Write and Edit Programs
The software used to write programs is known as a text editor. A text editor helps us
enter, change, and store character data. Depending on the editor on our system, we could
use it for writing letters, creating reports, or writing programs. The big difference be-
tween the other forms of text processing and writing programs is that programs are ori-
ented around lines of code, while most text processing is oriented around characters and
paragraphs.
The text editor could be a generalized word processor, but it is more often a special
editor provided by the company that supplies the compiler. Some of the features we look for
in an editor are search commands that are used to locate and replace statements, copy-and-
paste commands that can be used to copy or move statements from one part of a program
to another, formatting commands that use colors to display key parts of the program, and
automatic formatting that aligns and indents parts of the program.
After we complete a program, we save our file to disk. This file then becomes the input
to the compiler; it is known as a source file.
1.5.2 Compile Programs
The information in a source file stored on disk must be translated into machine language so
the computer can understand it. This is the job of the compiler.
1.5.3 Link Programs
As we will see later in the text, a program is made up of many functions. Some of these
functions are written by us and are part of our source program. However, there are other
functions, such as input/output processes and mathematical library functions, that exist else-
where and must be attached to our program. The linker assembles the system functions and
ours into the executable file.
1.5.4 Execute Program
Once the program has been linked, it is ready for execution. To execute a program we use
an operating system command, such as run, to load the program into main memory and
execute it. Getting the program into memory is the function of an operating system program
known as the loader. It locates the executable program and reads it into memory. When
everything is ready, control is given to the program and it begins execution.
In a typical program execution, the program reads data for processing, either from the
user or from a file. After the program processes the data, it prepares the output. Data output
can be written to the user’s monitor or to a file. When the program is finished, it tells the
operating system, which removes the program from memory.
1.6
Testing 15
1.6 TESTING
After we write the program, we must test it. Program testing can be a very tedious and
time-consuming part of program development. As the programmer, we are responsible for
completely testing it. We must make sure that every instruction and every possible situation
have been tested.
1.6.1 Designing Test Data
Test data should be developed throughout the design and development of a program. As we
design the program, we create test cases to verify the design. These test cases then become
part of the test data after we write the program.
In addition, as we design the program, we ask ourself what situations, especially
unusual situations, we need to test, and then we make a note of them. For example, in
FindLargest, what if only one number is input? Similarly, what if the data were in se-
quence or all the same? When we design the program, we review it with an eye toward
test cases and make additional notes of the cases needed. Finally, while we code the pro-
gram, we make more notes of test cases.
When it comes time to construct the test cases, we review our notes and organize
them into logical sets. Except for very simple student programs, one set of test data never
completely validates a program. For large-scale development projects, 20, 30, or even more
test cases may need to be run to validate a program. All of these test cases become what is
known as a test plan.
One set of test data never completely validates a program.
Finally, as we test the program, we discover more test cases. Again, we write them down
and incorporate them into the test plan. When the program is finished and in production, we
still need the test plan for modifications to the program. Testing of modifications is known as
regression testing and should start with the test plan developed when we wrote the program.
How do we know when our program is completely tested? In reality, there is no way
to know for sure, but there are a few things we can do to help the odds. While some of
these concepts will not be clear until we get to later chapters, we include them here for
completeness.
a. Verify that every line of code has been executed at least once. Fortunately, there are
programming tools on the market today that help us do this.
b. Verify that every conditional statement in the program has executed both the true and
false branches, even if one of them is null.
c. For every condition that has a range, make sure the tests include the first and last
items in the range, as well as items before the first and after the last. The most
common mistakes in range tests occur at the extremes of the range.
d. If error conditions are being checked, make sure all error logic is tested. This may
require a temporary modification to the program to force the errors; for instance, an
input/output error usually cannot be created—it must be simulated.
1.6.2 Program Errors
There are three general classifications of errors: specification errors, code errors, and logic
errors.
16 Chapter 1 Introduction to Computers and Programming Languages
Specification Errors
Specification errors occur when the problem definition is either incorrectly stated or misin-
terpreted. Specification errors should be caught when we review our design with analysts
and users.
Code Errors
Code errors usually generate a compiler error message. These errors are the easiest to
correct. Some code errors generate what is known as a warning message, which usually
means that the compiler has made an assumption about the code and needs to have it
verified. It may be right, or it may be wrong. Even though the program may run with a
warning message, the code should be changed so that all warning messages are elimi-
nated.
Logic Errors
The most difficult errors to find and correct are logic errors. Examples of logic errors are
division by zero or forgetting to store the first number in largest in FindLargest. They
can be corrected only by thorough testing. And remember, before we run a test case, we
should know what the correct answer is. Don’t assume that the computer’s answer is correct;
if there’s a logic error, the answer will be wrong.
K e y T e r m s
algorithm loader
application software logic errors
application-specific software logic paradigm
arithmetic-logical unit (ALU) machine language
assembler object-oriented paradigm
assembly language operating system
central processing unit (CPU) output system
code errors primary memory
compiler procedural paradigm
computer hardware procedure
computer language program design
computer software program errors
computer system program testing
data item regression testing
executable file secondary storage
executable program software
function source file
functional paradigm specification errors
general-purpose software symbolic language
hardware system development software
high-level language system software
imperative paradigm system support software
linker text editor
input system Unified Modeling Language (UML)