.. _getting-loopy: ############# Getting Loopy ############# .. seealso:: Text, Chapter 5 Loops are a general tool used any time there is some task to do over and over. Your job as a programmer is to find those places in your problem where a loop is needed, then figure out how to set up the loop to do the job. ************** Kinds of Loops ************** Python, and most modern programming languages, has several different kinds of loops you can use. Why more than one is just a matter of convenience. You can get away with fewer, in fact only one, but life is easier when we have options. The basic kinds of loops we will study are these: While loop ========== While some condition is true, do something. In Python, this looks like this: .. code-block:: python while x > 0: # do something here The something we want to do is any sequence of Python statements indented as shown in the comment above. That sequence is called the loop body. The flow chart diagram for this looks like this: .. image:: while-loop.png :align: center Of all the loops we will study, this is the one I would keep if I had to choose. Another name for this kind of loop is the ``pre-test loop`` because we test the condition before we process the loop body. It may be that we never process the loop body, if the condition comes up ``False`` the first time we test: .. code-block:: python num = 5 while num > 5: print(num) We will not see any output from this sequence since the condition is initially ``False``. Do Until loop ============= The next loop is similar, but waits to ask the question until after you do the task. This one is not used as much, and Python does not provide a loop of this kind. This kind of loop is also called the ``post-test loop`` because the condition is evaluated after the loop body is processed. The minimum number of times we will process the loop body is once, since we do that before ever checking the condition. Counted loop ============ Both of the above loops run until something happens to stop them. If you think about it, that something must happen inside the loop so the condition we are checking causes the loop to stop. The counted loop, does something exactly the right number of times. The basic Python counted loop looks like this: .. code-block:: python for count in range(1,10): print(count) The only annoying thing about this loop is how that ``range`` function works. Formally, it produces a list of numbers starting with the first number and going up to ``but not including`` the last number. So we see this: .. code-block:: text >>> for count in range(1,10): ... print(count) ... 1 2 3 4 5 6 7 8 9 >>> .. note:: We have not studied the ``list`` in Python yet, so for now, just think of it this way. ``count`` is a ``loop variable`` that is automatically set to a value from the sequence of values produced by ``range`` each time the loop is processed. On the first time through the loop, the value is 1, on the second pass, the value is 2 and so on. Counted loops are used most often when you know exactly how many times you want the loop body to be processed. If that number of times is not known, we probably need to use the ``while`` loop above. ************************** Learning when to use loops ************************** The big challenge new programmers face is seeing a loop as part of the solution. Each problem has its own issues, bu the key thing you need to see is that you are doing some set of things over and over. Let's look at a simple example. I need to add up a bunch of numbers the user will type in from the console. Is that a place for a loop. Absolutely! As stated, I do not even know how many numbers I might need to add up, and even if I did know the number, it is still a looping opportunity. To see this more clearly, we need to think about how we would add up the numbers manually. Summing by hand =============== If you were given this problem to do by hand, you would probably write down the entire list of numbers on a piece of paper and start adding all of the digits in the ``ones`` column, writing down the result and copying the ``carry`` into the next column. You know the drill. This does not seem like a loop at all, but it can be recast as one if we think about how we would do it with a calculator! What you do now is first, clear the ``accumulator`` (the number on the display). You enter the first number, then hit the plus key. You see the first number on the display. That number is actually the starting value (0) plus the number you just entered. Hitting the ``plus`` key tells the calculator to add in whatever number you type in next. Keep doing the sequence (enter number, then hit the plus key) until all numbers have been entered, then hit the ``equals`` key to see the final result. All those steps were pretty much identical, with a few exceptions, so we are actually doing the same thing multiple times. We can translate this process into Python code as follows. First we need to create a variable that will hold the sum. Let's call it ``sum`` for convenience. The initial value for this variable should be zero, since we have not added in anything yet. (This is the same step as clearing the calculator display!). Now, our loop involves getting a new number and adding it in. Let's try this: .. code-block:: python sum = 0 new_number = int(input("Enter the next number")) Well, durn. I wanted to use a loop, but I am stuck. How am I going to control the loop? I need to be able to say NO!, I do not want to enter another number. The solution to this dilemma is to figure out a possible number that you never expect to enter, and ask the user to enter than number to stop the process of adding in new numbers. (If you know how many numbers to add, this will not be necessary, as we shall see in a bit. Let's assume that we only want to add up positive numbers (perhaps including zero). We could ask the user to enter a negative number to stop the loop: .. code-block:: python sum = 0 new_number = int(input("enter a number (negative to quit)")) while new_number >= 0: sum = sum + new_number So far we have our first number added in, but running this loop will not be a good idea. We never get another number since the input function was called outside of the loop. We need to get another number. The solution is to get that next number by repeating the input line, but this time ``inside`` the loop body. Like this: .. code-block:: python sum = 0 new_number = int(input("enter a number (negative to quit)")) while new_number >= 0: sum = sum + new_number new_number = int(input("enter a number (negative to quit)")) We need to see the final sum, so we need one more statement, this one to print out the final value for ``sum``. That one is not in the loop, so we do not indent it. Here is the final code: .. code-block:: python sum = 0 new_number = int(input("enter a number (negative to quit)")) while new_number >= 0: sum = sum + new_number new_number = int(input("enter a number (negative to quit)")) print("The sum of the input numbers is", sum) Here is a sample run: .. code-block:: text Enter a number (negative to quit)10 Enter a number (negative to quit)20 Enter a number (negative to quit)30 Enter a number (negative to quit)40 Enter a number (negative to quit)-10 The sum of the input numbers is 100 Looks like it works. Summing a known number of numbers ================================= If we know how many numbers we want to add up, the code is easier. We will use a ``for loop`` to do this one: .. code-block:: python for count in range(1,6): new_number = int(input("Enter value",count) This loop just asks for exactly five numbers (why?). It does no work. Let's add those numbers up by using some of the logic we used before: .. code-block:: python sum = 0 for count in range(1,6): new_number = int(input("Enter value",count) sum = sum + new_number print("The total of the numbers input is",sum) See how this one works. We did not need to start off by reading a number, then checking it since we knew exactly how many numbers we would input. Replacing the for loop with a while loop ======================================== We have seen this problem before, time to look at how you should have done it. The ``for loop`` sets up a counter and gives it an initial value. That value is the lower number we set up in the range. The ``range`` generates a sequence of numbers as we saw earlier. We want our ``while loop`` to process code exactly the same number of times, with a new value in loop each pass. Here is how that might be done: .. code-block:: python count = 1 while count < 6: print(count) count = count + 1 What do you expect to see? The answer should be the same set of numbers we saw earlier. In this code, we needed to generate the right sequence of values for ``count`` ourselves, and we did this by adding one each pass. Make sure you see that this does the job! .. note:: I will leave it as an exercise to add in the rest of the summing logic to add up the same set of numbers. ************** Infinite loops ************** There is an interesting kind of loop that never stops! Sounds kind of useless, but it turns out to be handy in some cases. Here is what it looks like: .. code-block:: python count = 1 while True: print(count) count += 1 .. note:: Whoa! What was that last line? Python lets you be lazy (remember, good programmers are lazy) any time you want to add something to a variable and put it back in the same variable. The ``+=`` notation is just a short form for ``count = count + 1``. We can also do these: * count += 2 # add two each pass * count /= 2 # divide the value in half each time Neat! (I bet you can think up a few more variations) Back to our infinite loop, running this example will never end. Try it and see what happens. Eventually, the number might get so bit that Python will pitch a fit, but that will take a while. You can press ``Ctrl-C`` on the PC to stop the program. If all else fails, close the console window! Escaping from this prison ========================= What do you do when you wanr to escape from prison? Break out, of course (NO!, I do not condone such behavior!) Python has a nice escape statement that will get you out of any loop you are in at the moment. Here is what it looks like: .. code-block:: python count = 1 while True: print(count) if count > 10: break print("I am free!!") The ``break`` statement will only be executed when the value in the ``count`` variable gets to a value of 11. The ``break`` will exit the infinite loop and we will continue processing on the statement after the loop. *************** Combining loops *************** We cah have loops inside other loops with no problems, just indent properly and you are good! Suppose you want to print out that old multiplication table from elementary school. Here is some code that might do the job: .. literalinclude:: code/multiplication.py The new stuff at the end of the print function forces Python to add a space after each thing it prints, but not to add a newline. The result is that the numbers have spaces between them, and we do not generate a newline until we want one. I want one after each row is complete, so I add a print statement after displaying a complete row, Running this code give this: .. code-block:: text 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100 Well, that is close to what I want to see, but it is ugly. Can't we do something about the way things do not line up? The answer is yes, but we need a trick we have not studied yet: .. literalinclude:: code/multiplication1.py See that funny stuff in the print statement. The ``"%3d"`` says print something here that will be a decimal number three characters wide. The ``% val`` right after that says to use the current value stored in the ``val`` variable as the thing to display. Before we added this code, Python printed things any way it chose to, and we got ugly, but readable output. Here is what we get now: .. code-block:: text . 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100 .. note:: ignore that funny dot at the beginning, I had to put that in to force my note system to display the output correctly. We will learn more about all this later in the course. One last try: .. literalinclude:: code/multiplication2.py This has logic in it that is trying to display the header row and column to complete the table. .. code-block:: text ; 1 2 3 4 5 6 7 8 9 10 ________________________________________ 1 | 1 2 3 4 5 6 7 8 9 10 2 | 2 4 6 8 10 12 14 16 18 20 3 | 3 6 9 12 15 18 21 24 27 30 4 | 4 8 12 16 20 24 28 32 36 40 5 | 5 10 15 20 25 30 35 40 45 50 6 | 6 12 18 24 30 36 42 48 54 60 7 | 7 14 21 28 35 42 49 56 63 70 8 | 8 16 24 32 40 48 56 64 72 80 9 | 9 18 27 36 45 54 63 72 81 90 10 | 10 20 30 40 50 60 70 80 90 100 Looks more like what we expect, right?