.he 'NUMBERS''Page %'
.fo 'Aaron Sloman'- % -'9th May, 1977'
.sp 3
.ce 5
POP11 PROGRAMMING WITH LISTS
----- ----------- ---- -----

A mini-project involving counting.
- ------------ --------- ---------
.sp
.ce2
Introduction
.br
------------
.sp
Conversation with a three and a half-year old child:
.nf
Me:     Can you count up to twenty?
Child:  One two three four five six seven eight nine ten eleven twelve
 	thirteen fourteen fifteen sixteen eighteen twenty.
Me:     What comes after three?
Child:  One two three four - four.
Me:     What comes after eight?
Child:  Four.
Me:     What comes before six?
Child:  Don't know.
Me:     How many fingers are on my hand?
Child: (counting fingers): One two three four five.
.fi
Does this child understand what numbers are?
Doing the exercises which follow should give you some insight into
the tremendous intellectual achievements of children as they learn to
use number names.

What follows is best read sitting at a teletype where you can try out
the exercises. If you have already learnt to use the editor, you should
store your function definitions in a file.
This will save you a lot of trouble.

Before doing the following you may find it useful to work through some
of the basic POP11 ideas in the "crib sheets" available in the computer
room.

One aim of the following exercises is to introduce you to techniques of
testing your programs, and tracking down reasons why they go wrong. They
often will go wrong!
.bb
1)\ Suppose we want to design a program which mimicks a few aspects of a child
learning to count.
We can create a list of the number names known so far. This list will be
used for counting, and as new number names are learnt the list will grow.
So:
 	: VARS NUMBERS;
 	: [ONE TWO THREE FOUR] -> NUMBERS;

Now define a function which will print out every element of a list. This can
be used to define a function COUNT which will print out all the known
numbers.
Suppose we call the function to print out every element of a list PRLIST.
We can plan a definition for it thus:
 	plan for:		PRLIST
 	arguments:		L
 	action:			HD(L) =>
 	recurse:		PRLIST(TL(L));
.br
The printarrow, "=>", prints the HD of the list L.

.tp5
The line labelled "recurse" expresses the idea that after you've done
whatever is needed to the first element of L, the HD, you then can
start again, using the same function on the rest of L, i.e. the TL of L.

We can translate this plan into POP11 as follows:
.tp 5
 	: FUNCTION PRLIST (L);
 	:    HD(L) =>		;;; Print first element of L
 	:    PRLIST(TL(L));	;;; restart with rest of L
 	: END;
.sp
You can test this function by typing:
 	: PRLIST([CAT DOG PIG])
.br
or	: PRLIST(NUMBERS)
.br
It doesn't work!  If you have trouble understanding why, type
 	: TRACE PRLIST;
.br
and try again.  Calls of the function PRLIST will then be "traced" by
the POP11 system
printing out
 	>PRLIST
.br
followed by the argument.
.sp
You'll see that just before the error occurs L has the value [].
There is a "bug" in the program because our plan did not take account of
what happens to the list when it is empty.
In that case neither HD nor TL can be applied to the
list without generating an error.
.sp
We can try to form a better plan by putting in a test to see whether
the list is []. As the function restarts, or recurses, with the TL of L,
after printing each item, the list it is working on gets smaller and
smaller, until only the empty list [] is left.
At this point further recursion must be prevented. So we need to include a
"stopping test" in our plan.For example:
 	plan for:		PRLIST
 	arguments:		L
 	stopping test:		L = []
 	action if L = []	"nothing --don't recurse, that's all".
 	main action:		HD(L) =>
 	recurse:		PRLIST(TL(L))

Try to remove the "bug" in PRLIST by including a test for an argument
which is the empty list, i.e. [], thus:
 	: FUNCTION PRLIST (L);
 	:	IF	L /= []
 	:	THEN	<print first element of L>;
 	:		<continue with rest of L>
 	:	CLOSE
 	: END;

What should go in place of the bits of English in the above schema? Try testing your new version of
PRLIST as above.
NB - the 'operation` "/=" means "not equal to", so the following are equivalent:
 	: IF X = Y THEN ELSE "DIFFERENT" => CLOSE;
 	: IF X /= Y THEN "DIFFERENT" => CLOSE;
.br
Neither of these two instructions does anything if the condition 'X\ =\ Y` is TRUE.
.sp10

