Looping to solve problems¶
See also
Reading assignment
Text: Chapter 5
I hope you have seen that looping over a chunk of code can be useful in many kinds of problems. We need to look a bit closer at loops and figure out when they should be used.
Kinds of loops¶
When we looked at the basic structures, we saw two kinds of loops. One asked if
we had something to do before we set off to to that thing, and the other went
of and did the thing, then asked if we needed to do it again. The first kind of
loop is called a pre-test loop
and the other is called a post-test
loop
. I hope you can see why!
The two forms of loop have different forms in C++, as you might suspect. The
pre-test loop
looks like this:
while(condition_is_true) {
// do something here
}
The condition_is_true
part is any expression you can form that evaluated
to true
. We have seen a number of ways to set up such expressions.
The post-test loop
has this form:
do {
// do something here
while(condition_is_true);
Of the two, the pre-test loop
is used more often, and we really do not need
both, but having both makes certain tasks easier.
Setting up counters in a loop¶
If we want to count the number of times we work through a loop, we need to create a counter variable (an integer) and increment it inside the body of the loop. We create it and initialize it before we get to the loop statement. Like so:
int count = 0;
while(count < 10) {
count = count + 1;
cout << "current count is " << count << endl;
}
Note
Make sure you can figure out what will be displayed in this loop!
Notice that we set up and initialized the counter outside the loop. We also
made sure our condition_is_true
expression would make us enter the loop.
If we initialized the counter to 11, the loop would do nothing.
Now, we do not really need a counter, but having one is handy especially if we want to label our output lines with a number:
int count = 0;
while(count < 10) {
count = count + 1;
cout << count << ") This is data line number " count << endl;
}
Adding up numbers¶
Now, suppose we wanted to get exactly 10 numbers from the user and add them all up. This is a common problem, and we can do this easily. However, once again, we need to set things up before we get into the loop.
This time, we need a variable what will be used to build up the final answer.
We can such a variable and accumulator
since it accumulates the final
result. We already know how to set the loop up so it spins exactly 10 times.
Here is the code we need:
1 2 3 4 5 6 7 8 9 10 | int sum = 0;
int user_value;
int count = 0;
while(count < 10) {
cout << "enter a number: ";
cin >> user_value;
sum = sum + user_value
count = count + 1
}
cout << "The final sum was: " << sum << endl;
|
In this code I did not initialize the user_value
variable. This is fine as
long as you are certain that you will give it a value before using it. In this
case, we will let the user input the value.
Desk Checking the loop¶
Is this code correct? Study it and make sure you see that it will work as we
want it to!. Doing this kind of thinking is called desk checking
and it is
important, since we are trying to convince ourselves that the loop does what we
want.
One way to do this is to build a table of values. For each line in the code, we record the values of all the variables and make sure things are going right. As an example, I am going to assume that the user will enter the numbers one through ten when asked in this code. Here is a table that would result:
line sum |
user_value |
count |
|
1 |
0 |
||
2 |
0 |
||
3 |
0 |
0 |
|
4 |
0 |
0 |
|
5 |
0 |
0 |
|
6 |
0 |
1 |
0 |
7 |
1 |
1 |
0 |
8 |
1 |
1 |
1 |
4 |
1 |
1 |
1 |
5 |
1 |
1 |
1 |
6 |
1 |
2 |
1 |
7 |
3 |
2 |
1 |
8 |
3 |
2 |
1 |
And so on. (The dots indicate that no value has been assigned to those variables yet). While this seems tedious, being able to do this is a good indication that you understand what changes when in your code. You see the loop work through the steps, and eventually, you will fall out the bottom and display the final result.
Another summing loop¶
Suppose you want to count up a sequence of positive numbers without knowing exactly how many of them you might need to add (sort of like adding up the checks you wrote in a month - assuming you do such things).
What we can do in this case is to set up a sentinal
value that we will use
to identify when we are done. If we know that all positive numbers are to be
added, we can ask the user to enter a negative number to terminate the loop.
Our loop must look for this value and terminate! But there is a hitch in this.
What if the user starts off by entering a negative number. Well, we do not want
to add that into our sum, so we probably need to read one number outside the
loop, check it and then go into the loop to do the sum, then read another
number after that. If we set this up right, the loop will work. Look this over:
int sum = 0;
int user_value;
cout << "Enter a number to add, negative to quit: ";
cin >> user_value;
while (user_value >=0) {
sum = sum + user_value;
cout << "Enter another number: ";
cin >> user_value;
}
cout << "The final sum is: " << sum << endl;
Once again, you should check this to make sure you see how it will work. Better yet, pretend the user does weird things and see if it handles the possibilities. (Is zero a good entry? Will it work if so?).
Doing other work in the loop¶
We have only looked at a simple chunk of math inside the loop, but we can do many other things there as well, Our counter can be used to track the number of checks we wrote while we sum them up. We coult use those two numbers to figure out the average amount of each check we wrote during the month.
In fact, a simple variation on this might be useful in figuring out our bank balance (I am going to keep is simple, no coins in our world here!
In this case, a positive number is a check, a negative number is a deposit, and a zero gets us out of the program!
Warning
This seems backward, but most folks write a lot more checks than they make deposits, so entering a negative might be easier on them. OK, so they have to think a bit to use this code - make it better!
int balance;
int user_value;
int count = 0;
cout << "Enter a starting balance (integers only here): ";
cin >> balance;
cout << "Starting balance is: " << balance << end;
cout << "Enter checks and deposits (+ for a check, - for a deposit): ";
cin >> user_value;
while(user_value != 0) {
balance = balance - user_value;
count = count + 1;
cout << "Current balance is: " << balance << endl;
cout << "Enter checks and deposits (+ for a check, - for a deposit): ";
cin >> user_value;
}
cout << "You processed " << count << " transactions." << endl;
cout << "Your final balance is: " << balance << endl;
Obviously, with a little work, we could turn this into something more useful. Perhaps we ask the user for a code (like “c” or “d”) to indicate the kind of transaction, then get a positive number so we use the right formula. Such a program might be better since it would make more sense both to the programmer and later reader of the code.
There is no absolute right way to write code, your goal is to pick structures
and names that make the logic of your code clear to everyone. You would be
amazed at how often reading your own code leaves you confused later on
(somethings not that much later!) Practice by having someone else look your
code over. In the “real world” programmers go through code reviews
with
others on their team and get to explain why the code is as it is. If you cannot
do this, it is time to re-write it and make it better. Programmers do this to
working code all the time. It is called refactoring
.