whare are loops?
what are the syntax and semantics of a while loop or a do-while loop?
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.
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 $n$ 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 $n$ instead of an explicit term number, we can represent the whole program as follows:
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:
we set up the loop by initializing the value $n$ (start in Term 3),
we execute the loop and update the value of $n$ every time we go through it and
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 $n$ 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.
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:
the keyword while
(though there is never an else
),
a condition in parentheses and
a block of code (a loop body) to be executed if the condition is true.
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.
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.
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
Consider the problem of computing a factorial. We know that:
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:
This definition has two parts:
the general case: how to compute a factorial abstractly for most numbers and
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?
Using the following function defintion (which you can just copy and paste into your code):
double getNumberFromUser()
{
double value;
cin >> value;
return value;
}
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.
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
(at this point, if g is a really good guess, y will be close to 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 |
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?
(c) 2009â€“2018 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.