Search                        Top                                  Index
TEACH VEDPROC                                     Aaron Sloman Sept 1997

This file continues from TEACH VEDPOP, showing how to define and test
procedures in VED. Reading TEACH VEDPOP first is strongly recommended.
See also TEACH * USEFULKEYS for a summary of some basic VED commands.



         CONTENTS - (Use <ENTER> g to access required sections)

 -- Defining a procedure
 -- Create your own file with Pop-11 instructions
 -- A simple procedure
 -- Testing your procedure
 -- Compiling the "current" procedure
 -- Error messages when you load a procedure definition.
 -- Some terminology
 -- Putting a procedure definition into a file
 -- Another procedure
 -- Mishaps
 -- Types of Mishaps
 -- Still more on mishaps
 -- A procedure for calculating tax
 -- Using TRACE to show when procedures are run
 -- Input and output
 -- More on procedures with results
 -- Some examples of graphics in Pop-11
 -- Tidying files
 -- WHAT TO DO NEXT
 -- More online information

-- Defining a procedure -----------------------------------------------

We turn now to defining procedures. A procedure in Pop-11 is a
collection of instructions to do something. It usually has a name, so
that you can use it repeatedly. For example "pr", "+", "*" were all
names of procedures in examples above.

A definition of a procedure is a complex expression, composed of
declarations, denoting expressions and imperatives. It also usually
specifies a name (though you will meet anonymous procedures later).

A procedure is defined using a header, which introduces the name, then
declarations, then a "body" then "enddefine". Here's an example,
defining a procedure called "test"

    define test(num) -> result;
        lvars num, result;
        num + 2 -> result;
    enddefine;

This illustrates the general format:

    procedure header (starting with "define")
        declarations
        body
    procedure end (i.e. "enddefine") ;

The header starts with the word "define" then the name of the procedure,
then more information about the inputs of the procedure (if any) and the
outputs (if any), and ends with a semi-colon.

A procedure definition extends the range of Pop-11 commands. You need to
be able to do that in order to write programs. For the time being you
are not going to define any interesting procedures. Instead some trivial
examples will help you get used to some of the mechanics of using the
editor and getting the editor to compile procedures and obey Pop-11
instructions. (Doing these elementary things is a bit like playing
scales when learning a musical instrument.)

-- Create your own file with Pop-11 instructions ----------------------

So, now use VED to start your very own file called 'test.p' It is
important to end the file name with '.p' so that VED knows that the file
is going to contain Pop-11 programs. I.e. do

    ENTER ved test.p

This will start a new file belonging to you called 'test.p'. You can put
Pop-11 instructions into it.

-- A simple procedure ------------------------------------------------

Type the following definition of a new procedure called 'test' into
your file. It tells Pop-11 to create a new procedure called 'test'.

define test(num) -> result;
    lvars num, result;
    num + 2 -> result;
enddefine;

Notice that this definition uses "lvars" rather than "vars", because
specially restricted "local" variables are needed for such a procedure.
(This is an oversimplification, which will do for now.)

When this procedure is run (not when it is defined) it will produce the
result of adding 2 to a number called "num".

When you type that definition into your file called test.p. Be careful
to type exactly what is shown (including the semi-colons).

When it is done you cannot use the procedure without first compiling its
definition, i.e. telling Pop-11 about the procedure:

Using the MARKLO and MARKHI keys as explained in TEACH MARK, (or the
Mark menu buttons) mark those four lines in your file, then load the
marked range (ENTER lmr).

If you make a mistake you will get an error message, and you may have to
examine very carefully what you have typed, in order to fix it.

If all goes well your procedure will have been compiled. What that means
is that it is translated from the human-readable text form into into the
machine language of the computer, which you cannot use directly. The
machine language version of the procedure is stored in the temporary
memory of the computer ready to be obeyed.


-- Testing your procedure ---------------------------------------------

Once you have loaded the definition, you have effectively extended the
Pop-11 system to recognize a new command called "test".

You can get get Pop-11 to obey it and print out the result as follows.
Edit a file called 'output.p', thus:

    ENTER ved output.p