The function COUNT can now be defined:
 	: FUNCTION COUNT;
 	:   PRLIST(NUMBERS)
 	: END;
.br
so typing
 	: COUNT();

will cause all the known numbers to be printed out.
Notice that, in PRLIST, L is a local variable, whereas, in COUNT,
NUMBERS is a global (or non-local) variable. We shall continue to
use NUMBERS as a global variable in several functions below, although 
in general the use of global variables can be risky.

Try executing the function COUNT with and without tracing PRLIST.
Try assigning different lists to NUMBERS and see what difference it makes.
.sp
Try rewriting  PRLIST to use the function PPR instead of the printarrow, "=>".
That is, instead of 'HD(L)\ =>` have 'PPR(HD(L));`
The function PPR ("PrettyPRint") prints a space before each word.
.br
You may want to alter PRLIST so that if given an empty list, [], it prints
a newline (using the command 'PPR(NEWLINE)`, that is:
 	: FUNCTION PRLIST(L);
 	:	IF	L = []
 	:	THEN	PPR(NEWLINE)
 	:	ELSE	PPR(<code for first element>);
 	:		PRLIST(<code for remaining elements>)
 	:	CLOSE
 	: END;
.br
.bb
2)\ We can tell the program about a new number by asking it to change the
list NUMBERS.
What will the following do?
 	: NUMBERS <> [FIVE] -> NUMBERS;
 	: COUNT();

Define a function NEWNUMBER which takes a one element list (like [SIX])
and concatenates it onto the end of NUMBERS:

 	: FUNCTION NEWNUMBER (NUM);
 	:	... <> ... -> NUMBERS;
 	: END;

What should go in place of the dots?
What will the following do:
 	: NEWNUMBER([SIX]);
 	: COUNT();
 	: NEWNUMBER([SEVEN]);
 	: COUNT();
 	: NUMBERS =>
.bb
3)\ Define a function called COUNTTO which takes a number name as
argument and prints out all the elements of the list NUMBERS up to and
including the argument.
E.g. COUNTTO("THREE"); should print out ONE, TWO THREE.
Here is one way to think of this task. Use a local variable, say LIST, to
"point" to the numbers not yet counted. So initially LIST will have the
same value as NUMBERS. After that it will have the same value as TL(NUMBERS)
then TL(TL(NUMBERS)), and so on: i.e. first
 	: [ONE TWO THREE FOUR...]

then
 	: [TWO THREE FOUR...]
etc.

The procedure is as follows. Look at the first element (the HD) of LIST.
Is it different from the word you are counting up to? If so, then print it
out and restart with the rest of LIST (i.e. its TL).
If the first element of LIST is the same as the target word, then just
print it out and stop: you've completed the task.
How can we tell this to POP11?

Suppose we start by trying to formulate a precise plan, thus:
 	plan for:		PRLISTTO
 	arguments:		TARGET,LIST
 	stopping test:		HD(LIST) = TARGET
 	stopping action:	HD(LIST) =>
 	main action:		HD(LIST) =>
 	recurse:		PRLISTTO(TARGET, TL(LIST))

This says that we shall start by
defining a function called PRLISTTO which takes
two arguments called TARGET and LIST, the former being a word and the
latter a list of words. It should print out elements of LIST until the
TARGET is found. We can then use PRLISTTO to define the desired
function COUNTTO.
E.g

.tp 6
 	: FUNCTION PRLISTTO(TARGET,LIST);
 	:	IF	HD(LIST) = ...
 	:	THEN	... =>
 	:	ELSE	... =>
 	:		PRLISTTO(TARGET, TL(...))
 	:	CLOSE
 	: END;
.br

What should go in place of the dots?
Test the function PRLISTTO by typing:
 	: PRLISTTO("CAT", [DOG PIG CAT MOUSE]);
 	: PRLISTTO("TWO", NUMBERS);

Type TRACE PRLISTTO; then try again. Then UNTRACE PRLISTTO;

You should also test your functions with the empty list [] and other
"special" cases. E.g.:

 	: PRLISTTO("CAT", []);
 	: PRLISTTO("CAT", [DOG PIG]);

What could be done about the errors? Dont worry about them for now.
Dealing properly with the errors would require using two different
stopping tests, with different actions.


