0:00
In previous episodes we have
discussed the notion of control structures.
They are special instructions,
making it possible to repeat
processing or to execute processing
according to a number of conditions.
Today, we will expand on this idea with another way
to repeat processing : the conditional loops.
If, in a program, we wish
to repeat a number of processing and that the number
of repetitions is known à priori, that is,
before entering the loop, it is possible
to use a "for loop".
However, there are situations where we wish to repeat processing
as long as a given condition
is true but we do not know how many times
it will be necessary to repeat the processing until
the condition becomes false. In such
a case we have to use other types of
repetition instructions :
The conditional loops. Their execution
depends on the fulfillment of a condition.
In C++ they appear as loops "do while" or "while".
For illustrations purposes, let us reuse our example
tasked with the computation of some grades' average.
A priori, we do not know
how many grades will be entered to calculate their average. We begin by
by asking it from the user. The number of
grades will thus be entered via keyboard interaction.
Then, if this number is positive, we will
use an iteration "for" and input all the grades.
At every iteration, a grade will be typed on the keyboard
and added to the sum of all grades.
Once all the grades have been entered, we will obviously
exit the iteration and continue
with the execution of the rest of the program
At this point, we will calculate the average and print it.
We can surmise that is not
very natural to let the user input a number of grades
equal to 0. Thus, we wonder
how to compel the user into
typing a number of grades
strictly greater than 0.
Concretely, we wish to repeat these instructions until
the user inputs a number of
grades strictly positive.
We do not know how many times
we will have to repeat these instructions, for we cannot
forsee a priori when the user will indeed
input a number strictly positive.
Thus, we will have tu use a special control structure.
What we are looking for, here, is the so-called
"do while" loop. The "do while" loop is written this way :
the keyword "do" followed by a pair
of braces and the keyword
"while" which means
to repeat as long as
this condition is true.
Concretely, we will repeat
these instruction inside
the body, that is
the instructions in the braces
as long as the condition is true.
Let us now examine step by step the execution flow of
our little example with the "do while" loop.
We presuppose that a variable "nombre_de_notes" has been previously declared
and is ready to contain the number of grades entered by the user.
Once we reach this instruction, nothing prevents us from
entering the loop's body.
At this point, we wil ask the user to input
the number of grades.
The number of grades is entered via keyboard interation.
Now this line will be executed, evaluating the
loop continuation condition.
Let us suppose that the user types,
as the number of grades, the value 0.
In this case, the evaluation of the condition will return
"true" which means that the number of grades doesn't
fulfill our criteria.
Since the condition is true, we will enter a new cycle.
Once again, we will thus execute the loop's body.
This time, let us suppose that
the user inputs, as number of grades,
the value -1.
During the evaluation of the continuation condition, this condition
will once again return true, for, of course, -1 is less or equal to 0.
At this point, we will
once again, enter a new cycle.
We begin our third iteration and, again,
we will ask the user to input the number of grades
and read it via keyboard interaction. Let us suppose, that this time,
the crestfallen user ends up understanding what we're expecting
and inputs number of grades strictly positive, for example, 6.
At this time, during the evaluation of the loop continuation
condition, the evaluation will return false since 6 is
strictly greater than 0. Thus, we exit the "do while" loop
and resume the execution right
after the semicolon concluding the "do while" instruction.
Now let us formally examine the syntax of a
"do while" instruction in C++.
The keywords "do while" frame the so-called
"do while body", which rests in braces.
Following the "while" is the
loop continuation condition. The semantic is as follows:
the processing within the body are repeated as long as the condition is "true".
Once again : we repeat the processing as long as the condition is "true".
Not unlike the instruction of a conditional branching,
the "do while" loop continuation
condition can be relatively complex, resorting
to logical operators.
For example, we can formulate such a condition :
let's repeat the processing as long as x is equal to y plus z and that
z is greater than 10 or t is less than 25.
Thus we can imagine pretty
sophisticated continuation conditions, especially resorting to
logical operators.
It is also worth noting that the parentheses around
the condition are absolutely mandatory in C++.
Just as it is with the"if", the condition
will be written in a pair of parentheses.
An important observation regarding the "do while" loop is
that its body will systematically be executed at least once.
Indeed, if we recall our
step by step example of a "do while" loop,
we see that at this point,
nothing prevents us from entering the body,
thus we will execute the block at least once.
However, this block will only be executed once if, after the
first iteration, the evaluation of the condition
returns "false".
In this case, we will simply resume the execution after
the loop.
But in the meantime, the block of instructions will have been executed once.
This is very important and stands among the "do while" loop's specificities.
Another observation : if the condition doesn't ever
become false, then the processing will be repeated indefinitely.
Consequently, one must be careful in the condition's formulation in order
to prevent an infinite loop. Indeed, infinite loops are generally
not the expected result,
at least not for beginners.
Those were the
core ideas regarding the "do while" loop.
Before we can put behind us
the topic of the "do while" syntax, please heed this last warning :
this semicolon here, at the end of the "do while" instruction,
which is rather easy to forget.
Now, there are situations where we wish to test the loop's condition
prior to the execution of the body.
In such as case, we will resort to another variant of
conditional loops : the "while" instruction.
Its syntax is as follows :
the keyword "while" immediately followed
by the condition, in parentheses.
Then comes the loop's body in braces.
The "while" loop is rather similar to the
"do while" loop : we will repeat the processing as long as
the condition returns true. The main difference is that, in the "while" loop,
the condition is tested before we enter the loop.
Thus, if the condition is
false from the get-go, we will immediately jump
to the next instruction after the "while", meaning that the block
will not be executed a single time. As we've seen before, it is not
the case with the "do while" loop
where the block will always be executed at least once.
Here are two basic examples, illustrating the
fundamental difference between "while" and "do while".
We have here two very similar loops,
with identical continuation conditions.
In both cases, the continuation condition relies on
a variable i, intialized to 100.
Let us start by examining
what happens during the execution of the "do while" loop.
When we reach the execution of the "do while",
nothing prevents us from entering the loop's body.
At this point, we will print
the message "bonjour".
Now we reach the point of the "do while" loop
where the continuation condition is evaluated.
Since i contains 100, the condition will of course return "false".
Thus we exit the loop and continue
the execution with the possible
instructions after the "do while".
In the meantime, the message "bonjour" has been displayed on the terminal.
Now on to the "while" loop. Upon this
line's execution, we will right away
test the condition which will, as before,
since both conditions are identical,
be evaluated as false. This means that we won't
ever enter the loop's body
and instead continue the execution
right after the "while". Thus, the loop has not printed anything.
Here, we found ourselves in a situation where two
very similar loops : one "do while" and one "while"
do not lead to the same result.
This is due to the time where the continuation condition is evaluated.
Among the classic mistakes among beginners using
the "while" and "do while" loops is one
possibly leading to very
puzzling results. This mistake relates
to our famous semicolon.
This semicolon marks the end of the "do while"
instruction and is placed right after the condition.
Now, if we were to add a semicolon after the
"while" condition, our program would behave strangely and, most importantly, incorrectly.
Let us see what would happen
if we added this semicolon.
This instruction would be interpreted
as if it were a "while" loop
with, as body, an empty instruction. Therefore, there is nothing
within the loop impacting i.
So, if i contained something
less than 10, for example 1, this loop would cycle indefinitely
since there is no way to make i reach a value
greater than 10, which would render the condition false.
As a matter of fact,
this ++i, which we probably wished
to be inside the body,
will never be reached if i started containing less than 10.
Indeed, this plus plus i instruction
will be considered as being outside the "while" loop.
Now you know three ways to repeat instructions
in a program : the "for loop"
which we have discussed
in a previous episode and what we have just seen,
namely the conditional loops which can take two forms,
either "while" or "do while".
Now, we will discuss
how to choose which form to use.
The choice depends on rather simple criteria.
If the number of repetitions is known a priori,
we will want to choose the "for" type iteration.
Imagine for example that you wish to calculate
the average of all students attending
this course. We know a priori how many students follow
this course. Thus, in order to calculate the sum of the grades
and the average, we will gladly
use a "for loop" since the number of iterations is known a priori.
However, if the number of iterations is not known
a priori, we will pick a conditional loop,
either "while" or "do while". Now how do we choose
between "while" and "do while" ?
Simply, we will ask
ourselves if we need to execute
at least once the loop's body.
If we want the body to be executed at least once,
which is the case, if we're interracting with the user,
for example, if we ask a value from the user and demand this value to be
between two values, we need the user to input the value
at least once before we can test if it fulfills the condition.
In this situation, we will rather resort to a
"do while" loop.
Otheriwse, we will naturally use a
"while" loop, that is,
a loop where the condition evaluation will
occur before entering the loop's body.
Now, let us enrich our little example from before.
It is a typical situation where a "do while" loop
is required since we need the user to input
the number of grades at least once before we can
test if this number is within the desired interval.
Thus we resorted to a "do while" loop. Now we
wish to upgrade this program a little so that
the user will be informed upon entering, for example,
a negative number, of the program's expectations.
So we wish to complete
the here program so that a
relevant message will be displayed when the user
enters a negative or null grade.
How should we proceed ?
Obviously, this message will appear every time the number
does not fulfill the desired condition.
Thus, it will probably be placed
within the loop's body : we will repeat this instruction
every time the user
inputs something incorrect.
We could imagine such a solution :
enriching the loop's body
with one more test. After having read the
entered value, we will test if the typed number
is less or equal to 0.
In this case, we will display a message to warn the user.
Please note, that this is indeed part of the loop's body
since we wish to repeat this process every time
an unsatisfactory number is entered.
As you can see in this example,
the condition testing if the number of grades
is less or equal to 0 appears now twice :
Once in the "if" and another
as the loop continuation condition.
It would have been smart to store the result of this evaluation
in a variable so that we do not need to evaluate it again.
For example, we can introduce a
boolean variable "saisie_invalide" initialized to "false". (TN: saisie_invalide means invalid_entry )
We will update this variable here, in the loop's body at every iteration,
storing the result of the condition evaluation, that is
"number of grades less or equal to 0". Thus, "saisie_invalide" will contain either "true" or "false"
This will allow us to write the condition
in a unified way, replacing this by
"if (saisie_invalide)" and we'll
resume the processing as before.
We will repeat the processing "while" the the variable "saisie_invalide" contains true.
This here is good practice.
In a program, we always wish to avoid
situations where we express the same thing
multiple times in independant ways, in several places.
It is like the copy-paste.
So, let us return to our little example
regarding the input of grades by a user.
Let us examine different ways to formulate the condition
in order to tend to different needs.
Until now, we compelled the user to type
a number of grades greater than 0.
Let us imagine,
that we wish to restrict the number of grades to 10.
The question we are asking is:
how do we formulate the condition with this new goal in mind ?
In order to formulate the continuation condition,
let us start by expressing it in plain words.
Now, the number of entered
grades will be incorrect in two cases :
either this number will be
less or equal to 0, which contradicts
this expectation, or the user will input a number
greater than 10, which contradicts this.
That means we will loop as long as
we find ourselves in either one of these situations.
In both cases, the number of grades, is incorrect.
It now becomes obvious
that we will have to resort to
the logical connective "OR". Namely,
we repeat the processing as long as either situation
is encountered.
In C++, this will be translated
by such a code. We repeat the processing as long
as the entered number of grades is less or equal to 0
OR that the number of grades is greater than 10.
Let us move on to another example. We wish to write a short
program which purpose is to make the user guess a number.
To simplify, let us imagine
that the number to guess is always 5.
The idea is to code a "do while" loop
asking a number from the user and comparing it
with the secret number. We will exit
the loop only once the number has been guessed.
We're wondering how to
formulate the condition so that
we exit the loop once the user has guessed the correct number.
As before, we will start by formulating the condition in plain words.
We know that
the loop is to be repeated as long as
the user has not guessed the secret number.
What does it concretely mean ?
They have not guessed correctly as long as the typed
number is different from the secret number, which is 5.
How do we formulate this condition in C++ ?
Simply through this line : we repeat the process
as long as the entered number is different from the secret number.
We notice that
the formulation in C++ is not too far from
the formulation in plain words.
Let us now enrich this example where the user is tasked with guessing
a number. We wish to give the user only a limited number of trials.
The poor fellow will not be allowed to guess
indefinitely. Thus we will resort to a
trial counter, that is, a variable we
introduce here. This trial counter will be
incremented at every loop iteration.
Now, we wish that,
after three trials, the game will be over
unless the user has guessed correctly.
Now we're wondering
how to formulate this condition.
Once again, let us start by formulating this condition in plain words.
We will repeat the loop as long as the user has not guessed
the correct number and as long as the user still has trials left.
It is clear that both conditions
must be fulfilled together.
We will keep iterating only if both conditions are fulfilled.
Now, let us try to formulate
this somehow closer to C++.
The first condition is that the entered number
is different from the secret number. The second
condition is that the number of trials is less
than 3. Both these conditions must be verified together.
In C++,
this translates into this line of code.
The first condition is that entered number is different from the secret number,
which translates into what is written here. The second condition is that
the number of trials must be less than 3, which translates into
what is written.
We have here the logical connective "AND" which guarantees.
that both conditions must be fulfilled simultaneously.
Again, we see, that the C++ formulation
is not too different from the formulation in plain words.
The user can now exit the loop in two different ways.
He can exit the loop because he has guessed correctly, in which case,
this part of the condition becomes false
or because he has run out of trials.
Now,
let us suppose that we wish to let the user know
why, concretely, he exits the loop.
Since we wish to inform the user of the reason why he
exits the loop, we need to enrich the program.
Once we have exited the loop,
we will test for which condition we have exited.
Did we exit the loop because of the failure
of this condition, namely, because the user has guessed
correctly. Or did we exit the loop
due to the failure of this second condition,
namely, because we ran out of trials.
Thus, we will run the test here.
If the entered number is equal to
the secret number, it means we exited the loop
because of this condition's failure and we can
inform the user that he has guessed correctly.
Otherwise, it means that the user has burnt all his trials
and we can inform his that he has lost the game.
We can also print the secret numer to let him know
what we were expecting from him.
However, we must avoid logical fallacies in the test formulation.
Let us imagine that, trying to the see if the
correct number has been guessed, we test if
the limited number of trials has been reached. In this case, the situation
where the user has guessed
the correct number exactly on his third trial
will be interpeted as a failure
while it was actually a success.
Thus, one must remain careful regarding the logic
in test formulations when we aim to realize such processing.
The correct way here was
the one presented in the first solution, namely
formulating the condition not in relation to
the number of trials but in relation to wether or not
the correct number has been guessed. This way,
we avoid the aforementioned trap.