then type into it:

     test(5) =>

This says, "obey the procedure called test, giving it 5 as input, and
print the result preceded by two asterisks (that's what "=>" means)".
When obeyed this should make the procedure test calculate the result of
adding 5 and 2. Then "=>" prints it out.

Load that line. The result should be printed into 'output.p'.

You will then have three files in VED, this teach file, your file
'test.p' and your file called 'output.p'. Depending on which version of
VED you use, and the amount of space on your screen, you are likely to
see only two of the three files at any one time.

If you are using XVED you may find it convenient to reduce the number
lines shown in one or more of the files, or rearrange them so that you
can see all three files at once. Remember to put the mouse pointer into
a file if you want to do anything in it.

If this file gets covered up in the single window VED you can get back
here by using the command:

    ENTER teach vedpop

Or else use the <ESC> e  command to select a file, as described in
TEACH * BUFFERs.

-- Compiling the "current" procedure ----------------------------------

If you look back at the table of VED commands for compiling portions of
a file you'll see that there are special commands for compiling the
"current" procedure, which do not require you first to mark the range.

Those commands will work ONLY if the word "define" starts at the
beginning of a line (i.e. on the left, without any preceding spaces).
Here's the definition again: put the VED cursor somewhere in it, then
try the different "Load current procedure" VED commands.

define test(num) -> result;
    lvars num, result;
    num + 2 -> result;
enddefine;

Try again with the "+" missing, which should produce a MISSING SEPARATOR
"mishap" message:

define test(num) -> result;
    lvars num, result;
    num 2 -> result;
enddefine;

Then fix the definition and try again.


-- Error messages when you load a procedure definition. ---------------

If anything goes wrong (i.e. you got a MISHAP message, or the wrong
result) it may have been because you had not typed the procedure
definition exactly right. If you suspect this is what happened then go
back to your file

    ENTER ved test.p

and look carefully at the procedure definition in your test.p file. If
you see a mistake, then correct it. Then repeat the process of loading
the procedure definition and running the procedure.

Try loading some other lines with tests of the same procedure, e.g.

    test(99) =>

Exercise:
    Try changing the procedure so that it adds a different number, 10,
    to its input, instead of always adding 2.
    Define a procedure called add_ten, in this format:

define add_ten(number) ;

    ......
    ......

enddefine;


Then mark it and re-load it, and come back to the output file and test
it again.

    add_ten(10) =>

    add_ten(90) =>

See what happens if you leave out one of the parentheses.
    add_ten(90 =>

then
    add_ten 90) =>

Read the mishap messages carefully. They will later help you find bugs
in your programs.


-- Some terminology ----------------------------------------------------

"test" is now the name of a PROCEDURE (not to be confused with your FILE
'test.p').

The procedure has one ARGUMENT (or INPUT) and returns one RESULT, i.e. a
number. The Pop-11 instruction

     test(5)

is a CALL of the procedure, i.e. Pop-11 is asked to RUN, (or EXECUTE, or
OBEY) the instructions in the DEFINITION of the procedure. It is given
the number 5 as its "argument" (sometimes called "parameter" or "actual
parameter", or "input", or "input value").

Pop-11 runs this command by putting the argument, i.e. the number 5,
on "the user stack" (you'll learn more about that, and then runs the
machine instructions in the procedure test. Those instructions take the
5 off the stack, then do some manipulations, and put a new result back
on the stack.

The RESULT left on the stack is printed out by the print arrow '=>' in
the instruction:

     test(5) =>

The parentheses are important. They tell Pop-11 that the procedure is to
be obeyed. If the name is not followed by parentheses (with an argument
inside), then Pop-11 will just attempt to print out the procedure. Try
this:

     test =>

Think of the parentheses "(  )" as the 'doit' parentheses. The fact that
we put something inside the parentheses indicates that the procedure
requires one thing to operate on, its ARGUMENT (i.e. INPUT).

That was a pointless procedure: nobody would ever want to define a
procedure like that except for practice. For now try to ignore the fact
that it is pointless, and simply do these exercises to develop skills
you will use later for less pointless tasks.


-- Putting a procedure definition into a file ----------------------

One reason why it is useful always to use an editor to create your
programs is than you would otherwise have to re-type them every time you
logged in and wanted to run them.

Moreover, the chances of typing in a big procedure without making a
mistake are low. So usually it is best to use VED to create a file
containing the definition and then arrange for Pop-11 to read (or
compile) the file. This allows you to use VED to fix mistakes. The
definition will be stored in long term memory on the magnetic disk and
so be available for use on another day.


-- Another procedure --------------------------------------------------

Now try adding a second procedure definition to your program file
'test.p'

First tell VED you want to edit test.p, then go to the end of the file
(how?) then type in the second procedure and run it:

    ENTER ved test.p

                       <You should then be in the test.p file>
                       <go to end>

define newtest(num1, num2) -> result;
    num1 + num2 -> result;
enddefine;

                       <Then mark the procedure and load it>
                       <Or use load current procedure>
                       <Then go to the output file and try>

newtest(5,7) =>

                       <Try other pairs of numbers>

Note that in the definition of newtest, we could have written
    lvars num1, num2, result;

just below the procedure header. Since Poplog version 15 this has not
been necessary: the variables in a procedure header are automatically
declared as "lvars" (lexical local variables).

Then, to return to this teach file do

     ENTER teach vedpop

Or use ESC e to get a selection of files in VED and chose one.

-- Mishaps ----------------------------------------------------------

We will now introduce a deliberate error, to see how Pop-11 reacts. Go
back to your file test.p, go to the end of the file, and then type in a
third definition, thus:

    ENTER ved

define double(num) -> result;
    num + num -> result;
enduntil;

Can you see the deliberate mistake?

Mark and load it. Pop-11 will print out a message effectively saying
that it couldn't understand what ENDUNTIL was doing there, (there being
no preceding UNTIL). It was expecting to find ENDDEFINE, to match the
initial DEFINE.

So you can alter your "double" procedure to finish with "enddefine". Do
that (i.e. replace 'enduntil' with 'enddefine'), then mark and load it.
Now go to your output file and test it with:

    double(5)=>

Try some other tests.

Your test.p has a mixture of procedure definitions, and your 'output.p'
file has a collection of test instructions with the printout they
produce.

If you keep both definitions and test instructions in the same file, the
tests may get in the way when you want to use your procedures later. You
can prevent this by putting them in "comment" expressions using "/*" to
start a comment and "*/" to finish. E.g.

/*

    double(20)=>

*/

If you load the whole file that command will be ignored because it
occurs between the "comment" brackets /* and */. But you can put the
cursor on the test line inside the comment and use "load this line", to
get it obeyed directly.


-- Types of Mishaps ---------------------------------------------------

As you will have found, Pop-11 can detect certain types of mistake (or
MISHAP as it calls them) automatically. Mistakes come in two types -
syntactic and run-time.  You make a syntactic mistake when you type
something to Pop-11 (or into a file subsequently read by Pop-11) that is
ungrammatical (according to the Pop-11 language). In that case Pop-11
cannot understand the instructions.

A run-time error occurs when you have issued a command that, although
grammatically correct, goes wrong when Pop-11 tries to perform it. For
example, to ask Pop-11 to add two and two and then print the result you
would type:

     2 + 2 =>

If you typed:

     + 2 2 =>

Pop-11 would tell you of the SYNTACTIC mistake. If you typed:

     [two] + [two] =>

it would tell you of the run-time mistake (the mistake is that [two] is
not a number but a 'list' and cannot be used for arithmetic; don't worry
what a list is - all will be revealed in due course).


-- Still more on mishaps -----------------------------------------------

Syntactic mistakes are discovered as the file is loaded. Run-time
mistakes may not be discovered until much later (when the new
definitions are used). A multi-million pound rocket produced by the
European Space Agency was destroyed because it went of course due to a
run-time mistake in the control programs which was discovered only once
the launch had started. Many programs have very serious run time errors.

You'll find that as you get more proficient at programming you'll make
fewer and fewer syntactic mistakes; you will always make run-time
mistakes however. If anything goes wrong in VED or Pop-11 itself (which
happens occasionally!) then they are examples of undetected run-time
mistakes: mistakes made by the Poplog system developers (or someone
responsible for a local Poplog library). Please report the mistakes!

-- A procedure for calculating tax ------------------------------------

Just so that you can see how something useful might be done using the
techniques learnt so far, here is how you might define a procedure which
works out how to add VAT tax to a price. Suppose that you have to add
15% VAT tax to the cost of items you buy. We can define a procedure
called TAX which works out what the TAX on an item is, then we can
define a procedure called COST which works out the total cost, i.e.
price + tax. To work out the TAX we have to multiply the price by 0.15
(i.e. 15%). In Pop-11 the asterisk is used for multiplication.

Put the following procedures into a file called tax.p.

define tax(price) -> total;
    ;;; multiply price by 0.15 to get 15%
    price * 0.15 -> total;
enddefine;

define cost(price) -> total;
    ;;; add the 15% tax to the price, to get a total for cost
    price + tax(price) -> total;
enddefine;

All the words after the three semi-colons ";;;" until the end of a line
are taken as 'comments' (i.e. they are ignored by Pop-11). Comments are
useful to you, and to anyone who might read your program, to remind you
of the purpose and effect of a procedure. You can add a comment to the
end of any line of Pop-11. Any good programmer will write lots of
comments.

When you have typed the procedures into your file load them (you can
load the two together by marking the entire range). Then to find out the
total cost of something whose price is 20 pounds do:

    cost(20) =>

If you just want to find out the tax, do

    tax(20) =>

Notice how the second procedure, cost, makes use of the first procedure,
tax. You will often do things like that. If you want to run the
procedure cost, it is not enough to compile it. You MUST also compile
the procedure tax.

-- Using TRACE to show when procedures are run ------------------------

Pop-11 has a "trace" command that changes procedures to show when they
start and what their arguments and results are. Change tax and cost to
turn on tracing:

trace tax, cost;

Now test cost. The ouput will show how it calls "tax" by showing the
beginning (>) and end (<) of the tax calculation between the beginning
and end of the cost calculation.

cost(100) =>

Load that line and see what appears in your output.p file:

> cost 100
!> tax 100
!< tax 15.0
< cost 115.0
** 115.0

The first four lines are the new trace output. The lines with ">" show a
procedure starting and what its argument (input) is. The lines with
"<" show the procedure finishing and what its result (output) is.
The "!" is used to indicate that the newly started procedure is running
inside a procedure that is already being traced. In more complex cases
the indentation will be very helpful. Try running cost with another
number as argument.

You can turn off tracing if you load this line:

untrace tax, cost;

Check that tracing has been turned of by loading this line:

cost(100) =>

Learning when to trace procedures to find out what is going on is an
essential skill required for debugging complex programs.

-- Input and output ----------------------------------------------------

The procedures TAX and COST, like the previously defined procedures
require some input. If you just type

    tax() =>

You will get an error message:

;;; MISHAP - STE: STACK EMPTY (missing argument? missing result?)
;;; DOING    :  prmishap tax  ...

Try it and see!

This happens because you defined the procedure TAX with something
between the parentheses on the first line, indicating that an input was
needed:

    define tax(price) -> total;
        ;;; multiply price by 0.15 to get 15%
        price * 0.15 -> total;
    enddefine;

The input is here named 'price'. But Pop-11 doesn't 'understand' the
word, it is merely a name to indicate where the input for TAX is stored:
you could have used any other name for the input, e.g. 'xyxy' as in:

    define tax(xyxy) -> total;
        ;;; multiply xyxy by 0.15 to get 15%
        xyxy * 0.15 -> total;
    enddefine;

Whatever you call the input, if you specify in the definition that there
is to be an input then you must supply one when you run the procedure.
The input is also referred to as an "argument" to the procedure. A
procedure that takes in arguments and produces a result is often called
a "function".

But you can use different arguments at different times. Try different
arguments, in some tests inside comments, e.g.

/*
    tax(200) =>

    tax(150) =>

*/

Should give the results:

    ** 30.0
    ** 22.5

Try all that and other examples.

Then try the procedure COST with different inputs.

-- More on procedures with results ------------------------------------

Some Pop-11 procedures produce an output, often called a result. Here's
the definition of TAX again:

    define tax(price) -> total;
        ;;; multiply price by 0.15 to get 15%
        price * 0.15 -> total;
    enddefine;

The part of the definition which says that a result is returned from the
procedure is this bit of the header line.

    -> total

It says: whenever the procedure finishes, the value of the variable
total, whatever it happens to be, should be left on the Pop-11 stack as
the result of the procedure.

The line that gives total its value is this bit in the body of the
procedure:

        price * 0.15 -> total;

The multiplication (*) produces a number which is assigned to total.

After that the procedure finishes, and the value of total is left on the
stack.

Note that "total" is called an "output local variable" for the procedure
tax.

What was "price" called?

-- Some examples of graphics in Pop-11 --------------------------------

Now for something completely different!

On machines that include the X window system, Pop-11 includes a very
useful extension that can be used for making pictures. Some of the
graphical facilities are available via the RC_GRAPHIC (Relative
Coordinates Graphic) library. You can ensure that this is available, if
you load the following command:

    uses rc_graphic;

(At Birmingham that command is not needed, as rc_graphic is already
built into Pop-11.)

Then you can use the library procedure rc_new_window to create a window
which is 400 "pixels" wide, 350 high, with its top left corner 520
pixels from the left of screen and 20 pixels down, thus:

    rc_new_window(400, 350, 520, 20, true);

If you load that line it should create a graphical window. The "true"
makes it create a "coordinate frame" based on the centre of the window,
with X values going to the right and Y values going up the screen.

You can draw some lines with commands like these. Try them:

    rc_draw(100);
    rc_turn(90);
    rc_draw(100);

    ;;; The next two commands will not draw anything, but will
    ;;; influence the start location and heading of the next line.
    rc_jumpto(150, 150);
    -90 -> rc_heading;
    rc_draw(100); rc_turn(-90); rc_draw(100);

You can clear the graphical window with this command

    rc_start();

Here's how you can use those commands to define a procedure to draw a
square, with a side of a given length:

define rc_square(side);
    repeat 4 times
        rc_draw(side); rc_turn(90);
    endrepeat
enddefine;

Put that at the end of your test.p file and compile it (either mark and
load, or load current procedure).

Then put some test commands in a comment after the procedure, and try
them, loading one line at a time, to see what happens.

/*
    ;;; This clears the graphic window
    rc_start();
    ;;; Draw a square of side 50
    rc_square(50);
    ;;; Draw a bigger square starting in the same place
    rc_square(100);
    ;;; Start at location X = -100, Y = -100, and draw another
    rc_jumpto(-100, -100);
    rc_square(50);
*/

You can make a pretty picture with the next procedure, which you can add
to your file. The procedure takes four arguments:

define prettypic(x, y, heading, side);
    ;;; Go to location x, y, aim in direction heading, then
    ;;; draw 8 squares of the given side in different orientations.

    ;;; Jump to start location
    rc_jumpto(x, y);
    ;;; Set initial drawing orientation
    heading -> rc_heading;
    ;;; Draw a square then change the orientation, 8 times
    repeat 8 times
        rc_square(side); rc_turn(45)
    endrepeat
enddefine;

Compile that procedure (how)? Also make sure that your definition
of rc_graphic has been compiled and tested.

Then try some tests like these, trying to work out what will happen
before you load each line.

/*
    rc_start();

    prettypic(0, 0, 0, 70);

    prettypic(0, 0, 22.5, 120);

    prettypic(0, 0, 22.5, 100);

    prettypic(0, 0, 0, 60);

    ;;; Start again in a different place
    rc_start();

    prettypic(-50, 0, 0, 50);
    prettypic(-50, 0, 22.5, 100);

*/

You can try some more pictures, but don't spend too much time on them
as there's lots more to learn, and making pretty pictures can be
seductive.

(Developing graphical programs like that is a good way to learn a lot
more about geometry and arithmetic.)

There are lots more graphical facilities in Pop-11, some of them
introduced in TEACH FACES and TEACH RC_GRAPHIC.


-- Tidying files ----------------------------------------------------

If you log out and, later, log in again, the files you have created will
still exist, but the procedures will need to be reloaded. You can do
this simply enough by just marking the range of all the procedure
definitions and then use either the "ENTER lmr" command, or the
CompileRange Menu button.

If you include test procedure calls in amongst the procedure definitions
then Pop-11 will run these as well, which can cause confusion. That's
because when you come back to a file, you will want to load the
procedures first, and then try them out on new data. You will not want
all your tests to be run every time. So it is a bad idea to mix
procedure definitions and test procedure calls, unless you put the test
commands inside comments, as illustrated above.

For practice, you could go through your file test.p looking for test
commands that are not between comment brackets, and either delete them
or put the /* ... */ brackets round them (ensuring that the procedure
definitions are not included).


-- WHAT TO DO NEXT ----------------------------------------------------

A good thing to try next is

TEACH * RIVER - This gives useful practice in defining a Pop-11 procedure
    to solve a problem. It re-introduces some of the syntax of Pop-11,
    reinforcing what you have learnt here, and gives you more practice
    using the editor. It introduces a puzzle, the river-crossing puzzle,
    that is the topic of several later teach files.

Later you can try:

TEACH * MATCHES - this introduces you to the Pop-11 pattern matcher,
    a powerful tool used for analysing lists.

Then you will be ready for:

TEACH * RESPOND - this will show you have to build an Eliza-like program
    using the matcher.

TEACH * READLINE - this explains how to use the POP=11 "readline"
procedure to read in something typed by the user, for a program to
respond to.

For revision:

TEACH * TEACH
TEACH * USEFULKEYS
TEACH * VED
TEACH * MARK    (summarised in HELP * MARK)
TEACH * LMR
TEACH * BUFFERS

TEACH * POPCORE is an online summary of a subset of Pop-11. You should
be given a printed version.

If you are shooting ahead, try
    TEACH * DEFINE    for more information on defining Pop-11 procedures.
                      (Some bits may be slightly out of date)
    TEACH * VARS      for more information on variables and
                        declarations.
    Read the Pop-11 Primer (see below).

Later you will find it useful to be able to get the editor to search
for a part of a long file, instead of searching by eye. You can find out
how to do this by reading: TEACH * VEDSEARCH

For most purposes it is enough to learn how to search forward and
backward using the commands
    ENTER /string
    ENTER \string


For more information on Pop-11 read:

Aaron Sloman
    TEACH PRIMER
    A book-length introduction to Pop-11 which is available online,
    and is also available in printed form. (Ask in the Computer Science
    departmental library, Birmingham).
    It includes far more material than is needed for most introductory
    courses. It may at first be hard to understand if you are new
    to programming. If so read it again later, and you will understand
    more.
    Note: there is an online version of this called TEACH * PRIMER. It
    is VERY long, so do not try to print it. However it may be
    convenient to search in the editor.

M.Sharples, D.Hogg, C.Hutchison, S.Torrance and D.Young (1989)
    Computers and Thought, A Practical Introduction to Artificial
    Intelligence
    MIT Press
    (Many students find this stimulating and readable. It may be
    out of print, alas.)

Chris Thornton & Benedict du Boulay (1992)
    Artificial Intelligence Through Search
    Paperback version Intellect Books
    (This introduces several AI techniques using both Pop-11
    and Prolog.)


-- More online information --------------------------------------------

TEACH * GSTART
    If you feel fluent with VED and defining and testing Pop-11
    procedures, and have had previous experience of programming,
    the GSTART teach file will enable you to gain a lot more
    insight into Pop-11 quite rapidly.

TEACH * POPSUMMARY
    Gives a longish overview, but less than the Primer.

A yet more detailed account of Pop-11 is given in the POPLOG REF files.
But they are only worth reading if you are a very experienced programmer
already.


--- $poplocal/local/teach/vedproc
--- Copyright University of Birmingham 1997. All rights reserved. ------