Could you redefine PRLISTTO using UNLESS instead of IF? What else
would need to be changed.
Consider the following 'schema`:
 	: FUNCTION PRLISTTO(TARGET,LIST);
 	:	HD(...) =>
 	:	UNLESS	... = ...
 	:	THEN	PRLISTTO(..., ...)
 	:	CLOSE
 	: END;
.br

Which do you think is clearest: the POP11 definition of PRLISTTO, the
plan or the original explanation in English?
Can you comment on the advantages and disadvantages of the different
formulations?

.tp 10
Now define COUNTTO in terms of this function, thus:
 	: FUNCTION COUNTTO (TARGET);
 	:	PRLISTTO(...., ....)
 	: END;
.br
What should go in place of the dots?
What will the following do:
 	: COUNTTO("FOUR");
 	: COUNTTO("SEVEN");
.br
Try it.

Try doing  TRACE PRLISTTO;  then COUNTTO("THREE");
Then UNTRACE PRLISTTO;
.bb
4)\ What happens if the list NUMBERS does not contain the target word?
e.g. COUNTTO("FRIDAY");
Redefine PRLISTTO so that if it does not find the specified target, it
goes on printing as long as there are more words in the list LIST, then
prints out a message. One way is to include the lines:
 	: IF	LIST = []
 	: THEN	'CANNOT FIND IT' =>
 	: ELSEIF ...
.br

where should this line go and what other changes will be needed to make the
definition of PRLISTTO syntactically correct? Test your solution.
Notice that you now need a plan for the function PRLIST in which
there are TWO different stopping conditions. The first stopping condition
is
LIST\ =\ []
and the action corresponding to it is the printing of the string.
The second condition is the same as the one used previously.
If you find this hard, don't worry. You can get on with the
other functions below, since most of them do not depend on this.
.bb
5)\ How about defining a function COUNTFROM which takes a number name as
argument, and searches down the list NUMBERS until it finds that name,
then starts printing. So
 	: COUNTFROM("ONE");
should have the same effect as:
 	: COUNT();
whereas
 	: COUNTFROM("THREE");
prints out THREE FOUR FIVE... etc.

You could define a function called PRLISTFROM which takes two
arguments, TARGET and LIST. If the head of LIST is the same as
TARGET then it will apply the function PRLIST to LIST (see
question 1), otherwise it tries again with the same TARGET and the
rest (TL) of LIST.
Notice that here the stopping condition of one process is the "starting
condition" for another. That is, when PRLISTFROM stops its search down the
list, PRLIST starts.
You may find it helpful to look at the definition of
function PRLISTTO. Try defining function PRLISTFROM, then test it,
e.g. by typing:
 	: PRLISTFROM("CAT", [DOG CAT PIG MOUSE]);
 	: PRLISTFROM("THREE", NUMBERS);
.sp
.tp 10
Try using TRACE on PRLISTFROM.
If you get stuck try the 'schema`:
 	: FUNCTION PRLISTFROM(TARGET,LIST);
 	:	IF	TARGET = HD(LIST)
 	:	THEN	PRLIST(...)
 	:	ELSE	PRLISTFROM(..., TL(...))
 	:	CLOSE
 	: END;
.br
Try your function on:
 	: PRLISTFROM("SIXTY",NUMBERS);
.br
Are you happy with its response?

You can now define the function COUNTFROM, thus:
 	: FUNCTION COUNTFROM (TARGET);
 	:    PRLISTFROM(TARGET, NUMBERS)
 	: END;

What should happen if the target does not occur in the list NUMBERS?
.br
Notice the difference between two different kinds of stopping tests.
One kind simply detects that the job has been done, and there's nothing
more to do, e.g. no need to search further down a list. The other
kind is a test for whether something has gone wrong, in which case it
may be necessary to interrupt the program, and perhaps print out an "error message".
.bb
6)\ Besides doing things like counting, a child has to learn to answer questions
like "what comes after three?". How could a program do this?
Define a function called SUCC which takes a single argument,
a number name, and searches down the list NUMBERS until it finds that
name, then produces as its result the NEXT name in the list.
If a function is to "produce a result" it should simply leave its result on the
stack
instead of printing it out. So the function will not use the => command.
.br
You will
find it helpful to start by defining a function called AFTER which takes
two arguments, a target and a list, and leaves on the stack the first element in the
list after the target.
You will need a stopping test which is TARGET = HD(LIST),
The stopping action will be simply to leave on the stack the next
element of LIST, i.e. HD(TL(LIST)).
Try defining the function AFTER, and test it by typing:

 	: AFTER("CAT", [DOG CAT PIG MOUSE]) =>
 	: AFTER("FOUR", NUMBERS) =>

