.. _loop-operations: ######################### Looping to solve problems ######################### .. seealso:: 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: .. code-block:: c 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: .. code-block:: c 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: .. code-block:: c 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: .. code-block:: c 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: .. code-block:: c :linenos: 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: .. csv-table:: 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: .. code-block:: c 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! .. code-block:: c 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``.