TEACH POPCOURSE11 - The rest of POP-11

Contents


Beyond the POPCOURSE subset of POP-11

All the teach files in the `POPCOURSE' series up to this point have used a small subset of POP-11. Using this subset you will be able to write virtually any program you want. If you have had any difficulty getting through those files, or if you really feel that you are not interested in acquiring a more advanced programming ability, then you might want to finish the course here. (This applies particularly to AI1 students.) On the other hand, if you have found the preceding files relatively easy and wish to get a broader understanding of the possibilities of POP-11 and Poplog then you should carry on to the end of the course. The material in this file provides information on advanced features of POP-11. Using this material you will be able to write programs more easily and more efficiently. You will also be better able to understand other people's POP-11 programs since it is quite unlikely that these will be restricted to the POPCOURSE subset.


Printing values

Throughout the earlier files we used a single command to print out values. This was the `==>', known as the `pretty-print arrow'. POP-11 provides various other ways of printing values. There is, for instance, the ordinary print arrow `=>'. This works in much the same was as the `==>' except that it does not attempt to print out lists neatly. Thus

[foo [bang bong bang bish box] [doobey dooley dongley] [ding
bong bonce brinks [nog nag]]] ==>

** [foo [bang bong bang bish box]
        [doobey dooley dongley]
        [ding bong bonce brinks [nog nag]]]
but

[foo [bang bong bang bish box] [doobey dooley dongley] [ding
bong bonce brinks [nog nag]]] =>
** [foo [bang bong bang bish box] [doobey dooley dongley] [ding bong
bonce brinks [nog nag]]]
We can print out values without the initial two asterisks using the `npr' procedure.

npr([This is a list]);
[This is a list]
We can print things out without POP-11 trying to put them onto a new line using the `pr' procedure.

pr([This is a list]); pr([This is another list]);
[This is a list][This is another list]
Using the `ppr' procedure we can print out lists without the enclosing list brackets or the preceding double-asterisk.

ppr([Look - no list brackets]);

Look - no list brackets
See HELP PRINT for an overview of other online files that deal with printing.


Variable initialization

The local variables that are used in procedures very often require initial values if they are to be used successfully. Variables that are to contain lists may have to be initialized to the value `[]'. Variables that are to contain numbers may have to be initialized to the value `0'. And so on. These initializations can be done by explicit assignments in the body of the procedure. They can also be done within the `vars' command itself. This involves following the variable name with an `=' and then a value.

define get_numerals(n) -> list;
   vars i, list = [], numerals = [one two three four five six];
   for i from 1 to n do
      [^^list ^(numerals(i))] -> list;
   endfor;
enddefine;

get_numerals(3) ==>
** [one two three]
See HELP VARS for further details.


Lexical variables

The variables that are set up by the `vars' command are referred to as `dynamic' variables. They are called dynamic because they come into existence while the procedure is actually running and then `disappear' immediately afterwards. POP-11 also lets you set up what are called `static' or `lexical' variables. These are only `in existence' for within the procedure where the variable is set up, (or the whole file if the variable is set up outside a procedure). Thus

vars baz;

define foo;
   vars dbong = 2;
   lvars lbong = 3;
   baz();
enddefine;

define baz;
   dbong ==>
   lbong ==>
enddefine;

foo();
** 2
** <undef lbong>
The value of `lbong' is not accessible inside the procedure `baz' because it is a lexical variable only accessible within `foo'.

HELP LEXICAL provides further information on `lvars'.


The `dlocal' command

In most programs it is essential to use some global variables. Typically these will have normal values. It may be necessary to set up procedures that temporarily alter these normal values and then later reset them. This sort of `localising' effect is most easily implemented using the `vars' command. When we set up a local variable, POP-11 ensures that its initial value will be its global value. We can then change the value of the variable within the body of the procedure in the knowledge that POP-11 will automatically reset its global value when the procedure finishes. Thus

vars foo = 3;

define baz;
   vars foo;
   foo ==>
   5 -> foo;
   foo ==>
enddefine;

baz();
** 3
** 5

