TEACH DEFINE                                Revised A.Sloman Oct 1987

                      Defining procedures in POP-11
                      =============================

This TEACH file introduces you to some fundamental programming  concepts
concerned with defining procedures and running them, using POP-11.

It assumes that you are familiar with the editor VED, and in  particular
have worked through TEACH MARK  and TEACH LMR, so  that you know how  to
mark a range in the editor buffer  and compile it. You should also  know
how to switch between different VED files, as explained in TEACH BUFFERS
and TEACH SWITCHWINDOW.

TEACH RIVER gives a simple introduction to some of the programming ideas
presented in this file.  If you find this  file difficult, try going  to
TEACH RIVER and then come back.


A table of contents follows. Put the cursor on the section you  require,
below, then type:

    <ENTER> g <RETURN>

Redo <ENTER> g at any time to get back to the table of contents.

CONTENTS

 -- Introduction
 -- What is a procedure?
 -- Procedures need building blocks
 -- Warning messages and error messages
 -- Format for simple procedure definitions
 -- A simple example: letterhome
 -- Giving a procedure an input variable: greet(someone)
 -- What happens when the procedure -greet- is run
 -- How does POP-11 interpret the definition of the procedure?
 -- Revision questions
 -- What are local variables?
 -- So you thought you understood "->" ?
 -- Using the result of a procedure
 -- A different procedure: perim
 -- Procedures are more powerful than files
 -- Local variables again
 -- You must give a procedure the right number of arguments
 -- Defining -perim- using multiplication
 -- A more complicated example: total_perim
 -- Conclusion
 -- Further reading


-- Introduction --------------------------------------------------------

POP-11 enables  you to  express instructions  which refer  to  different
sorts of objects, including numbers, words, strings, and lists. In order
to  perform  operations   on  these   objects  POP-11   needs  to   have
"procedures". So you  need to learn  how to create  objects, and how  to
tell POP-11  to  do things  to  the objects.  Both  require the  use  of
procedures.

Some procedures are built in to  POP-11, e.g. procedures for adding  two
numbers and for building lists or examining the elements of lists. Users
can define additional procedures to suit the requirements of  particular
tasks. In order to do this you  need to learn the "syntax" of  procedure
definitions. Some simple examples were given in TEACH VEDPOP.

POP-11 provides several different kinds of procedures with many kinds of
instructions that can be  built into them.  This file introduces  only a
subset of forms that are most widely used. The "Further reading" section
at the end of this file gives references to more advanced information.


-- What is a procedure? ------------------------------------------------

A POP-11 procedure is a special kind of object. You can think of it as a
sort of list of machine instructions stored in the short term memory  of
the computer. It is a bit like a file of instructions, except that it is
not stored on the magnetic  disc, but in the  fast short term memory  of
the computer so that it can be got at very quickly.

When  you  mark  and  load  a  definition  like  the  following  example
introduced in TEACH VEDPOP:

     define test(num);
        return(num + 2);
     enddefine;

