next up previous contents
Next: Using conditionals to Up: CHAPTER.4: PROCEDURES IN Previous: Some important constructs

Loops: instructions to do something repeatedly

Several looping constructs are provided.

For compatibility with older systems, wherever the word DO is used in the examples below, the word THEN is also accepted. Similarly most looping constructs will accept CLOSE or ENDDO as the closing bracket, for consistency with POP-2 and earlier versions of Pop-11. It is probably best to ignore these variant forms, as they may be withdrawn later.

The examples of looping constructs below are among the most widely used ones. There are others described in HELP LOOPS and HELP FOR. Moreover, because Pop-11 is an extendable language, users can define new syntax words and macros defining new constructs, as explained below. Examples are the library definitions of "foreach", "forevery", and "switchon", each of which has its own help file. In each case the library definitions can be inspected using the "showlib" command, e.g.

    ENTER showlib foreach

UNTIL <condition> DO <action> ENDUNTIL

This means, check if the <condition> is true, and if not then do the action. Then test again to see if the condition is true. And so on. The condition is tested again each time AFTER <action> is done. For example, to print out all the numbers from 3 to 99 do:

    vars num;
    3 -> num;
    until num > 99
    do    num =>
      num + 1 -> num;
    enduntil;
The <action> element of a loop (like a conditional) may be an arbitrarily complex pop-11 imperative, or sequence of imperatives. So to print out the words "THE" "CAT" "SAT" "ON" "THE" "MAT", you could make a list of the words, then keep printing out elements of the list. We use the system procedure TL which, when given a list, returns a new list containing all except the first element of the original. chop one off. until the list is [], thus:

    vars list;
    [the cat sat on the mat ]  -> list;
    until list == []
    do
        list(1) =>                  ;;; print first element
        tl(list) -> list;           ;;; prepare for next
    enduntil;
NB the loop will not be terminated immediately if the condition becomes TRUE in the middle of executing the action. Note also that the condition is always tested at least once BEFORE anything else is done.

REPEAT <number> TIMES <action> ENDREPEAT

After the word REPEAT you can have a number, e.g. 4, or a variable whose value is a number, e.g. REPEAT N TIMES... or a more complex expression which calculates a number,

      e.g. repeat 66 + 53 times....   endrepeat;
The <action> will be done the specified number of times.

Example: to print out 10 blank lines, do

    repeat 10 times pr(newline) endrepeat;
Make a list of 10 randomly generate numbers between 1 and 20

    [% repeat 10 times random(20) endrepeat %] =>
    ** [12 15 19 18 9 5 4 11 17 19]
See also the definition of procedure SQUARE, above.

For indefinite iteration do

    REPEAT <action> ENDREPEAT;
e.g.

    repeat
        [you are wonderful] =>
    endrepeat;
You will need to interrupt (e.g. with CTRL-C).

(See HELP LOOPS for more on interrupting loops)

WHILE <condition> DO <action> ENDWHILE

This means, test the condition. If it is true, then do the action. Then test the condition again. And so on. This is similar to UNTIL, except that WHILE does the action each time the condition is found to be TRUE whereas UNTIL does the action each time the condition is found to be FALSE. In both cases the condition is tested first.

E.g. to find the first integer whose square is greater than 1000, you could do:

    vars num;
    1 -> num;
    while num * num < 1000 do
        num + 1 -> num
    endwhile;
The value of num and its square can now be printed out:

    num =>
    ** 32
    num * num =>
    ** 1024

`FOR ... ENDFOR' loops

We have seen examples of the use of the `for ... endfor' construction, to iterate over lists. In fact there are several different formats.

The following is a very general for loop, which can be used in many contexts, although a more specific version will often be preferred.

  FOR <initiate> STEP <step> TILL <condition> DO
      <action>
   ENDFOR;
This is equivalent to:

      <initiate>;
      UNTIL <condition> DO <action>; <step> ENDUNTIL;
In other words, do the initialisation, then, until the condition evaluates to TRUE, repeatedly do the action followed by the step.

For example, to print out all the numbers from LO to HI, separated by spaces:

    for   lo-> x
    step  x+1 -> x
    till  x > hi
    do    spr(x);
    endfor;
N.B. The `step' is not done until after the `action'.

Suppose FOO is a procedure of two arguments, a word and a number. If you are given a list of words and a list of numbers and wish to apply FOO to the first element of each, then the second element of each, etc., until either there are no more words, or no more numbers, you could do something like:

    vars w, n;
    for   words -> w; numbers -> n;
    step  tl(w) -> w; tl(n) -> n;
    till  w = [] or n = []
    do    foo(hd(w), hd(n));
    endfor;