Notice that we need to use the print-arrow (=>) in these tests as the
function AFTER is not defined so as to print anything. It merely leaves
its result on the stack. The advantage of this is that the result is
available in the computer for use by other procedures. Things printed out
on the terminal are not. You'll meet examples of this later.
If things go wrong, use TRACE to help you track down the source of
the trouble.

You can then define the function SUCC, thus:
 	: FUNCTION SUCC (NUM);
 	:    AFTER(NUM, NUMBERS)
 	: END;
 	: SUCC("TWO");
.br
should then leave the word "THREE" on the stack. So
 	: PR(SUCC("TWO"));
.br
or	: SUCC("TWO") =>

should cause THREE to be printed out.

Since SUCC leaves a result on the stack instead of printing
it, the result is available to be the argument for another function call,
e.g. another call of SUCC. So you can try things like:
 	: SUCC(SUCC("TWO")) =>
 	: SUCC(SUCC(SUCC("TWO"))) =>

What should AFTER do if it doesn't find the target in the list?
It could produce the result UNDEF, or it could print out an error
message and then do SETPOP();

What should it do if asked for the successor of the last known
number?
.bb
7)\ Have a go at defining a function PRED which takes a
number name and produces as its result the name which occurs before it in the
list NUMBERS, thus:
 	: PRED("THREE") =>
 	** TWO
.br
The easiest way is probably to define a function BEFORE which takes a word and
a list and looks to see if the word is the same as the HD of the TL of
the list, and if so returns the HD of the list as its result, otherwise it
tries again with the word and the TL of the list.
That is:
.tp6
 	: FUNCTION BEFORE(TARGET,LIST);
 	:	IF	TARGET = HD(TL(LIST))
 	:	THEN	<return the element before it>
 	:	ELSE	<carry on looking>
 	:	CLOSE
 	: END;
.br
Try defining function BEFORE, and test it by typing things like:
 	: PR(BEFORE("DOG", [PIG CAT DOG MOUSE]));
 	: BEFORE("THREE", NUMERS) =>

Then define PRED in terms of BEFORE.
What  should PRED("ONE") produce as its result?

How do you think a child learns to answer questions like "what comes before 
five"? Why is this harder for a child than answering "what comes after four"?
Try testing yourself, or a friend on letters of the alphabet,
with questions like "what comes after 'K`?", "what comes before 'M`?"
.bb
8)\ Try changing the functions defined so far so that if they ever fail to
find what they are looking for in NUMBERS they then assume that it
must be a new number to be added on to the end of the list.
You may find it useful to use "decorated list brackets" [% and %]. Everything
between the brackets is evaluated and the results made into
a list.
E.g. the following function could be useful:
 	: FUNCTION ADDNUMNAME (NAME);
 	:   NEWNUMBER( [% NAME %] )
 	: END;

then, in a function which fails to find the target in a list (how could
it decide it had failed?) you could use the instruction:
 	: ADDNUMNAME( TARGET );

If you find this exercise hard, don't worry. Try some of the later ones
instead. They don't depend on this one.

If you have learnt how to use the system function ITEMREAD, you can make
the computer ask whether it should add the missing target onto NUMBERS.
E.g.
 	: [IS %TARGET% A NUMBER?] =>
 	: IF ITEMREAD() = "YES" THEN  ADDNUMNAME(TARGET) CLOSE;
.bb
9)\ Define a function called  COUNTBACKFROM which takes a number
name and prints out, in reverse order, the names which precede it.
So COUNTBACKFROM("THREE"); should print out:
 	** THREE
 	** TWO
 	** ONE

If you don't like typing in the long names suggested here, use your own
abbreviated versions. However it is always a good idea to use meaningful
names. This will help you when you try to understand your programs later
on, and it will also make it much easier for anybody else to help you if
things go wrong.

There are many ways of defining COUNTBACKFROM.
One way is to use the function PRED which you defined
in answer to question 7.
Try it.