POP-11  builds  one   of  these  special   objects  containing   machine
instructions. The instructions will say, in effect:

 1. Get an object from  the "arguments/results stack" (explained  below)
    and call it -num-.
 2. Put the value of  -num-, and the number 2  on the stack and RUN  the
    procedure called "+". It will put its result back on the stack.
 3. Return (i.e. stop doing this  procedure, and return to whatever  was
    being done before, e.g. running the editor.

Later you can RUN the procedure you have defined (or CALL it, or  INVOKE
it) by marking and loading a command like:

    test(55) =>

That tells POP-11 to put the input, namely 55, in a special place  where
it can  be accessed  by the  procedure, i.e.  the "stack"  and then  the
stored instructions are run.

Revision of TEACH VEDPOP:
You can mark a portion  of this file using  the MARKLO and MARKHI  keys.
You can load (i.e.  compile) the marked range  by means of the  command:

        <ENTER> lmr

or, if your terminal is set up normally, by doing:

    CTRL-d

i.e. while holding the CTRL button down tap the D button down.

When you have marked and compiled  the procedure definition, the set  of
stored machine instructions is associated  with the name "test" so  that
you can easily  tell POP-11  which procedure  you want  obeyed. This  is
unlike some languages  where you have  to tell  the system to  GO TO  an
instruction on, say, line 3050.

To summarise:
You can think of a procedure as a set of machine instructions stored  in
the machine's short term memory.

Most procedures have names that can be used repeatedly to ask POP-11  to
RUN the procedures without  having to spell  out the instructions  every
time you need to have them obeyed.


-- Procedures need building blocks -------------------------------------

All interesting computer programs are complex procedures built up  using
simpler  procedures,  which  in  turn  are  built  from  still   simpler
procedures, and so on.

The most basic procedures available to  you are the procedures built  in
to the POP-11 system. These include procedures for:

    building lists,
    comparing lists with 'templates',
    assigning values to variables,
    adding multiplying or dividing numbers,
    storing information in a database of lists

and many more.

Before seeing how  to build up  a procedure, you  need some  familiarity
with some of the  basic building blocks. This  section explains some  of
them. Later you will see  how to put them into  a new procedure of  your
own.

The built in POP-11 procedures can  be run directly without your  having
to define new procedures. For  example, mark the following  instructions
then compile them:

  vars someone, list;                     ;;; declare two variables
  [dear mum] -> someone;                  ;;; assign a list to -someone-
  [hello ^^someone how are you] -> list;  ;;; build another list using it
  list =>                                 ;;; print out the list

The comments on  the right will  be ignored by  POP-11. Compiling  those
four lines should print out in your 'output' file:

  ** [hello dear mum how are you]

Let's look at how that all works, one line at a time.

  vars someone, list;

This tells POP-11 that you want to use -someone- and -list- as names  of
objects, i.e. as variables.  The semi-colon ";"  tells POP-11 where  the
instruction ends  (since POP-11  can have  several instructions  on  one
line, or can have one big instruction going over several lines).

  [dear mum] -> someone;

The square  brackets provide  one way  of invoking  the built-in  POP-11
procedures for  creating lists.  So [dear  mum] creates  a list  of  two
words. The arrow "->" is the assignment arrow. "-> someone;" assigns the
last object constructed to be the value of the variable -someone-.

  [hello ^^someone how are you] -> list;

As before the square brackets say  "build a list" and the arrow  assigns
it to  the variable  -list-. But  something special  goes on  here.  The
'double up-arrow'  symbol "^^"  tells  POP-11 not  to include  the  WORD
"someone" but instead to get the list which is the value of the variable
-someone- and put all the elements  of that list (i.e. the words  "dear"
and "mum") into the new list.  (TEACH ARROW gives lots more examples  of
this).

To see the difference, mark and load each of these commands in turn:

    [hello someone how are you]=>
    [hello ^^someone how is ^^someone]=>


-- Warning messages and error messages ---------------------------------

When you use  the building  blocks of  POP-11 things  will sometimes  go
wrong. Mark and load the following.

    [hello someone how are ^^you]=>

This will produce a  warning message and an  error message. The  warning
message is:

    ;;; DECLARING VARIABLE you

because ^^you is an attempt to use  the value of the variable -you-  and
it has not yet been declared by a command like:

    vars you;

The error message is something like:

    ;;; MISHAP - LIST NEEDED
    ;;; INVOLVING:  <undef you>
    ;;; DOING    :  mishap compile

This is because [.... ^^you ....] in a list requires the variable  -you-
to have a list as  its value, so that the  elements can be spliced  into
the larger list. Because no value  has been assigned to -you- its  value
is an "undef" object -- i.e. it is undefined.

If you mark the offending  line and compile it  again, you will get  the
mishap message, but not the warning message, because by now the variable
has been declared, by POP-11.

The difference between a  warning message and an  error message is  that
after a warning message POP-11 tries to continue -- e.g. it declares the
variable for you -- whereas after an error (or MISHAP) message it  stops
whatever it was doing because it is totally unable to continue.


-- Format for simple procedure definitions -----------------------------

A special format is used for telling POP-11 to define a new procedure.

(A) First the word "define" is used to say that a new procedure is being
defined.

(B) Immediately after "define" give the name of the new procedure, e.g.:

        define test

(C) After  the name  give  a pair  of  parentheses '()',  possibly  with
something in  between  if the  procedure  needs some  inputs  (sometimes
called arguments) to work on, e.g.

        define test(num);

        define silly();

(D) A  semi-colon is  required to  say that  the end  of the  "procedure
heading" has been reached. (The semi-colon is essential to indicate  the
end of the heading, because in  advanced programs more complex forms  of
heading are allowed, possibly extending over more than one line.)

(E) Then give  the instructions  to be  executed when  the procedure  is
obeyed. This is called the "body" of the procedure.

(F) The definition ends with "enddefine". (This is the closing  bracket,
to match the opening bracket "define".)

(G) Add  a  semi-colon to  tell  POP-11 that  this  is the  end  of  the
instruction to define a procedure.

I.e. the format for the simplest sort of procedure definition is:

    define <name of procedure> ( <optional argument names> ) ;
        <instructions>
    enddefine;