________________________________________________________________

. FOR X IN LIST DO <action> ENDFOR;

X will take on as its value the first element of LIST, then the second element, then the third, etc. Each time the <action> is performed the latest value of X will be used. E.g. printing out the squares of a list of numbers

    for num in numbers do
        pr('\nThe square of: '); pr(num); pr(' is: '); pr(num*num);
    endfor;
(Note: this uses strings. `\n' in a string causes a new line to be printed.)

It is also possible to iterate over several lists at the same time, e.g.

    for x1, x2, x3, in list1, list2, list3 do
        <something involving x1, x2, x3>
    endfor;
________________________________________________________________

. FOR L ON LIST DO <action> ENDFOR;

Here, the first time the <action> is done L will refer to the whole LIST. The second time it will refer to the TAIL of the list (i.e. a list of all but the first element). The next time a still shorter list, and so on. E.g.

    for l on [a b c] do l=> endfor;
    ** [a b c]
    ** [b c]
    ** [c]
Again this can be done with several lists at once.

________________________________________________________________

. FOR X FROM <number> BY <number> TO <number> DO

FOR X FROM <number> BY <number> TO <number> DO <action> ENDFOR;

e.g. to print out numbers from 2 to 20 going up in steps of 7

    for x from 2 by 7 to 20 do  x => endfor;
    ** 2
    ** 9
    ** 16
or going down:

    for x from 50 by -12.5 to -20 do x => endfor;
    ** 50
    ** 37.5
    ** 25.0
    ** 12.5
    ** 0.0
    ** -12.5
the `FROM <number>' and `BY <number>' portions can be omitted. The starting value defaults to 1, as does the increment.

    vars x;
    for x to 6 do pr(x); pr(space) endfor;
    1 2 3 4 5 6

There are several forms of conditionals.

All the <conditions> in what follows should be expressions which evaluate to TRUE or FALSE.

(1) IF <condition> THEN <action> ENDIF;

(2) IF <condition> THEN <action1> ELSE <action2> ENDIF;
     if the <condition> is true, then <action1> will be executed,
     otherwise <action2> will be executed.

(3) IF <condition1> THEN <action1>
    ELSEIF <condition2> THEN <action2>
    ELSEIF <condition3> THEN <action3>
    ELSEUNLESS <condition4> THEN <action4>
    .............
    .............
    ELSE <default action>
    ENDIF;
This `multi-branch' conditional says:

    try <condition1> then <condition2> etc in turn until an ELSEIF
    condition is found which is TRUE, or an ELSEUNLESS condition is
    found which is FALSE. If either is found, execute the
    corresponding <action>.  If none of the conditions comes out
    TRUE then do the thing following ELSE, i.e. <default action>.
Note that in an imperative you do not have to have the ELSE <default action> bit. You must, however, have it in an expression intended to denote, something, for then it should denote something under all conditions. You can include as many ELSEIF clauses as you like.

  (4)   UNLESS <condition> THEN  <action> ENDUNLESS;
       this is equivalent to:
       IF NOT(<condition>) THEN <action> ENDIF;
UNLESS can also have ELSEUNLESS and ELSEIF and ELSE clauses.

Note: the words NOT, AND and OR are available for use in formulating complex conditions. E.g.

    IF <condition1> OR (<condition2> AND NOT(<condition3>))

. Examples of conditionals

To test whether the value of X is bigger than the value of Y, and print out the bigger value do:

    if    x > y
    then  x =>
    else  y =>
    endif;
Compare:

    if       x > y
    then     x =>
    elseif   y > x
    then     y =>
    else     "same" =>
    endif;

    if    2 < x and x < 6
    then  true
    else  false
    endif =>
Note that the last example is exactly equivalent to:

    2 < x and x < 6  =>
If LIST1 and LIST2 are two lists and you want to print out the one which is shorter you could do:

    if    length(list1) < length(list2)
    then  list1
    else  list2
    endif =>
If N and M are two numbers, and you wish to assign the bigger one to the variable MAX then do:

    if    m > n
    then  m
    else  n
    endif -> max;


next up previous contents
Next: Using conditionals to Up: CHAPTER.4: PROCEDURES IN Previous: Some important constructs



Aaron Sloman
Fri Jan 2 03:17:44 GMT 1998