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 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 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 define newtest(num1, num2) -> result; num1 + num2 -> result; enddefine; newtest(5,7) => 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. ------