foo ==>
** 3
Problems can arise when we are not sure whether the global variable we want to localize is a dynamic variable (set up with `vars`) or a lexical variable (set up with `lvars'). In this situation we should use the `dlocal' command to explicitly localize the variables. This command works out whether the relevant variable is dynamic or lexical and acts accordingly.

vars foo = 3;
lvars baz = 2;

define boo;
   dlocal foo = 1, baz = 2;
   foo ==>
   baz ==>
enddefine;

boo();
** 1
** 2
For clarity, it is best to use `dlocal' whenever the aim is to localize a global variable. See HELP DLOCAL.


Other loop constructs

In all the POPCOURSE files preceding this one we used a single looping command: the `for' loop with an integer loop variable. However, POP-11 actually provides a variety of looping commands. It also allows the programmer to use a `for' loop in a number of different ways. The main variants of the `for' command are illustrated in the following examples.

for x in [one two three] do x ==> endfor;

** one
** two
** three
Here we have a loop variable that gets set to the next element in the list each time around the loop.

for x on [one two three] do x ==> endfor;

** [one two three]
** [two three]
** [three]
Here the loop variable is set to the next tail of the list each time around the loop.

 for x to 5 do x ==> endfor;

** 1
** 2
** 3
** 4
** 5
Here we have omitted the initial integer in the range. The default initial is `1' so the effect is as shown.

for x from 10 by -3 to -4 do x ==> endfor;

 ** 10
 ** 7
 ** 4
 ** 1
 ** -2
Here we have specified a range of values for the loop variable that begins at 10 and then goes down in steps of 4 to the value -4.

All these variants are described in HELP FOR.


The `repeat' and `until' commands

In addition to the variants of the `for' command, we also have some other loop constructs. These all follow the usual convention that the closing word of the command is just the opening word with `end' tacked on the front.

The `repeat' command is used to repeat a certain sequence of commands a certain number of times. Thus

repeat 4 times "hello" ==> endrepeat;

** hello
** hello
** hello
** hello
We can also use the `repeat' command to repeat a sequence of commands an infinite number of times. We do this by leaving out the `times' part in the command.

repeat "hello" ==> endrepeat;

(This one goes on printing forever. Be prepared to
hit the INTERRUPT key if you execute it.)
See HELP REPEAT.

As an alternative to the while command we can use an `until' command. This continues to loop until the value of the provided expression is not `<false>'.

vars n = 1;
until n > 5 do n + 1 ->> n ==> enduntil;

** 2
** 3
** 4
** 5
** 6
See HELP UNTIL.


Loop control commands: quitloop, nextloop et al.

Many of the control commands provided by POP-11 are used in conjunction with loops. The `quitloop' command can be used to jump out of the current loop. Thus

for x in [1 2 3 4 5] do
   if x = 4 then quitloop endif;
   x ==>
endfor;

** 1
** 2
** 3
There is also a `nextloop' command that causes control to jump back to the beginning of the next loop. Thus

for x in [1 2 3 4 5] do
   if x = 4 then nextloop endif;
   x ==>
endfor;

** 1
** 2
** 3
** 5
There are conditional versions of both these commands. These are called `quitif' and `nextif'. They are always followed by a boolean expression in a pair of round brackets. The implement the relevant jump if the value of the expression is not `<false>'. Thus we might have written the previous example

for x in [1 2 3 4 5] do
   nextif(x = 4);
   x ==>
endfor;
All the loop control commands can be followed by an extra pair of round brackets containing a whole number. This causes control to jump to the relevant enclosing loop. Thus

for x in [1 2 3] do
   for y in [a b c] do
      nextif(y = "b")(2);
      [^x ^y] ==>
   endfor;
endfor;

** [1 a]
** [2 a]
** [3 a]
For a more extensive treatment of control commands see TEACH CONTROL.


The truth about truth values

Many commands in POP-11 are conditional commands. The obvious example is the `if' command. When we use this command we normally place an expression between the `if' and the `then' that returns a truth value: <true> or <false>. In fact, for the purposes of conditional commands, POP-11 treats any value that is not explicitly <false> as if it was <true>. This feature can be useful in certain situations. For example imagine that we want to print out the result of applying `lmember' (see above) to certain arguments) only if that value is a list. We can do it like this.

if (lmember(3, [1 2 3 4 5]) ->> list) then
   list ==>
endif;

Commands that use the matcher

The POP-11 `matches' command is very powerful. In some situations it is useful to be able to exploit the side-effects (i.e., variable instantiations) produced by `matches' without actually testing for a match. This usually occurs because we want to use `matches' to dig out several chunks of a list at once.

If we know that a `matches' command will produce <true>, and all we are interested in is the side-effect that would be produced, we can use the --> command instead of matches. This is called the `matcher arrow'. As an illustration, consider the following rewrite of the code that works out accumulated costs from the phone_calls database.

vars name duration rate entry;
[] -> costs_list;
for n from 1 to 8 do
   phone_calls(n) --> [?name ?duration ?rate];
   add_costs(name, duration * rate);
endfor;

costs_list ==>
** [[Jill 1.8] [Bob 1.75] [Fred 6.9]]
Here, the matcher arrow is used to dig out the three components of each entry in the phone-calls database, and put them in special variables. This somewhat simplifies the code. Rather than seeing complex subscripting commands in the mathematical expression we see meaningful variable names. This makes it much easier to see what the code is doing.

Another useful packaging of the `matches' command is the `isin' operator. This is a boolean operator like `matches' itself but it tests whether a particular list pattern is in a list containing many sublists. Thus

[= ?x 3] isin [[a b c][one two three][1 2 3][foo bang][zog]] ==>
** <true>
As a side-effect, `isin' assigns the sublist that matched the pattern to the built-in variable `it'. So

it ==>
** [1 2 3]
See also HELP ISIN.

In addition to --> and `isin' we have a number of looping commands that use the matcher. Particularly useful is the `foreach' command. This is rather like the `for' command when used with a list. However, rather than taking a loop variable it takes a pattern. It executes one cycle of the loop for each time it is able to match the pattern against an element of the list. During that cycle, the element that matched is assigned to the variable `it'. An example illustrating this is as follows.

foreach [== ?word ?num] in [[uno one 1][due two 2][three 3][four 4]] do
   [The numeral for ^word is ^num] ==>
endforeach;

** [The numeral for one is 1]
** [The numeral for two is 2]
** [The numeral for three is 3]
** [The numeral for four is 4]
See also HELP FOREACH.

In addition to the facilities we have already looked at there is a whole package of matcher-based commands that work with a list called `database'. These are fully dealt with in TEACH DATABASE.


Using procedures as data-objects: maplist, applist and closures

Procedures in POP-11 are actually just data-objects like lists and words. You can do everything with them that you can do with any other data object. Unfortunately, printing them out is not very informative. POP-11 just prints the name of the procedure and a label that tells you the object is a procedure; e.g.

member ==>
** <procedure member>
We can assign procedures to variables. Thus

vars pdr = member;
pdr ==>
** <procedure member>
We can also pass them into other procedures as arguments. In some situations this can be rather convenient. The built-in procedures `maplist' and `applist' exploit this facility directly. `maplist' takes a procedure and a list and it constructs a new list by applying the procedure to every item in the list. Thus

maplist([1 2 3 4], sqrt) ==>
** [1.0 1.41421 1.73205 2.0]
The procedure `applist' works in a very similar manner except that it does not attempt to collect up the values into a list. Thus

applist([1 2 3 4], npr);
1
2
3
4
The files HELP APPLIST and HELP MAPLIST provide additional information.

POP-11 also provides syntax that enables us to construct a new procedure by `freezing' certain arguments to an existing procedure. This is best illustrated using the `member' procedure. If we want to write a procedure that decides whether a particular object is an animal we might do it like this.

vars animals = [rat elephant dog cat snake horse];

define isanimal(thing) -> result;
   member(thing, animals) -> result;
enddefine;
As an alternative we can construct what is called a `closure' on member that freezes in a particular list argument. We do this by typing the procedure name followed by a pair of round brackets enclosing a paird of percents enclosing the argument we want to freeze-in. Thus

vars isanimal = member(%animals%);

isanimal("rat") ==>
** <true>

isanimal("house") ==>
** <false>
See HELP CLOSURES.


The POP-11 stack

So far I have been careful to differentiate between commands that produce values (i.e., expressions) and commands that do not produce values (statements). This distinction is certainly an important one. However, there is a story behind it relating to something called the `stack'. This needs to be carefully understood by anyone wanting a complete understanding of POP-11.

Let me illustrate the job that the stack does with an example. Consider the following code.

vars x y z;
1; 2; 3 -> x -> y -> z;
What is the value of `z' likely to be? The code looks rather strange at first sight. There is a fairly straightforward assignment in the middle (`3 -> x') but the rest is difficult to comprehend.

To correct read the code above we have to understand that when a value is produced by some POP-11 it is placed on the stack. The stack is just what it says it is. It is like a stack of dinner trays. With stacks of dinner trays you can only get trays on and off the stack in certain ways. You can push a tray on to the top. Or you can pull a tray off the top. The stack either pops up or gets pushed down. You cannot extract a tray from the middle of the stack.

The POP-11 stack is just like this. Every bit of code that produces a value effectively pushes that value onto the stack. Every assignment arrow takes a value off the stack and puts it into a variable (or some other updatable structure, see below). Thus the code

1; 2; 3;
pushes three values onto the stack. The code

-> x -> y -> z;
takes three values in succession off the stack. The first gets assigned to `x', the second to y and the third to `z'.


The double-headed assignment arrow

There is a version of the assignment arrow that assigns a copy of whatever is at the top of the stack without removing it from the stack. This can be used to assign one value to several variables. Thus

vars w1, w2, w3;
"nothing" ->> w1 ->> w2 -> w3;
We use the double-headed assignment arrow `->>' here to assign a copy in the first two cases.

The double-headed assignments are particular useful when used in conjunction with procedures that return some value or <false> if they are unable to exit successfully. An example of such a procedure is `lmember'. This is a built-in variant of the `member' procedure. It takes an item and a list and checks whether the item is a member of the list. If it is, the procedure returns that part of the list that begins with the item. if it is not, the procedure returns <false>. Thus

lmember("foo", [bang ding foo zog ting]) ==>
** [foo zog ting]
But

lmember("foo", [bang ding zog ting]) ==>
** <false>
Imagine that we have stored a list of names and ages in a list, like so.

vars ages;
[fred 23 julie 34 ann 22 gerald 12] -> ages;
We can then write a procedure called `get_age' that works like this.

define get_age(person) -> age;
   if (lmember(person, ages) ->> result) then
      result(2) -> age;
   endif;
enddefine;
Now

get_age("ann") ==>

** 22

get_age("julie") ==>

** 34
and

get_age("chris") ==>

** <undef age>
The task of explaining why we get this result here is set as an exercise below.


Input variables and the stack

All values that are passed into or out of a procedure go via the stack. Consider the following definition of a procedure called `silly'.

define silly(foo, ding) -> baz;
   foo -> baz;
enddefine;
When we call this procedure we pass in two arguments, thus.

silly(2, 3) ==>
** 2
What really happens when we do this is that POP-11 puts all the input values on the stack, one after the other working right to left. It then calls the procedure, which pops the values off the stack and assigns them to the relevant input variables. This has the advantage that procedures can be defined so as to be able to take optional arguments. The built-in procedure `delete' is a case in point. This usually takes two arguments: an item and a list. It then returns the list with all instances of the item deleted. Thus

delete(1, [2 1 3 1 4 1 5 1 6]) ==>
** [2 3 4 5 6]
However, we can also call it giving it an extra number argument at the end. If we give it 2 as final argument, the procedure will delete no more than 2 occurrences of the item. Thus

delete(1, [2 1 3 1 4 1 5 1 6], 2) ==>
** [2 3 4 1 5 1 6]
When we define a procedure specifing explicit input variables then we are effectively telling POP-11 to set up the procedure so that it automatically takes values off the stack and assigns them to the corresponding input variables. If we leave out the input variables then POP-11 will not do anything. We can make the procedure then collect the input arguments explicitly. For example, we might define our own version of delete like so.

define new_delete -> newlist;
   vars i, item, list, num, n;
   -> list;
   if isnumber(list) then list -> num; -> list else 1 -> num; endif;
   -> item;
   1 -> n;
   [] -> newlist;
   for i from 1 to length(list) do
      if list(i) /= item
      or n > num then
         [^^newlist ^(list(i))] -> newlist;
      endif;
      if list(i) = item then n + 1 -> n endif;
   endfor;
enddefine;


new_delete(1, [2 3 1 4 1 5 1 6], 2) ==>
** [2 3 4 5 1 6]

Output variables and the stack

Output variables in POP-11 work rather like input variables. When we define a procedure with an explicit output variable this effectively tells POP-11 to set the procedure up so that, when it gets to the end of its sequence of commands, it puts the value of the relevant variable on the stack. One of the implications of this is that procedures can explicitly leave result values on the stack, even when they do not have explicit output variable. Thus

define good(x) -> y;
   x -> y;
enddefine;
is the same as

define naughty(x);
   x;
enddefine;
In fact, if you think about it, this has the same effect as

define naughty;
enddefine;
This version of the procedure does not even take its input argument off the stack. Therefore it is still there as a `result' when the procedure has finished.

Different programmers have different preferences about how procedures should return values. A common view is that the method that uses an explicit output variable is much better and should be used in all cases. However, you should be aware that the other methods are possible.

Another consequence of the fact that the values returned by procedures are effectively just values left on the stack is that procedures can return more than one result. This is demonstrated by the following.

define silly;
   1;
   2;
   3;
enddefine;

silly() -> x -> y -> z;
[^x ^y ^z] ==>
** [3 2 1]
This sort of practice is deplorable since it leads to procedure definitions that are very difficult to read. However, there are situations where it is very useful to be able to return more than one value from a procedure. This can be achieved in a clean and respectable way by having multiple output variables. A version of the previous definition that uses multiple output variables is as follows.

define silly -> a -> b -> c;
   1 -> c;
   2 -> b;
   3 -> a;
enddefine;

silly() -> x -> y -> z;
[^x ^y ^z] ==>
** [3 2 1]
This may look unduly complicated. However, when dealing with procedures containing large amounts of code, the use of explicit output variables is essential.

The fact that the stack plays such a central role in procedure calls and returns is one of the reasons why a very common mishap message in POP-11 is `STACK EMPTY'. This occurs when some bit of code attempts to take a value of the stack that is not actually there.


Using the stack in list-generation

The POP-11 stack can be exploited in a variety of ways. For example, consider the task of constructing a list containing all the whole numbers between 1 and 100. There are too many numbers to type in explicitly so we have to use POP-11 to somehow generate the list. One way to do the job would be like this.

vars i, list;
[] -> list;
for i from 1 to 100 do
   [^^list ^i] -> list;
endfor;
We can also use a `for' loop that simply leaves all the number values on the stack. We can effectively collect these together into a list by using a hat in conjunction with a pair of round brackets.

[^(for i from 1 to 100 do i endfor)] -> list;

list ==>

** [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
93 94 95 96 97 98 99 100]
For more on the stack see TEACH STACK.


Other data types: strings, vectors and properties

In all the examples we have looked at so far, we have just used three basic types of data object: numbers, lists and words. You can do almost anything you want using these three types. However, for some purposes it is useful to have access to alternative types of object. POP-11 provides a wide variety of such data-types. The ones described below need to be carefully understood before proceeding on to subsequent TEACH files in the `POPCOURSE' series.

When dealing with the various different types of data-object it is necessary to keep in mind the distinction between objects that are identically equal and objects that are just equal. We can test whether one value is exactly the same object as another value by using the test for identical equality `=='. Thus

vars x = [1 2 3], y = [1 2 3];
x = y ==>
** <true>
but

x == y ==>
** <false>
and

x == x ==>
** <true>
In initializing `x' and `y' we used list constructor brackets. Thus we constructed two different lists with the same contents. In returning `<false>' the `==' is showing us that the two list are not the same object even though they contain the same components.

See also HELP EQUAL.


Strings

A very important type of data-object in POP-11 is the `string'. A string is a little like a word. However, whereas there are strict rules concerning the characters that can go to make a word, there are no such rules concerning strings. Strings can contain arbitrary characters. Strings, like words, are constructed using a pair of quote marks. However, to construct a string we use the single quote rather than a double quote. A example of a string is

'This is a string that contain funny characters like &^%$#@())!]]]'
If we print out this object, it comes out just the way we typed it in only without the quotes.

** This is a string that contain funny characters like &^%$#@())!]]]
We can apply subscripts to strings in the normal way. Thus

'a string'(4) ==>
** 116
Strings differ from words in the fact that they are always unique data objects. Words are effectively references into a central dictionary. Thus if we construct two words both containing the characters `abc' they will be identical objects. This is because they are both references to the same entry in the central word dictionary. Not so with strings. Each time we construct a string we construct a totally new object. Thus

vars w1 = "foo", w2 = "foo", s1 = 'foo', s2 = 'foo';
w1 == w2 ==>
** <true>
s1 == s2 ==>
** <false>
but

s1 = s2 ==>
** <true>
See HELP STRINGS.


The string concatenator

A very useful operator in POP-11 is the string concatenator `><'. This takes any two objects and produces a string that represents the concantenation of the `look' of the objects, i.e., the way they look when printed out. Thus

[1 2 3] >< "word1" >< 2 >< 'foobang' ==>

** [1 2 3]word12foobang

Vectors

Vectors are sequences of values just like lists. However, they have a more compact internal representation, which means they take up less space in the machine. On the other hand, they are not so flexible and cannot be used with the matcher. Vectors, like lists, are constructed using a pair of brackets. However, we use the curly brackets {} to construct vectors rather than the square brackets. Thus

vars vec = {one two three};
vec(2) ==>
** two
The hat symbols work in the usual way when used inside vectors. So

{the value of vec is ^vec} ==>
** {the value of vec is {one two three}}
See HELP TWIDDLYBRA.


Properties

Another useful data-type is the `property' or `association table'. This is an object that records associations between arbitrary POP-11 objects. The easiest way to construct a property table is to use the built-in `newassoc' procedure. This takes a single list as argument. Each sublist of the list contains two objects that are to be associated together within the table. To get the value of a particular association we simply `subscript' it, giving it as argument the object in question rather than a numeric subscript. The use of properties can be illustrated as follows.

vars num_table = newassoc([[one 1][two 2][three 3]]);
num_table("three") ==>
** 3
The default value is <false>. So

num_table("five") ==>
** <false>
We can update the table by assigning to the relevant `subscript'. So

5 -> num_table("five");

num_table("five") ==>
** 5
In order to discover the value associated with a particular object in the table, we have to provide that exact object. It is not enough to provide an object that is equal to it. This can cause problems when using properties involving strings:

vars num_table = newassoc([['one' 1]]);

num_table('one') ==>
** <false>
The problem here is that the string we have constructed in the `subscripting' operation is a different object to the one we constructed when setting up the table.

See also HELP PROPS.


And there's more

We have now covered some of those features of POP-11 that were not contained within the subset used in the initial part of the course. There is still a great deal more to be discovered about the language. The following is a short list of features you might like to explore.


Exercises

To test and expand your understanding of the material in this file, you should do the following exercises. Copy them into a file of your own and then edit in your answers underneath each question. If possible get your tutor or a friend to check your answers.

  1. Explain why the call `get_age(chris)' returns `<undef age>' in the example above.

  2. Write a procedure called `two' that tests to see whether a particular list contains two instances of the same sequence (where a sequence is assumed to contain at least two elements). If there is such a duplicated sequence, the procedure should return it. Otherwise the procedure should return `<false>'. The behaviour should be like this.

    two([one two three four five one two three six]) ==>
    
    ** [one two]
    
    two([seven one two three four five two three six]) ==>
    
    ** [two three]
    
    two([seven two one three four five two three six]) ==>
    
    ** <false>
    
  3. Set up a database list in which each sublist represents the fact that some particular person lives in a particular place. Then write a procedure that takes a place-name and builds a list (using a `foreach' loop) of all the people that live in that place.

  4. Write a recursive procedure called `count_down' that given an integer as argument counts down from that integer to 0 e.g.

    count_down(4);
    ** 4
    ** 3
    ** 2
    ** 1
    ** 0
    
  5. By adjusting `count_down' make a new procedure `count_down_up' that first counts down to 0 and up from 0 to the given number.

  6. Write a recursive procedure named `intersect' that takes two lists as arguments and returns another list which contains only those elements that occur in both the input lists. If there are no such elements the procedure should return [ ]. e.g.

    intersect([tom dick harry],[mary susan jane]) ==>
    ** []
    intersect([a b c d e f],[e f g a]) ==>
    ** [a e f]
    
  7. Write a recursive procedure named `check' that takes an arbitrarily deeply nested list as its argument. The procedure should return <true> if there is any integer anywhere in any of the lists or sub-lists and otherwise return <false>. e.g.

    check([a [b c [[e f]]g] h i]) =>
    ** <false>
    check([a [b c [[e f]]5] h i]) =>
    ** <true>
    
  8. Adjust your answer to the previous question to produce a recursive procedure named `censor'. It should take the same arguments as `check' but rather than return simply <true> or <false>, it should return the nested list that it was given with any integers replaced by the word `censored'. e.g.

    check([a [b c [[e f]]g] h i]) => ** [a [b c [[e f]] g] h i]) check([a [b c [[99 f]]5] h i]) => ** [a [b c [[censored f]] censored] h i]


Moving on

If you have completed all the exercises satisfactorily you are now ready to move on to TEACH POPCOURSE12.


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