Search                        Top                                  Index
TEACH DEFINE                                   Revised A.Sloman Oct 1987
                                                     Revised 26 Oct 2011

                      Defining procedures in Pop11
                      =============================

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

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. There are more simple examples involving
programming with numbers, in TEACH ARITH


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 Pop11 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 --------------------------------------------------------

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

Some procedures are built in to  Pop11, 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 are given in TEACH VEDPOP.

Pop11 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 Pop11 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;

Pop11  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 Pop11 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 Pop11  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 Pop11  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 Pop11 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 Pop11 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  Pop11. 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 Pop11 that you want to use -someone- and -list- as names  of
objects, i.e. as variables.  The semi-colon ";"  tells Pop11 where  the
instruction ends  (since Pop11  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  Pop11
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  Pop11 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  Pop11 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 this:

    ;;; MISHAP - LIST NEEDED
    ;;; INVOLVING:  <undef you>
    ;;; FILE     :  /usr/local/poplog/current-poplog/pop/teach/define   LINE NUMBER:  207
    ;;; DOING    :  mishap runproc

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 Pop11.

The difference between a  warning message and an  error message is  that
after a warning message Pop11 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 Pop11 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()

        define isbetween(num1, num2, num3)

If your procedure is to produce a result, that can be shown using "->"
after the second parenthesis, e.g.

        define test(num) -> result

        define isbetween(num1, num2, num3) -> true_or_false

If a  procedure  uses  a result  variable,  then  at least  one  of  the
instructions in  the  procedure  must  assign a  value  to  the  result.
Otherwise the  result produced  will be  meaningless. Examples  will  be
given below.

Some procedures return two or more results, which can be shown in
parentheses, e.g.

    define solve_problem(question1, information) -> (result, explanation)

    that takes two inputs, and produces two outputs. Often it is a
    good idea to use names for inputs and outputs that indicate
    their roles.

If there are two or more output variables each one must be assigned to
inside the procedure body.

(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.)

Examples based on the above:

        define test(num);

        define silly();

        define isbetween(num1, num2, num3);

        define test(num) -> result;

        define isbetween(num1, num2, num3) -> true_or_false;

        define solve_problem(question1, information)
                -> (result, explanation);

(As in the last example, a procedure header can be split across two or
more lines.)

(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  Pop11 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;

or with output variables:

    define <name of procedure> ( <optional argument names> ) -> (<names of results>);
        <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


You can mark and compile that, and test it thus:

    join_three([a], [b c], [d e f]);

which should print out

    ** [these arguments were supplied]
    ** [a b c d e f]

Another example, with a result variable:

    define mirror(list) -> palindrome;
        ;;; take a list of words and produce a new list containing
        ;;; those words followed by the words reversed.

        [ ^^list ^^(rev(list)) ] -> palindrome;

    enddefine;

Mark that, compile it then test it:

    mirror([a b c]);

If you just run that, its result is left on the Pop11 stack, and nothing
will be printed.

But you can run it and print the result:

    mirror([a b c]) =>

which prints:
    ** [a b c c b a]

What will this print?
    mirror([eat and enjoy everything]) =>

What will this print?
    mirror(99) =>

try it.


NOTE:
Pop11 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 for  beginners 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 Pop11 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;

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

The last error message may be abit misleading because pop11 thinks you
intended the procedure definition to end with ")" and complains because
there is no ";". It cannot tell that you left out "->" .



-- 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();
        lvars 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.)

Pop11 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 Pop11 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  Pop11  to obey,  or  execute,  the
instructions.

Here's how you ask Pop11 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:

    lvars 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;
        lvars 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.

This definition explicitly specifies in  the second line that the  input
variable "someone"  and the  output  variable "greeting"  are to  be  be
declared as local variables, using "lvars".

However that declaration will be done automatically for input and output
 variables, so you can omit  the lvars declaration for  input and output
 variables, thus:

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


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 somewhere  inside the  procedure
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 procedure 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. If you
don't have a local expert, you can join the pop-forum mailing list at
Birmingham university and post messages to it.
  https://mailman.cs.bham.ac.uk/mailman/listinfo/pop-forum )

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 Pop11 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 Pop11 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
    ;;; FILE     :  .....(file name) and line number
    ;;; DOING    :  null dl greet runproc ...

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 Pop11:

        greet([uncle joe]) =>

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

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

Then Pop11 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 Pop11 interpret the definition of the procedure? ----------

Suppose you type to Pop11:

    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 Pop11 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 Pop11 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  Pop11 to do  an
    assignment.

    In the  heading  "->"  tells  Pop11  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]) =>

Pop11 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 Pop11 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 LVARS 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 declare the variables, give them values and print them out.

Mark and load the next four lines:

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

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

The line starting "vars" declares the two variables as "global". They
are not defined inside a procedure definition, so they are not "local".
Although the two words were used for input and output variable names
inside the procedure greet, that use is totally disconnected with the
use of global variables.

Now run -greet-

    greet([uncle joe]) =>

This will TEMPORARILY give the  local variable SOMEONE the value
    [uncle joe]

and the local output variable GREETING the value:

    [hello uncle joe how are you today]

and the value of the output variable should be printed out. But if you
now re-do:

    someone, greeting =>

You'll see that their values have not been changed as a result of
running greet.

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 Pop11 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 Pop11 is concerned, the
procedure defined by:

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

produces behaviour that 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 PAGEUP and PAGEDOWN 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 Pop11 to  assign  anything to  the
variable GREETING.

Rather it is a 'dummy instruction' which tells Pop11 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 Pop11 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.  Pop11
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 Pop11 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 runproc

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 Pop11 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 runproc

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 Pop11  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 which has local variables is executed, 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

    TEACH PRIMER

There is a partly out of date book on Pop11:

    R.Barrett, A.Ramsay and A.Sloman
    Pop11: A practical language for artificial intelligence

Chapters 1, 2,  3 and  6, cover some of this material with some  more
advanced  information  in chapters 11 and 14. However, that book was
written before Pop11 was made to treat local variables in procedures as
'lvars', so some parts of the book are now out of date.

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:
--------------------------

For more on local and global variables and scoping see
    HELP * VARS
    HELP * LEXICAL
    HELP * LVARS

For more experienced programmers there is more on the difference between
"vars" and "lvars":

    TEACH * VARS_AND_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  Pop11  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 2011. All rights reserved. ----------