Another is to
modify the function PRLISTTO so that it prints things
in reverse order. Something like the following will do:

.tp 8
 	: FUNCTION PRBACKFROM(TARGET,LIST);
 	:	IF	<the first element of LIST is the same as TARGET>
 	:	THEN	HD(LIST) =>
 	:	ELSE	PRBACKFROM(..., TL(...));
 	:		<now print the first element, the others having
 				been printed in reverse order>
 	:	CLOSE
 	: END;
.br

(Use the print-arrow "=>" for printing.)
You may find this a bit difficult to understand.
Using TRACE should help to show what's going on.
The basic idea is: if you do something to every element of
the TL of a list, then
do it to the first element, then this will in effect do it to everything
in reverse order.
.br
Test your function by typing something like
 	: PRBACKFROM("CAT", [DOG PIG CAT MOUSE]);

It should print out  CAT, PIG and DOG.

Having defined PRBACKFROM you can now define the function
COUNTBACKFROM using a method similar to that used in question 5.
.bb
10)\ There is another way of doing question 9 which you might find
interesting. It involves making a list of all the numbers up to the
target, then reversing that list, then printing out the  reversed
list.  The system function REV can be applied to a list to produce a
new list with the same elements in reverse order. The function PRLIST
defined previously can be used to print it out. So how do you get the
list of names up to and including the target name?
You want a function COPYTO such that  COPYTO("THREE",NUMBERS)
will produce the list  [ONE TWO THREE].
Suppose we had a function called STACKUPTO which would take a target and
a list, and leave on the stack all elements of the list up to and including
the target word. We could then define COPYTO roughly as follows:
.tp 3
 	: FUNCTION COPYTO (TARGET, LIST);
 	:  [% STACKUPTO(TARGET, ...)  %]
 	: END;

(Fill in the dotted bit.) Notice that this, like the function
ADDNUMNAME, defined above, uses decorated list brackets to collect
things left on the stack into a list.
One way to define STACKUPTO is this:
.tp 6
 	: FUNCTION STACKUPTO (TARGET, LIST);
 	:    HD(LIST);
 	:    UNLESS HD(LIST) = TARGET THEN
 	: 	STACKUPTO(TARGET, ...)
 	:    CLOSE
 	: END;

What should the following do:
 	: STACKUPTO("PIG", [CAT DOG PIG  MOUSE])  =>
 	: STACKUPTO("FOUR", NUMBERS)  =>

(If you have trouble understanding how the stack works, please ask for
help.)

.tp5
Now use all this to define a new version of COUNTBACKFROM, in the
manner suggested. You might try not using the system function REV, and
instead defining a function REVERSE which does the same thing. But
first try using REV.
.bb
11)\ Now try defining a function called HOWMANY which takes a list of
items and "counts" them by stepping down the list and stepping down
then list NUMBERS at the same time. The last number name reached before
the first list is exhausted is the result.
Here is a first shot at defining such a function. In fact it doesn't
work. Can you see why? Try redefining it.
.tp 7
 	: FUNCTION HOWMANY(LIST);
 	:	STEPTHRU(LIST,NUMBERS)
 	: END;
 	: FUNCTION STEPTHRU(LIST,NUMS);
 	:	IF	LENGTH(LIST) = 1
 	:	THEN	HD(NUMS)
 	:	ELSE	STEPTHRU(TL(LIST),NUMS)
 	:	CLOSE
 	: END;
.br
This doesn't work. Why? (Hint: alter the recursive call of
STEPTHRU).
.bb
12)\ If you have come across "looping" instructions, like:
 	: UNTIL  <condition>  THEN <action>  CLOSE;
 	: LOOPIF <condition>  THEN   <action>  CLOSE;
.br
then you could try redefining all the above recursive functions
so that they use loops instead. (Probably UNTIL will do for all of them.)
The advantage of using loops instead of recursion is that the
functions will then run more efficiently.
The disadvantage is that it will be harder to get clear tracing information
printed out, and it will be harder to track down bugs (sometimes).
.bb
There are several more problems concerning counting, adding, counting
things out into a set, etc. See if you can think some up.

A good thing to do after this demo is to work through the ARITH demos,
ARITH0, ARITH1 and ARITH2. They explore issues about counting, adding
and visual search.