For example:

    define join_three(arg1, arg2, arg3);    ;;; heading
        [these arguments were supplied] =>  ;;; part of body
        [^^arg1 ^^arg2 ^^arg3] =>           ;;; rest of body
    enddefine;                              ;;; closing bracket


NOTE:
POP-11 has many kinds of matching opening and closing brackets. (This is
explained in  more  detail  in  TEACH BRACKETS).  This  is  unlike  some
languages (e.g. LISP) which use only  a few matching pairs of  brackets.
We think that  is bad because  although you have  fewer syntax words  to
learn about, it is very easy to put an opening or closing bracket in the
wrong place and then the compiler cannot give you advice as to what  the
mistake was, whereas the POP-11 compiler can give you messages like:

    FOUND endif READING TO enddefine

The following procedure definitions fail  to conform to the above  rules
for defining procedures. So if  you try to mark  and load them you  will
get error messages. Try editing them so that they no longer give  errors
when you mark and load them. First try them as they are so that you  get
used to the  error messages.  They will  not necessarily  be very  clear
messages in every case!

    define silly1 (arg1 ;
        arg1=>
    enddefine;

    define silly2
    enddefine;

    define (arg1) silly3;
    enddefine;

    define silly4 arg1,arg2)
        [^^arg1 ^^arg2] =>
    enddefine;


-- A simple example: letterhome ----------------------------------------

Here is an example, using the list-building technique shown above.  Type
the following  into a  file called  'define.p' in  which you  can  store
examples from this teach file:

    define letterhome();
        vars someone;
        [dear mum] -> someone;
        [hello ^^someone how are you]=>
    enddefine;

