.. _lecture6: Repetition Structure #################### .. wordcount:: .. seealso:: Text: Chapter 5 We introduced the basic structures: * Sequence * Decision * Loop Let's go over the basic idea of the loop. Repeating Work ************** Many problems you will face involve doing something to a group of things. You might be adding up checks (remember that), or processing each person in a group. Many such tasks are common in life. The basic work for each thing (person, check, etc.) is the same. These situations call for a loop in your solution. Kinds of loops ============== There are two basic kinds of loops we deal with. * Those where we know exactly how many times we will loop. * Those where we will not know, and have to figure out when to stop. Which kind were we using in our checkbook problem last week? You could count the checks before you start processing, but that is kind of silly. What if you have a lot of checks and count wrong? How will you fix that problem? Counted Loops ------------- If we know exactly how many times we need to do work, we can set up a counter variable, and use it to keep track of the number of times we go around the loop. We stop when we are done. Here is tha basic idea: .. code-block:: text set counter to zero set max_count to 10 while counter is less than max_count loop do work counter = counter + 1 endloop Look at the style of this pseudo-code. I am using very short, mostly English sentences to describe what I want to happen, without worrying exactly how this will be accomplished. I am focused on the `logic` of the loop, not the details of the work to be done. In this step, it is the loop that has my attention! .. note:: We use indentation to show the work that will happen `inside` the loop (we call this the loop body). Indenting is so common in programming, it is essential that you get used to using it in your pseudo-code. Typically, programmers indent 4 spaces, but that is not really a critical value, just so common, folks will look at you funny if you use any other indent size. We even teach our editors to automatically indent by four spaces when we hit the `tab` key. Think about this logic. Will it loop the right number of times. What would happen is I incremented the counter before doing the work. What value would you see if you printed out the counter as work was going on? All of these questions will come up in real looping problems. Make sure you see how the loop is stopped. In this case, we set up a `logical question` that checked the current value of the `counter` variable, and compared it against the `max_count` value we had set. What value will `counter` have when the loop stops? (In this case it is going to be 10 when the question gives us a false value and the loop stops.) Making sure you ask the right question is vital. What would happen if we asked the question a different way: .. code-block:: text while counter is not equal to max_count loop Now how many times would the earlier loop example do work? In this case it would stop when the value of the counter is 10 when the question is asked (before we increment it at the bottom.) How many times does it loop? Hopefully, you agree that it will still loop 10 times with values of `counter` inside the loop ranging from 0 to 9. Now try this: .. code-block:: text while counter is not greater than max_count loop Now, figure out how many times the loop does work. This is a classic "off by one" problem. The value of `counter` can be 10 and we will do work. But that gives 11 times we do work, not 10 as we wanted. Far too often, programmers make this kind of mistake, and you have to really watch out for this. Make sure you see the difference between these questions: .. code-block:: text while counter is less than 10 loop while counter is not greater than 10 loop Uncounted Loops =============== The second kind of loop is actually more difficult to deal with. We need to find a way to decide if we keep looping, or stop. The basic loop is going to get data from somewhere, then process that data in the loop. For now, we will assume that we are going to ask the user to input some data values for us to process. We might ask for one value, or ask for a group of items, then process the group in the loop body. Perhaps there is a way to use a special value for one of those input items, and use that to tell up to stop. We used a value of zero to stop processing items in our check balancing problem last time. This special value is called a "sentinel value". If we are going to ask for a number of data items, it is important to get the one that we are using as a sentinel first, so the user does not have to come up with a bunch of silly values that will not be used. As an example, Let's say we want to process each item in our checkbook balancing problem, but we want to also ask the user for the date of the item. Our `sentenel` value is an `item_amount` of zero. Here is the logic we could use: .. code-block:: text input item amount while item_amount is not equal to zero loop get item_date process item input item_amount end loop This is a common pattern for a loop that will handle as many items as we like, and will stop when we enter that "sentinel" value. Do you see how I managed to only ask for the date inside the loop, after we know the item we have entered is a good one. If we enter a value of zero for the item_amount, the user will never be asked for the date. You also need to notice that we asked for an input value `before` we started the loop, and again after we process the item. This is called a `pree-seeded` loo. We `seed` the variable we will use to control the loop before we decide if we want to process the item at all. After we process the item, we must ask for another `item_amount` which will be used when we loop back and ask the controlling question if we should process it again. Make sure you understand how the loop works. And make sure you see how it is stopped. Cal you see a way to count the number of items as we process them? Here is one way: .. code-block:: text count = 0 imput item_amount while item_amount is not equal to zero loop count = count + 1 get item_date process item input item_amount end loop output count Hopefully, you can see that the value of `count` displayed at the end of this loop is the number of items processed. The counter is not really involved in the processing, it just keeps track of how many items we actually process. Don't Repeat Yourself ********************* Beginners often ask for all the data items before entering the loop, then repeat that code at the bottom. That means there will be two blocks of code that are essentially identical. When you see this, you should stop yourself and think some more. Most likely, there is a way to restructure your code so the common logic is not repeated. If you cannot see a way to do this, we will introduce the idea of a code block, or module, that we can use to reduce the common logic to a simple name and activate the code in the block in multiple places. This is an important part of programming, so we will spend time on it later. For now, just make sure you watch for this. Finding Loops in your problems ****************************** The Square Root problem I assigned at the beginning of the course was designed to see if you could think your way to a solution, based on nothing more than your life experiences. Some of you did well, others not so well. This was expected. Was there a loop in that problem. Actually yes! What we wanted was a way "home in" on the right answer. What we were given as a starting point was some number (no, not just five, that was an example), and a way to figure out if our value for the square root was correct or not. We were not told how to start off the process, nor how to "home in". That was where the thinking came in. .. note:: Just follow along as we work out a solution to this problem. Visualize how it works, and try to see if you believe that it will give you a good answer. You will not become able to come up with things like this until you gain experience solving more and more problems. Soon, you will see similarities in how you got an answer, and use that scheme again and again! That is how we learn! Think about it ============== What does is mean to "home in" on a solution? Well, we might start off with a guess for the answer, and when we multiply our guess by itself, we will be off on either the low side, or off on the high side. Which side we are on tells us if we have to increase our guess, or decrease our guess. Knowing that will give us a way to start hunting for the right answer. Let's just start off with a scheme that increases (or decreases) our guess by one each time through the loop. We will call this value `step`. We will loop adding this step amount to our guess until the side we are on changes. How will we know that? Well, think about it some more. If we are on the low side this equation will give a positive value: .. code-block:: text number - guess * guess If we are off on the high side, that same equation will give a negative value. If we hit the right answer `exactly` the equation will give a value of exactly zero. .. warning: In computing something like a square root, we will probably never hit the exact answer except in a few special cases. More often we will get `close`. How close we need to get is something we need to figure out. Is getting an answer where we are off by no more than 0.000000001 close enough? If not adjust that value until it is close enough! How do we loop ============== The basic loop just keeps going until we get our answer, and we do not know how long that will take. For now, we are just `thinking` our way through the problem. .. code-block:: text input `number` accuracy = 0.00000001 step = 1.0 What should our first guess be. It could be anything, even the number itself. The better the initial guess the quicker we might find the answer! Let's just use half the number, since we know the square root is smaller than the number! .. code-block:: text guess = number / 2.0 error = number - guess * guess while error is greater than accuracy loop do something here end loop There is a basic problem here. `error` will be a number that is either positive or negative. We want our error to be less than `accuracy`, but any negative value for `error` will satisfy that question! We need to use the `absolute value` of the error in comparing it to the value of `accuracy`! .. code-block:: text while abs(error) is greater than accuracy loop do something here end loop .. note:: I used a common mathematical notation for finding the absolute value of some number. `abs` is the name of a `function` that can figure that out, just like `sqrt` could solve this problem for us, if we let that function be used here! We will loop initially until we step past the answer and the sign of our little equation changes. Then we need to change the step so we move in a smaller amount toward the answer that we just went past. How should we adjust the step? A simple way is to cut it in half! Then we step in the opposite direction until we step past the answer again. We then cut the step in half and move in the opposite direction again. Do you see how this works? Do you think it will "home in" on the solution? It seems like it might, but I am going to leave if for you to ponder if it will work. The basic idea is to keep cutting our step size down so it gets smaller and smaller, until we get to a step size equal to the accuracy we need. If we are that close, we can stop all this and deliver the final value as a result. Switching Direction =================== How do we know when to switch direction? The sign of the error term will change! One way is to use another variable to tell us the direction we are to move. Now this is tricky, but it might just work. Let's create a new variable called `sign` which we will give a value of positive one or negative one. If we keep our step value positive and multiply it by this `sign` variable, it will give us either a positive value or a negative value. If we add that value to our guess, it will either increase or decrease, depending on the value for sign. Hmmm, this might work! .. code-block:: text error = number - guess * guess if error is greater than zero then set sign to +1.0 (our guess is too small) else set sign to -1.0 (our guess is too big) end if while abs(error) is greater than accuracy loop guess = guess + sign * step error = number - guess * guess what do we do here? end loop Now, the loop modifies our guess and calculates the error term again. That line asking what to do here is where we need more thinking! We have a new guess and a new error term. We need to see if we have stepped over the answer and adjust our value for step here. Here is how we might do this: .. code-block:: text if error is greater than zero then (our guess was to small) if sign is less than zero then (we were subtracting a value from our guess, we were too big) set sign to -sign (this flips from +1 to -1) set step to step/2 end if else (our guess was too big) if sign is greater than zero then (we were adding a value to our guess, so we were too small) set sign to -s (flip direction) set step to step / 2 end if OK, I admit it. This block of code will make your head hurt. But it has to hurt to get the point across. You really need to ponder this logic and make sure you see how it works. The idea is to figure out if we have stepped over the answer, and only if so, we adjust the direction (`sign` variable`, and the `step` variable. If we do not do both, we will not move in the opposite direction as we add to the guess and we will not "home in". The loop will not flip back and forth past the answer, but will decrease the step value each time this happens. Eventually, the answer should be as accurate as you like. I will leave it for you to decide if this will stop as we have designed it so far! History ======= Way back in the dawn of mathematics, people had no machines to do this kind of work, and did exactly the kind of brain exercises we just did to come up with a solution. What made someone famous was thinking up a better way to get the job done, using fewer steps. Look up folks like `Taylor `_ to see what he came up with! Enough of this! *************** If you followed all of this, your head is probably aching and needs some rest. I never expected that you would come up with something like this in that assignment, and I am not claiming this is the best solution. It just seems, well "Logical" to quote Spock! As you gain more experience expanding your brain, this kind of thinking will not be so tough! Go work on your lab assignment. It is much easier!