From Hand Simulations to Loops


Starting with simulations

So far we have thought of loops in terms of which actions need to be repeated, and followed that up with a hand simulation. That works well in many cases. But there is another way to design a loop that works backwards, from the desired hand simulation to the loop, and that approach can help you to create loops that work correctly, with very limited debugging.

The idea is to perform a hand simulation, as you want the loop to work. Show your variables, and show their value each time the program reaches the beginning of the loop. If you need more variables, add them. Do not continue until you think you have a viable simulation of an algorithm.

Now write a loop that follows that simulation. Make sure that the values shown in the simulation are the ones that you would get from the loop.


Case study: compute factorials

Let's return to the problem of computing factorials. As an example, imagine computing 5!. We want to multiply more and more numbers together. So a start is to have a variable that holds the result, accumulating more and more of the factorial as we go. In the following hand simulation, we do not cross out values, since it is important to see what they are each time the program reaches the beginning of the loop.

r
1
2
6
24
720
Put another way, the value of r is set to larger and larger factorials.
r
1!
2!
3!
4!
5!
That is a good start; it gradually accumulates the desired value, 5!. The problem is that the program does not know where it is. It is important for it to know just which factorial is stored in r at a given moment. So let's add another variable, k, telling which factorial is stored in r.
k    r
1    1!
2    2!
3    3!
4    4!
5    5!
So you can say that, each time the program reaches the top of the loop, r holds k!. When k is 4, r holds 4! and when k is 5, r holds 5!.

So now we have the hand simulation. The next steps are to answer three questions.

  1. How should the loop control variables (k and r in this example) be initialized? It looks like k = 1 and r = 1. So the initialization will be

      Let k = 1.
      Let r = 1.
    

  2. When should the loop end? If we are trying to compute 5!, then the loop should end with k = 5, since then r will hold 5!. But if we want to computer 9!, the loop should end with k = 9. Generalizing, to compute n!, the loop should end when k = n. So the loop should keep going when kn. That tells us that the loop heading should be

      While k =/= n
    

  3. How should the loop update the control variables to get from one line to the next, on the assumption that you want to do that? The statements that you choose must work no matter which two adjacent lines you choose. For example, if k = 4 and r = 4!, then, after performing those statements, you should find that k = 5 and r = 5!. Statements that will do the job are

      Relet k = k + 1.
      Relet r = r * k.
    
    Try it out to see that, if k = 4 and r = 4! before you do those statements then k will be 5 and r will be 5! after you do them.

Putting that all together leads to the following definition of function factorial(n).
  Define
    factorial(n: Integer): Integer by

    factorial(n) = r |
      Let k = 1.
      Let r = 1.
      While k =/= n
        Relet k = k + 1.
        Relet r = r * k.
      %While  
  %Define

That probably looks like a lot of work to write a small function definition. But it is important to look at the big picture. Your goal is to write a correct program. Often, extra time spent in planning saves a lot of time later spent fixing the errors that were in the plan. I have had students come to my office telling me that they had spent hours trying to get a simple function to work. When I asked them if they had planned it out, they invariably told me that they did not consider planning worth the effort. They had spent hours of debugging time to avoid spending twenty minutes planning.


Problems

  1. [solve] Write a definition of function power(x, n) = xn where x is a real number and n is a positive integer, using a loop. But plan out the loop by first looking at the variables and their values. Have two variables k and p, and arrange for p = xk each time the program reaches the beginning of the loop.

    Write Define ... %Define around your definition.


Summary

One way to design a loop is to start with what you want the simulation to look like, and then to build a loop that follows that desired simulation.

It can help to look at each row of the simulation and ask how the variable values are related to one another. For example, in the factorial loop, the relationship was r = k!.


Review problem

  1. [solve] Write another definition of function power(x, n) = xn where x is a real number and n is a positive integer. But this time use cases and recursion instead of a loop. Write Define ... %Define around your cases.