When you type  that into your  file, don't indent  the first line.  That
enables you to use VED facilities that tell when a procedure  definition
starts by looking  for "define" at  the beginning of  a line.  (Advanced
programs can  use "nested"  definitions which  should be  indented.  But
don't try that now.)

POP-11 is a 'lower case' language, so always make sure that you type  in
lower case, even if some of  the examples are presented in capitals  for
clarity.

Mark and compile  the definition of  -letterhome- The definition  merely
tells POP-11 that the word  'LETTERHOME' is to be the  name of a set  of
instructions to be stored in the  short term memory when the  definition
is compiled.  It  doesn't yet  tell  POP-11  to obey,  or  execute,  the
instructions.

Here's how you ask POP-11 to  execute the procedure (obey the  commands)
three times. Mark and compile the three lines.

    letterhome();
    letterhome();
    letterhome();

The procedure includes "local variable" declaration. The declaration in
the second line of the procedure, namely:

    vars someone;

(look back  at  the definition)  specifies  that the  procedure  needs a
'local variable'  called  "someone". That  is,  it needs  to  have  some
private storage, which is to be associated with the name "someone".

The assignment arrow '->', in line 3, is used to put something into that
storage area.

The remaining instructions are as explained in a previous section.


-- Giving a procedure an input variable: greet(someone) ----------------

The above  procedure  (LETTERHOME)  is  rather silly  for  a  number  of
reasons. One is that it prints out exactly the same thing every time. In
general you want the  behaviour of a procedure  to be more flexible  and
general. How it works should depend on the input it has been given.

We often refer to a procedure's input as its 'argument', or 'parameter'.
The term 'argument' is  derived from the  way mathematicians talk  about
the 'argument of a function'.

Here is a definition of a procedure a bit like -letterhome-, except that
it has one argument called "someone" and it introduces a new format, for
defining procedure outputs, in the first line.

    define greet(someone) -> greeting;
        vars someone, greeting;
        [hello ^^someone how are you today] -> greeting;
    enddefine;

In this case, the top line  of the definition specifies which  variables
are being used as names for the input and the output, namely SOMEONE and
GREETING. More  precisely SOMEONE  is used  as an  'input variable'  and
GREETING as an  'output variable'.  Sometimes these are  referred to  as
'input locals' and  'output locals'.  How these work  will be  explained
below.

Type the definition of -greet- into your file 'define.p' (making sure it
is quite separate from your definition of 'letterhome').

MARK and LOAD the procedure. You can do  this in one go if you make  the
FIRST line of the definition start  at the beginning of a line,  without
indentation, thus:

define greet(someone) -> greeting;

When you  have done  that you  can then  do the  following to  Load  the
Current Procedure.  Put  the  VED cursor  someone  in  the  definition -
anywhere will do, including  the first or last  line of the  definition.
Then do

    <ENTER> lcp <RETURN>

This means "Load Current Procedure". VED searches for the beginning  and
end of the definition then loads the whole thing without your having  to
mark it. You can do this even more quickly by typing:

    <ESC> c

I.e. press the ESC button  then the C button.  (However if VED has  been
set up for you so as to emulate another editor, e.g. EMACS, this may not
work, and you will have to  learn the appropriate command sequence  from
your tutor. <ENTER> lcp should always work, however.)

If you have  made a typing  error you may  get a mishap  message on  the
command line. Look at  the message carefully, and  see where the  cursor
has got  to in  the  file. The  error must  be  before that  point.  Try
correcting the error, and compile the procedure again.

(If you can't get it to work, be SURE to ask someone for help.)

Eventually you should  get your definition  compiled successfully  using
<ENTER> lmr or <ENTER> lcp or <ESC> c.

You can then test the procedure by  asking POP-11 to run it with a  list
as input (as argument). Type the  following in your 'output' file.  Then
mark and load the lines. (NB: <ENTER> lcp is not appropriate here as you
are not compiling a  new procedure definition. So  MARK the lines,  then
use <ENTER> lmr, or CTRL-d):

    greet([mum]) =>
    greet([silly computer]) =>
    greet([lots of rubbish]) =>

Try all that,  and variations. Look  carefully at the  round and  square
brackets. The  square  brackets are  for  making the  lists.  The  round
brackets indicate what is given as input to greet.

Notice what happens if you leave out  one or more of the brackets,  e.g.
try marking  and  loading  the  following,  errors  and  all,  and  look
carefully at the mishap messages that you get on the command line:

    greet( [mum ) =>
    greet( silly computer] ) =>
    greet [lots of rubbish]) =>

These all  produce  'compile  time'  error  messages  because  they  are
syntactically ill-formed. The second example will also produce a warning
message in your output file because POP-11 thinks you are using  'silly'
as an undeclared variable, before it realizes that there is a  syntactic
error, when it finds "]".

If you give  -greet- a number  instead of a  list, you will  get a  'run
time' error message. Try to mark and load the next line:

    greet(999) =>

    ;;; MISHAP - LIST NEEDED
    ;;; INVOLVING:  999
    ;;; DOING    :  null dl greet compile ...

It wanted a list, not a number.  Don't worry about the "DOING" line  for
now.


-- What happens when the procedure -greet- is run ----------------------

When you type, to POP-11:

        greet([uncle joe]) =>

several things happen.  The bit  in square brackets  actually gets  done
first.
        [uncle joe]

This tells POP-11 to make a list containing the two words "uncle" and
"joe". The list is then stored in a special place called the STACK.

Then POP-11 interprets 'greet ( )  ' as meaning, 'obey the  instructions
in the procedure called  "greet" (OR produce a  mishap message if  there
isn't such a procedure)'. -greet- is expected to produce a new object as
its result, which is then left on the STACK.

Finally the  'print  arrow' prints  out  what  was left  on  the  stack,
preceded by two asterisks.

You can tell that  the procedure -greet- itself  does not print out  the
result as follows. Mark and load the line:

    greet([joe]);

The result is  not printed out  because you have  not ended the  command
with the print arrow "=>". However when the procedure has finished,  VED
finds that something has been "left on  the stack" and prints it on  the
command line. If you  did not notice  that do it again  and look at  the
command line.

You can get -greet- to put three things on the stack then print them  in
your output  file  in one  go  using "=>",  if  you mark  and  load  the
following, which is three separate commands:

    greet([fred]); greet([joe]); greet([old boy])=>

Three lists will be printed in your 'output' file.


-- How does POP-11 interpret the definition of the procedure? ----------

Suppose you type to POP-11:

    greet([uncle joe])

This makes a  list [uncle joe],  and puts it  in a special  part of  the
computer's memory  called "the  stack". It  then runs  the  instructions
stored in the procedure -greet-. How does that work?

Look at the heading in the definition of the procedure -greet-.

    define greet(someone) -> greeting;

The bit before "->" says there  is an input variable called SOMEONE.  So
when the procedure -greet- runs it takes off the stack the list that was
left there for it, and uses that as the value of SOMEONE.

So when the procedure greet is run,  with the command greet([uncle joe])
this starts with the equivalent of:

    [uncle joe] -> someone;

The portion  after "->"  says that  the variable  GREETING is  to be  an
'output variable'.  The input  variable affects  what happens  when  the
procedure starts running: it takes  something off the stack. The  output
variable affects what happens when the procedure stops running: it  puts
something back on the stack, namely whatever happens to be the value  of
GREETING.

The next line of the definition:

        vars someone, greeting;

is a 'declaration' saying that SOMEONE and GREETING are local variables,
so that when  they are changed  inside -greet- they  will not  interfere
with anything else which uses variables with that name. This line is not
absolutely necessary here, because POP-11 automatically declares SOMEONE
and GREETING as  local to -greet-  because they occur  in the  procedure
heading. But  it is  a good  idea  to get  used to  declaring  temporary
variables as local.

The next line is:

        [hello ^^someone how are you today] -> greeting;

The part before "->"  tells POP-11 to build  a list, which contains  the
words between the square brackets, except that the '^^' symbol says that
the elements in  the list  called SOMEONE  should be  inserted into  the
list, rather than the  word "someone" itself. This  means that the  list
actually created is:

        [hello uncle joe how are you today]

The second half of the line is:     -> greeting;

This says that the newly created list is to be 'assigned' to become  the
new 'value' of the variable GREETING.  I.e. GREETING becomes a name  for
the list:

        [hello uncle joe how are you today]

This is its value ONLY during this run of the procedure -greet-, because
the variable GREETING is LOCAL to the procedure. This use of "->" as the
'assignment arrow'  is quite  different from  the use  in the  procedure
heading.

NB.
    In the body  of the procedure  "->" actually tells  POP-11 to do  an
    assignment.

    In the  heading  "->"  tells  POP-11  that  GREETING  is  an  output
    variable.

Finally  the  "enddefine"  is  reached,  so  that  there  are  no   more
instructions in the 'body'  of the procedure. At  that point instead  of
just finishing  off, the  procedure  checks whether  it has  an  'output
variable' and if so puts its  value on the stack. The procedure  heading
for -greet- was:

    define greet(someone) -> greeting;

So GREETING  is an  output variable,  and therefore  when the  procedure
finishes, the value  of GREETING is  put on  the stack, or,  as we  say,
-greet- 'returns' it as a result. So the list

        [hello uncle joe how are you today]

is left  on  the stack  when  the procedure  finishes.  If you  run  the
procedure with the command:

    greet([uncle joe]) =>

then the final part of this, the "print arrow" says that the result that
is left on the stack should be printed out (preceded by two  asterisks).
Hence you'll get in your 'output' file:

    ** [hello uncle joe how are you today]


Now test the procedure,  by giving it  some more inputs  as a value  for
SOMEONE,  and  try  to  relate  what  is  printed  out,  to  the   above
explanation. N.B. be  very careful  about getting the  round and  square
brackets right. Try various examples, like:

    greet ( [ stuart ] ) =>
    greet([my dear friend]) =>

POP-11 doesn't care whether  what you type makes  sense, as long as  you
give -greet- a list as input. So you can do nonsense like:

    greet([66 plus 99 is 55]) =>

You can even give it an empty list:

    greet([]) =>

Try marking and loading that.


-- Revision questions --------------------------------------------------

After running  a few  examples you  should go  back over  the  preceding
explanation, and compare it with  what you've observed. You should  make
notes on what you  have learnt so  far. You could use  VED to type  your
summary into a file called 'define.notes'. For example, type in  answers
to the following questions:

    What is a procedure?
    What is the format for a procedure definition without any input
        or output variables?
    What is the format for a definition with one input variable and
        no output variables?
    What is the format for a definition with one input variable and
        one output variable?
    How do you declare the variables as local to a procedure?
    What do the square brackets [....] do?
    What is the meaning of "^^" between square brackets?
    How do you tell POP-11 to run a procedure with a list as input
        and print out the result?
    How can you compile the "current" procedure without using
        <ENTER> lmr
    What does <ESC> c do?
    Why are you advised to type the first line of a procedure definition
        without any indentation?


-- What are local variables? -------------------------------------------

Look again at the definition.

    define greet(someone) -> greeting;
        [hello ^^someone how are you] -> greeting;
    enddefine;

The VARS line  has been  omitted because,  as explained  above it  isn't
needed for input and output  variables: they are automatically  declared
as LOCAL variables for the procedure.

What it means to say that the variables SOMEONE and GREETING are "local"
to -greet- can be illustrated by  the following. We first assign  values
to them outside  -greet- then run  -greet- so that  they get new  values
inside -greet- then check their final values:

First give them values and print them out. Mark and load the next  three
lines:

    [hi there] -> someone;
    [big boy] -> greeting;
    someone, greeting =>

That should print out two lists
    ** [hi there] [big boy]

Now run -greet-

    greet([uncle joe]) =>

This will  TEMPORARILY  give SOMEONE  the  value [uncle  joe]  and  then
GREETING the value:

    [hello uncle joe how are you today]

and the latter should be printed out. But if you now re-do:

    someone, greeting =>

You'll see that their values have been restored to what they were before
-greet- ran. This is true of all LOCAL variables: when the procedure  to
which they  are local  has finished  you cannot  get at  the values  the
variables had while  the procedure was  running. This enables  different
procedures to have the same local  variables and not interfere with  one
another. So you can define procedures which use variable names that  you
find convenient, without worrying about whether that will interfere with
something else.

Interference can  occur  where variables  are  used without  being  made
local, so that should be avoided wherever possible.

In fact, it makes no difference at all to POP-11 what names you use  for
your inputs, outputs and other local variables, as long as you use  them
consistently and you don't try to  use words like "define" that  already
have a special meaning to the system. As far as POP-11 is concerned, the
procedure defined by:

    define greet(person) -> salutation;
        [hello ^^person how are you] -> salutation;
    enddefine;

is identical in every way with the procedure -greet- defined above.

You will probably find  all this a  bit confusing at  first. You can  go
back over it  using the SCREENUP  and SCREENDOWN buttons.  (Or else  use
<ENTER> g to go  back to the  table of contents  and see which  sections
you'd like to re-read.)


-- So you thought you understood "->" ? --------------------------------

It is very important  to notice that  the use of  "->" in the  procedure
header is different  from its use  inside the procedure,  on the  second
line. In the header:

    define greet(someone) -> greeting;

The bit '-> GREETING' does not  tell POP-11 to  assign  anything to  the
variable GREETING. Rather it is a 'dummy instruction' which tells POP-11
that you are going  to include an instruction  somewhere in the  program
which will assign some object  to GREETING, and whatever value  GREETING
has when the procedure finishes (reaches "enddefine"), is to be  treated
as the 'result' of the procedure. I.e. it is to be left on the  'stack',
a part of the computer memory reserved for temporarily storing arguments
and results of procedures.

In the next line:

        [hello ^someone how are you] -> greeting;

the last bit  '-> GREETING' is  a real instruction  to POP-11 to  assign
something to GREETING.


-- Using the result of a procedure -------------------------------------

When a procedure produces a result (left on the stack), you can do other
things with it besides printing it out. For instance, you can  associate
the result with a  new name. That  would be a way  of getting the  value
into GREETING outside the procedure, e.g.:

    greet([mum]) -> greeting;
    greeting =>

However, GREETING outside the procedure has nothing to do with  GREETING
inside the  procedure. Thus,  if  you run  the procedure  again,  with a
different input, the external value of GREETING is unchanged:

    [hi there] -> greeting;
    greeting =>
    greet([fido])=>
    greeting =>

unless you assign a new value to the external GREETING, as in:

    [hi there] -> greeting;
    greeting =>
    greet([fido]) -> greeting;
    greeting =>

Often when you have  defined a procedure it  is a useful building  block
that can be used for constructing other procedures. The file:

    TEACH RESPOND

shows how you can use a  procedure something like -greet- as a  building
block in constructing a larger procedure that simulates a  non-directive
therapist!


-- A different procedure: perim ----------------------------------------

So far we have described only procedures with at most one input.  POP-11
allows you to define procedures with  more than one input. For  example,
suppose you need to work out  the perimeter of a rectangular room  given
its length and its breadth. If the  length is 15 feet and the  breadth 9
feet then you can work out the perimeter thus:

    15 + 9 + 15 + 9 =>

Given dimensions of  another room, e.g.  12 by  6, you can  do the  same
again:

    12 + 6 + 12 + 6 =>

If you frequently have to do this  sort of thing it is useful to  have a
procedure that takes in  the two numbers and  produces the perimeter  as
its result. See if you can define a procedure called 'perim'. It  should
take two  inputs  and  produce  one result,  so  the  heading  could  be
something like:

    define perim (side1, side2) -> total;

(We can't use 'length' as a variable because that is already the name of
a built in POP-11 procedure, and  the result will be a MISHAP  message.)
Try to complete that  definition in your  file called 'define.p'  before
you read on. Then mark it and compile it (or use <ENTER> lcp, or <ESC> c
to compile  it.)  If  you  get  a mishap  message  try  to  correct  the
definition and  try  again.  When  you  have  managed  to  compile  your
definition test it and  see if you get  the following results, when  you
test it in your file called 'output':

    perim(3, 5) =>
    ** 16

    perim(23, 31) =>
    ** 108

    perim(10,10) =>
    ** 40


           DON'T READ ON TILL YOU HAVE TRIED DEFINING 'PERIM'
                   AND HAVE TESTED YOUR DEFINITION


Here is a possible answer. You could have defined perim as follows:

    define perim (side1, side2) -> total;
        side1 + side2 + side1 + side2 -> total;
    enddefine;

What are the local variables for this version of -perim-?

If you  did not  get your  own version  to work,  put that  one in  your
'define.p' file and test it in your 'output' file.


-- Procedures are more powerful than files -----------------------------

In  some  ways  a  procedure  is  like  a  file  with  a  collection  of
instructions. You can  store a  set of  instructions in  a file  without
putting them into a  procedure. But by compiling  them into a  procedure
which has a  name you  not only can  execute them  repeatedly much  more
quickly than if  you frequently  have to  LOAD the  file to  to get  the
instructions done,  you can  also give  the procedure  different  inputs
(arguments), to get  it to do  slightly different things.  You saw  this
previously with -greet-, and can demonstrate it also with -perim-.

    perim(666, 99) =>
    perim(750, 532) =>


-- Local variables again -----------------------------------------------

Notice  that  although  something  is  assigned  to  TOTAL  inside   the
procedure:

    define perim (side1, side2) -> total;
        side1 + side2 + side1 + side2 -> total;
    enddefine;

you cannot get at the value of TOTAL after executing the procedure. This
is because TOTAL is a LOCAL variable of the procedure, just as  GREETING
was for the procedure -greet-. If  you need further convincing, try  the
following:

    total =>
    perim(3, 2) =>
    total =>
    perim(99, 45) =>
    total =>

So the value  of TOTAL 'outside'  the procedure is  not altered by  what
happens inside  the procedure.  The value  assigned to  TOTAL inside  is
accessible from outside  because when  the procedure  has finished,  the
value of its output local is left  on the 'stack' (a special portion  of
the machine's memory reserved  for this purpose). From  there it can  be
picked up by an assignment to a new variable. e.g.

    vars x;
    perim(99, 45) -> x;
    x=>

Try that.


-- You must give a procedure the right number of arguments -------------

It is an important fact that the procedure PERIM requires two  arguments
(inputs). This is indicated in the procedure definition by the fact that
there are two 'input variables' between the parentheses on the top line.
Look back at it. If you provide only  one input when you try to get  the
procedure obeyed, you'll get a mishap message. Try the following:

    perim(99) =>
    ;;; MISHAP - STE: STACK EMPTY (missing argument? missing result?)
    ;;; DOING    :  perim compile

PERIM tries to take two  things off the stack.  One thing is put  there,
namely 99. So when it  looks for the second thing  it can't find it  and
causes POP-11 to print the error message and abort.

You can also get an error message  if you give perim the wrong sorts  of
arguments, e.g. words or lists:

    perim("five","four") =>
    ;;; MISHAP - NUMBER(S) NEEDED
    ;;; INVOLVING:  five four
    ;;; DOING    :  + perim compile

Notice the second line gives the clue  that it found the two words,  and
the third line says it was doing "+" inside the procedure perim.


-- Defining -perim- using multiplication -------------------------------

Can you redefine PERIM yet  again, using the multiplication symbol  "*",
instead of only addition "+". Try modifying the definition in your  file
'define.p'. Replace the dots in the following with appropriate words:

    define perim (side1,side2) -> total;
        ( ... + ... ) * 2 -> total;
    enddefine;

I.e. add the two  numbers, then multiply by  2, to produce the  'total',
which will be left on the 'stack'. What should go in place of the  dots?
Try it and test the  modified procedure. If you  cannot get it to  work,
please ask for help.


-- A more complicated example: total_perim -----------------------------

Just in case you have found all  this too simple. Here is a new  example
showing how to use the procedure -perim- as a building block for a  more
complex procedure, along with POP-11  constructs that have not yet  been
introduced, but will hopefully be clear from the context.

Suppose you have to work out how  much wallpaper is needed for a set  of
rectangular rooms all of the  same height. You are  given a list of  the
lengths and breadths of the rooms as follows.

    vars roomsizes=[ [10 8] [9 7] [20 15]];

TASK:
Define a procedure called total_needed which  is to take a list in  that
form, except that it  can have more  than three room  sizes. It is  also
given the height (the same for all rooms), and the width of the roll  of
wall-paper. Work  out how  much wallpaper  is required,  i.e. the  total
length. (For  now ignore  the  problem of  cutting strips  of  wallpaper
narrower than the width of the roll.)

SUBTASK:
First define a procedure called total_perim which takes the list of room
sizes and works out the total perimeter.

    define total_perim(list) -> total;
        vars list, room, x, y, total=0;
        for room in list do
            room --> [?x ?y];
            perim(x, y) + total -> total;
        endfor
    enddefine;

Compile then test this with the command:

    total_perim(roomsizes)=>

Total_perim uses -perim- as a sub-procedure, and can itself be used as a
sub-procedure in -total_needed-. Thus:

    define total_needed(rooms, height, paper_width) -> len;
        vars rooms, height, paper_width, len;
        total_perim(rooms) * height / paper_width -> len
    enddefine;

Then test it, for rooms 8 feet high and wallpaper 2.5 feet wide.

    total_needed(roomsizes, 8, 2.5) =>

Do the calculations  by hand  to check that  the result  printed out  is
correct!

You can use TRACE  to see what  is going on,  with a different  example.
Mark and load the next line:

    trace total_needed, total_perim, perim;

Find out the total length  of wallpaper needed if  the rooms are 9  feet
high and the width of the wallpaper is 3 feet:

    total_needed(roomsizes, 9, 3) =>
    >total_needed [[10 8] [9 7] [20 15]] 9 3    ;;; starting total_needed
    !>total_perim [[10 8] [9 7] [20 15]]        ;;; starting total_perim
    !!>perim 10 8                               ;;; perim for room 10 by 8
    !!<perim 36                                 ;;; finished perim
    !!>perim 9 7                                ;;; starting perim again
    !!<perim 32                                 ;;; finished perim again
    !!>perim 20 15
    !!<perim 70
    !<total_perim 138                           ;;; finish total_perim
    <total_needed 414                           ;;; finish total_needed
    ** 414

So we need 414 feet of wall paper (approximately)

You can do it again without tracing if you first UNTRACE the procedures.

Mark and load the next two lines:

    untrace total_needed, total_perim, perim;

    total_needed(roomsizes, 9, 3) =>
    ** 414


-- Conclusion ----------------------------------------------------------

You should try making notes on  all the things you've learnt. Make  sure
you know the answers  to the following questions.  (Look back over  this
file, and if you can't find the answers, ask for help.)

1. If you are reading a help file and it tells you to define a procedure
    using VED, how do you tell VED you want to edit the procedure?

2. How do you then get POP to compile the procedure?

3. How do you test a procedure?

4. How do you get back to where you were in reading the TEACH file?

5. If you can't  remember part of  the TEACH file, how  do you move  the
    cursor back to read it again?

6. What is an output local variable?

7. What is an argument for a procedure?

8. When a procedure is executed which has local variables, what  happens
    to their values when the procedure has finished?

9. What  is  the  difference  between  an   EXPLICIT  and  an   IMPLICIT
    declaration of a variable as local to a procedure?

10. What is the stack used for? (See TEACH STACK for revision on this)

11. Why is it  useful if after a  procedure has finished, the  variables
    which it uses as locals, are  reset to their previous value?
    (See  TEACH VARS for more on this).


-- Further reading -----------------------------------------------------

The information presented here is elaborated in:

    R.Barrett, A.Ramsay and A.Sloman
    POP-11: A practical language for artificial intelligence

in chapters  1, 2,  3 and  6,  with some  more advanced  information  in
chapters 11 and 14.

Online information accessible via the editor
--------------------------------------------

The TEACH  files  mentioned  below  have  their  names  preceded  by  an
asterisk. This  is  because  VED  includes  a  short-cut  mechanism  for
accessing a teach or help file: type <ESC> n to move the cursor to the
next asterisk, and repeat until the cursor is just before the name  of
the file you want. Then type <ESC> h, and the TEACH file (or HELP  file)
will be read in. If you want to go back to a previous asterisk, use
<ESC> N instead.

TEACH * PROCEDURES
    elaborates on some of the ideas presented here.

TEACH * VARS
    explains more about local and global variables.

The following TEACH files provide extra practice and explanations:
TEACH * VARS, * ARROW, * MATCHES, * STACK

TEACH * RIVER and TEACH * RESPOND introduce mini-projects giving  more
practice.

TEACH * TEACHFILES
    An overview of teach files available.

HELP * DOCUMENTATION
    This tells you about  various kinds of  documentation in Poplog  and
    how to find it.

For more advanced readers:
--------------------------

The variables introduced  so far are  "dynamically" scoped. POP-11  also
supports lexically scoped variables.
    See HELP *LEXICAL and HELP *LVARS

HELP   * DEFINE
    summarises the  information  presented  in  this  file,  in  a  form
    suitable for more experienced users.

REF   * PROCEDURE
    gives an advanced overview of Poplog procedures.

REF   * POPSYNTAX
    gives  a  more  complete  summary  of  POP-11  syntax,  but  is  not
    recommended for beginners.

REF   * STACK
    gives an advanced overview of  the stack used for passing  arguments
    and results of procedures

--- C.all/teach/define
--- Copyright University of Sussex 1990. All rights reserved. ----------
