TEACH POPCOURSE7 - List processing using iteration

Contents


Iteration using `for'

We have already seen that POP-11 provides a basic looping construct in the form of the `repeat' command. This command is easy to use but in many situations, there are alternative loop constructs which will be more convenient. A particularly useful command in POP-11 is the `for' loop. Like the `repeat' command, the `for' has the effect of executing a particular sequence of commands a certain number of times, However, unlike `repeat', it lets you set up a special variable which is automatically incremented each time around the loop.

A simple application of a `for' command is as follows.

vars n;
for n from 1 to 10 do
   n ==>
endfor;
This prints out

** 1
** 2
** 3
** 4
** 5
** 6
** 7
** 8
** 9
** 10
The `n' here is called the `loop variable'. The line `for n from 1 to 10 do' instructs POP-11 to assign 1 to n and then execute the commands between the `do' and `endfor' 10 times, adding 1 to n each time around the loop. The general effect is that the n variable always contains the number of the current cycle, i.e., it effectively counts the number of times the loop has executed.

In this example, there is only command between the `do' and `endfor' and this just prints the value of n. Hence we get all the numbers between 1 and 10 printed out on successive lines. Of course, we do not have to use `n'. We can use any variable we like. And we can also use any range of integers we like. Thus

vars bang;
for bang from 4 to 7 do
   bang ==>
endfor;
** 4
** 5
** 6
** 7
Experiment: write a `for' command that prints out all the numbers between 3 and 11. Then write a `for' loop that prints out the `squares' of all the numbers between 4 and 8. A square of a number is just the number multiplied by itself. The square of 3 is the value of `3 * 3'.

We can have one `for' command inside another. In fact we can put any combination of commands inside the loop. The following code for example uses two `for' loops---one inside the other---to print out lists containing all pairs of whole numbers between 1 and 3 that sum to more than 2.

for n from 1 to 3 do
   for m from 1 to 3 do
      if n + m > 2 then
         [^n ^m] ==>
      endif;
   endfor;
endfor;
Note the use of indentation here to show how one command is contained within another.

The `for' command is particularly useful when used in conjunction with a list. If we want to do something to every element of a list, all we need is a `for' command whose loop variable steps through all the positions in the list. For example, imagine we set up a list as follows.

vars message;
[beam me up scottie] -> message;
We could print out each element of the list using the following `for' command.

for position from 1 to 4 do
   message(position) ==>
endfor;
The printing produced by this is as follows.

** beam
** me
** up
** scottie
Note that there are other versions of the `for' command that achieve this effect more directly. See the POPCOURSE file on `The rest of POP-11'.)

Experiment: construct a `for' command that prints out the `square' of every value in the list [3 46 87 32 0 2], where the square of a value is just the value multiplied by itself.

We can also use a `for' command to add up a list of numbers. To do this we first set up a variable (initialized to 0) that will serve as a place to store a `running total'. We then use a `for' command with a `position' variable to iterate over the list, adding in each element to our running total.

vars numbers, total;
0 -> total;
[345 1235 76 21 3] -> numbers;

for position from 1 to 5 do
   total + numbers(position) -> total;
endfor;
Once we have executed this code the value of total is as follows.

total ==>
**  1680
Experiment: modify the code above so that it initializes the `total' variable to the value 10000 and then subtracts each number in the list from it. The final value of `total' should be 8320.


Using `if' commands with `else' and `elseif'

`if' commands can include `elseif' sections. In fact, they can include an arbitrary number of such sections. `elseif' sections are like `else' sections except that they have a condition, just like the initial `if' part. An example of an `if' command with two `elseif' sections is

if 4 < 3 then
   1 ==>
elseif 5 = 6 then
   2 ==>
elseif 2 > 1 and 3 > 2 then
   3 ==>
else
   0 ==>
endif;
This causes

** 3
to be printed out. As noted, you can have as many `elseif' sections as you like but they have to go after the main `if' and before the `else' section (if there is one).

(For practice, write POP-11 code to assign the value of the expression `23 * 78 / 4 + 6' to a variable called `num'. Then write an `if' command that prints out 1, if the value of `num' is greater than 1000 or less than 100, or 2 if the value is greater than 750 or less than 250, or 3 otherwise. Mark all the code and load it. The value printed out should be 3.)


