Module 5: Loops

  • whare are loops?
  • what are the syntax and semantics of a while loop or a do-while loop?

Flowcharts again

Flowchart of promotion decisions in Term 3

In the previous module, we saw that the control flow of a program can be changed through the use of if statements. We used the example of a student’s progression through Term 3 of an Engineering discipline to show how we can choose to follow one path of control flow or another based on a condition. However, there is more to control flow than simply choosing between two (or more) alternative paths! We said that there are two major ways of directing control flow in a program:

  • we can choose to execute one bit of code or another, based on some condition (conditional control flow) and
  • we can execute a bit of code over and over while a condition is met (looping).

In this module, we will see the second form of control flow: looping while a condition is met.

Flowchart of promotion decisions through all terms in a discipline

A more complex example

Previously, we considered how a flowchart can be used to model a student’s promotion decision in a single term. However, the Engineering program is more than just one term! The flowchart to the right shows something a bit more realistic: a student’s progression through all of the academic terms of their Engineering discipline.

Notice that the only difference between each term is which term it is. That is, if we could abstract away the detail of which term we are currently in, calling it something like term $x$ instead, we could treat every term in exactly the same way. We could then write a description of “how a term works” without having to know which term it is, and then we could repeat this procedure for each term from three to eight.

Note: the previous paragraph is an example of two key programming concepts, abstraction and algorithms. An algorithm (step-by-step description of how to do something) that doesn’t need to know which term we’re in (i.e., one that is more abstract because it works at a higher level, ignoring a low-level detail) is one that can be applied in many situations instead of a single one.

If we change the description of each term to use the variable $x$ instead of an explicit term number, we can represent the whole program as follows:

Flowchart of promotion decisions through all terms in a discipline

Now there is one description of “a term”, and we simply repeat it over and over. This is called looping, and there are ways of doing this in every programming language. Note, in particular, three key aspects:

  1. we set up the loop by initializing the value $x$ (start in Term 3),
  2. we execute the loop and update the value of $x$ every time we go through it and
  3. we check a condition every time we go through the loop to see if we’re done yet.

There are two common errors in using loops (in many/all programming languages!). If we neglect setting up the loop, we will start with an unknown value of $X$ (maybe we’re in Term 473?), so the behaviour will not be what we expect. In fact, it could be different every time we run the program! If we don’t update the value of $x$ every time we go through the loop, it will always remain at 3, so Term == 8 will never be true, so we will loop around and around forever (this is called an infinite loop). We must do all three things (set up the loop, update something and check a condition) every time we “go around” in order to have a logically-valid loop.

We’ll now see how this applies to C++ with the while loop.

Syntax

The first kind of loop that we’ll write in C++ is a while loop. This kind of loop looks, syntactically, a bit like an if statement:

while (condition)
{
	/* body */
}

Just like an if statement, there are three key syntactic elements to notice:

  1. the keyword while (though there is never an else),
  2. a condition in parentheses and
  3. a block of code (a loop body) to be executed if the condition is true.

Aside

Just like if statements, it is possible to omit the braces in a while loop if there is only one statement in the loop body:

while (x < 10)
	x = x + 1;

However, just like if statements, this is not recommended. You can review the rationalle for this statement in the previous module.

Do-while

There is a closely-related kind of loop in C++: the do-while loop. For now, we’ll just look at its syntax:

do
{
	/* body */
}
while (condition);

This kind of loop has the same syntactic elements as a while loop, but it starts with do instead of while and the condition comes at the end of the loop rather than the beginning. There is an important semantic distinction here, which we’ll discuss below in the section on semantics.

Semantics

Whereas an if statement (without an else clause) means, “if this condition is true, execute this code over here and then move on”, a while loop means, “execute this code over and over until this condition becomes false”. We can think of the difference as:

if
execute a block of code zero times or once
while
execute a block of code zero or more times
do-while
execute a block of code one or more

Example: factorial

Consider the problem of computing a factorial. We know that:

$$3! = 3 \times 3 \times 1$$

$$5! = 5 \times 4 \times 3 \times 2 \times 1$$

These equations are very concrete: we can actually evaluate both sides of the equation and check that it’s true. However, such concrete statements of truth are not very widely applicable! We would like to have a more abstract solution: how can we find the factorial of any positive integer? We can express the definition of the factorial function in terms of math:

$$n! = n \times (n-1)!~\Big\vert~n > 0$$ $$0! = 1$$

This definition has two parts:

  1. the general case: how to compute a factorial abstractly for most numbers and
  2. the base case: what to do in a specific, concrete case.

This is also how proof by induction works: you prove that something is generally true for $n$ as long as it’s true for $n-1$, then you find an example of an $n$ where you can prove it using other means. Then, you’ve proved your theorem from that value of $n$ up to infinity! These kinds of problems — with a general case and a base case — are also very amenable to implementation in a computer program using loops. We can express the computation of a factorial using a loop, as shown in the following pseudocode:

factorial(number):
	let factorial = 1
	while number > 0:
		factorial = factorial * number
		number = number - 1

Pseudocode, like flowcharting, is a useful way of expressing an algorithm without worrying about the details of any particular programming language like C++. That is, pseudocode lets us express an algorithm more abstractly than if we were worrying about the details of C++ function declarations, creating a main function, etc. However, the essential qualities of the abstract algorithm have still been captured here.

Exercise: translate this pseudocode into C++ and run it. What should your program calculate the values of $11!$, $12!$ and $13!$ to be? What does it actually calculate them to be? Where does the computation break down when using the int type?

Exercises

Numeric input

Using the getNumberFromUser() function from some previous example code together with the isnan() function from the C++ standard library, write a function that will prompt the user to enter a number. As long as the user types things things that are not numbers, your function should keep prompting the user to enter a number.

Note: you do not (yet) need to understand how the getNumberFromUser() function works: you just need to call it from your code.

Square root approximation

Ancient Babylonian mathematicians had a simple algorithm for computing the square root of a number $x$:

let g be a guess at the square root
repeat:
	let y = x / g (if g were the actual square root of x, then y would be g)
	update g to the average of g and y

	(keep going until the guess is close enough)

Here is an example of this algorithm being used the find the square root of 14. After just five iterations, we converge on something very close to the correct answer:

Iteration $g$ $y$ $g^\prime - g$
begin 3 4.666666667 0.833333333
1 3.833333333 3.652173913 -0.09057971
2 3.742753623 3.740561471 -0.001096076
3 3.741657547 3.741657226 -1.60541E-07
4 3.741657387 3.741657387 -3.55271E-15
5 3.741657387 3.741657387

where $g^\prime - g$ is the difference between the new value of $g$ at the end of a loop iteration and the previous value of $g$. Notice how quickly this “amount of change” value shrinks! We can use this to tell us what “close enough” means when implementing the algorithm above. So, then, the exercise is to implement the Babylonian square root algorithm, making it print the number of iterations it takes to find a square root. Is a while or a do-while better suited to this task?

License: CC BY-NC-SA

(c) 2009–2016 Michael Bruce-Lockhart, Theo Norvell, Dennis Peters and Jonathan Anderson. Licensed under a Creative Commons Attribution–Noncommercial–Share-Alike 2.5 Canada License. Permissions beyond the scope of this license may be available at theteachingmachine.org.