Repetition Structure

Read time: 47 minutes (11964 words)

See also

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:

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:

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:

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:

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:

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:

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:

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.

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.

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!

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!

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!

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:

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!