Iteration using `while'

In addition to the `for' command, POP-11 provides another looping command that is useful in some situations. This is the `while' command. This command repeatedly executes some set of commands until the value of a particular expression is <false>. An example application of a `while' command is as follows.

vars n;
9999 -> n;

while n > 1 do
   n / 2 -> n;
   n ==>
endwhile;
This executes all the commands between the `do' and the `endwhile' until such time as the value of the expression between the `while' and the `do' is <false> (which is POP-11 for `no'). Executing the `while' command above prints

** 4999.5
** 2499.75
** 1249.88
** 624.938
** 312.469
** 156.234
** 78.1172
** 39.0586
** 19.5293
** 9.76465
** 4.88232
** 2.44116
** 1.22058
** 0.610291
Note how each time we go around the loop, the value of `n' is halved. Eventually it falls below 1. This causes the value of `n > 1' to become <false> which causes the looping to be terminated.

Experiment: write a `while' command that keeps doubling and printing out the value of `n' (initialized to 1) until its value is greater than 100.


Tip of the day

If you want output to come out in the current file, give the command `output .'.


Exercises

To test your knowledge of the material presented in this file you should do the following exercises. First, copy them into a file of your own. Then, edit in answers underneath each question (copying in any relevant output from your output file) and print out the file. Don't forget to annotate any code you write with explanatory comments.

  1. Write a `for' command that uses square-brackets, hats, double-hats etc. to reconstruct in reverse order a list of four elements stored in the variable `words'. (Hint: you may find it helpful to have another variable `revwords' (initialized to the empty list) in which to build up the reversed list.)

  2. Write a procedure called `spread' which takes a list and produces a copy of the list in which every element of the original list is followed by a 1. It should behave like this

    spread([foo bang ding dong]) ==>
    ** [foo 1 bang 1 ding 1 dong 1]
    
  3. Write a procedure called `merge' that takes two lists of the same length and interleaves them. When called like this

    merge([a b c d], [1 2 3 4]) ==>
    
    the value printed should be

    ** [a 1 b 2 c 3 d 4]
    
  4. Write a procedure, called `allbefore' that takes a list and a word and then constructs and returns a list of all the elements that precede the given word. When called like this

    allbefore([the man opened the door], "opened") ==>
    
    POP-11 should print

    ** [the man]
    
  5. Write a procedure called `all_pairs' that produces a list of sublists such that each sublist contains a possible pairing from the two lists provided as argument. The value printed by

    all_pairs([1 2 3], [a b d]) ==>
    
    should be

    ** [[1 a] [1 b] [1 d] [2 a] [2 b] [2 d] [3 a] [3 b] [3 d]]
    
    Hint: try using two `for' commands, one nested inside the other.

    1. Write a procedure called `prune' which takes a list as argument and produces a list as its result. The new list should contain the same elements as the original one, but with all redundancies removed . Thus

      prune([A B 1 2 C 2 1 B A]) ==>

      should produce

      ** [A B 1 2 C].
      
    2. Use the built-in `member' procedure to write a procedure called `issubset' which takes two lists and returns <true> if every element of the first list is a member of the second, but <false> otherwise.

    3. Write a procedure called `larger' which takes two lists as arguments and produces the result <true> if the first has more distinct elements than the second, otherwise <false>, thus:

      larger([a a a],[b c])=> ** <false> larger([1 2 3],[a b a b])=> ** <true>


Quick reference

The commands introduced in this file are as follows.

for    steps a variable through range, doing commands each time around
while  repeats some commands while an expression has value `<true>'

Moving on

The next file in the series is TEACH POPCOURSE8.


Page created on: Fri Apr 26 09:34:57 BST 2002
Feedback to Chris Thornton
hits this year