First you have to find a way to represent the initial information, and then you have to define procedures for operating on that information in order to answer the questions.
There are many ways you can express this sort of information. Below you'll see how to give the room dimensions to Pop-11 in the form of a list of lists. The procedures for answering the questions then have to use operations on lists.
The procedures to be defined in this introduction will each have a header line, which indicates the name of each procedure, the `arguments' required by the procedure (i.e. data for it to operate on), and whether the procedure produces a `result' for use by other procedures. The latter may have an arrow "->" in the header followed by a variable called an `output variable'. An example is this procedure heading, which will be found below.
define perim(len, breadth) -> total;It defines a procedure called "perim" which takes in two arguments (the value of the variable len and the value of the variable breadth) and then it returns one result, the value of the variable total. Some procedures do not have "->" and a following output variable (result variable).
Some of the procedures will use other procedures. For instance, display_data will use display_room, and display_room will use the procedures perim, area, volume. All these procedures will be defined below. If you have access to Poplog with an online version of this document you will be able to mark and load the definitions and test out the examples.
The procedures to be defined are summarised thus:
display_data(list_of_lists) Given a list of lists of information about rooms print out general facts about each one display_room(list) Given a list containing information about just one room print out information about its name, its perimeter, its area and its volume. perim(len, breadth) -> total area(len, breadth) -> total volume(len, breadth, height) -> total The above three take in numbers corresponding to measurements of a particular room, compute a new number, and return it as a result. (They leave it on the Pop-11 stack, instead of printing it out.) findroom(name, list_of_lists) -> data Given the name of a room and list of information about all rooms find the data about the named room and return it as a result. find_and_show(name, list_of_lists) Like the previous procedure, but print out the information instead of returning it as a result. find_and_show_all(namelist, list_of_lists) Do the same not just for one named room but for the rooms whose names happen to be in the list namelist
A major design decision for any program is (a) which information has to be represented in the program and (b) how it should be represented.
We assume that for each room we have four items of information, namely its name, in the form of a Pop-11 word, and three numbers representing its length its breadth and its height.
We shall represent individual rooms with a four element list, thus:
[room2 16 11 8] i.e. [<name> <length> <breadth> <height>]Note that unlike Prolog and some other languages, Pop-11 does not always require items in a list expression to be separated by commas. Later we'll see contexts where commas are needed.
Then we can represent all the rooms by means of a list of four element lists of the form shown above. Note that we are using position in the list to indicate the difference between length, breadth and height. We could have used a more verbose representation, such as
[room2 [length 16] [breadth 11] [height 8]] or [room2 [breadth 11] [height 8] [length 16]]This freer format (called an `association list') would have allowed the information to be presented in a more flexible form, with associations stored in any order, but would take up more space and would require more complex processing, so for now we'll use the simpler representation.
This is how you might give Pop-11 information about the set of rooms. First we use the word "vars" to tell Pop-11 to declare a "global" variable, called "rooms". We sometimes use the more accurate word "identifier" rather than "variable". But for now we'll ignore the difference.
global vars rooms;Then we construct a list of lists, giving for each room its length, breadth and height in feet (or whatever units we have chosen to work with). Finally we use the assignment arrow "->" to ask Pop-11 to "assign" the list to the variable "rooms".
[[room1 14 12 8] [room2 16 11 8] [room3 15 11 8] [room4 10 9 9] [room5 21 11 9]] -> rooms;In Pop-11 it is possible to combine the assignment and the declaration into an `initialised variable declaration', thus:
global vars rooms = [[room1 14 12 8] [room2 16 11 8] [room3 15 11 8] [room4 10 9 9] [room5 21 11 9]];Note that Pop-11 instructions can go over several lines, and a semi-colon ";" is used to terminate most declarations and most imperative instructions.
The above creates a very simple "database" of information about five rooms in the form of a list of lists of words and numbers.
The occurrence of a pair of square brackets [ .... ] tells Pop-11 that a list is to be constructed. Nesting the brackets tells Pop-11 to make a list of lists, as in this example.
If you are using VED you could mark that example, starting from the "vars" line down to the second semi-colon, then give the "load marked range" (LMR) command. (See TEACH LMR). Nothing visible will happen when you have done that. But it will enable you to do the examples that come later. You can check that it has been compiled by giving the command to print the value of the variable "rooms" thus:
rooms ==>This should produce the following printout:
** [[room1 14 12 8] [room2 16 11 8] [room3 15 11 8] [room4 10 9 9] [room5 21 11 9]]The two asterisks are printed by the Pop-11 print arrows "==>" and "=>", the former being used to produce neater formatting when printing long lists or other structures. "==>" prints only one (possibly complex) item at a time, unlike "=>" which can all the items that have been left on the Pop-11 "user stack", described below, and which does not format lists. This is how "=>" would print out the list
rooms ==> ** [[room1 14 12 8] [room2 16 11 8] [room3 15 11 8] [room4 10 9 9] [room5 21 11 9]]
Pop-11 (like any other textual programming language) has some built in "lexical analysis" rules, which specify how to analyse a sequence of characters into "text items" or "lexical items", from which a program is constructed. Normally the text items are words, numbers or strings. In this example we have:
words like "room1", "room2" etc., words that are made of a single square bracket, "[" or "]",and
numbers, 10, 12, 8 etc.The lexical analysis rules of Pop-11 state that a word starting with a letter may include a number, so that "room1" is accepted as a single word, not a word and a number, whereas "1room" would be split into a number and a word, i.e. 1 and "room". Many expressions typed without spaces will be broken into separate text items. For example:
x1+33 will be broken into: x1 + 3 member(x, list) will be broken into: member ( x , list )A later chapter will give more on lexical rules in Pop-11.
We have declared "rooms" as a GLOBAL variable. That is, it is not declared inside any procedure, unlike some of the variables to be introduced below. This means that it can be referred to at any time, and its value altered, or used, or printed out. E.g. we can give a command to print it out, using the `pretty-print' arrow ==>, thus:
rooms ==>If you are using VED you can get that command obeyed by marking the line and then giving the load marked range command (or pressing the LOADLINE key). The computer then prints out the value of the variable "rooms", something like this (where the two asterisks are produced by the print arrow "==>".
** [[room1 10 12 8] [room2 6 11 8] [room3 15 11 8] [room4 10 12 9] [room5 21 11 9]]
Let's start by defining a master procedure, called "display_data", which takes in a list of lists like that above, then for each room extracts the name and the three numbers representing the length, the breadth and the height, and then prints out the name followed by the perimeter, area and volume. So for room3 it might print out something like:
Room 3, length 15, breadth 11, height 8, ..... etc.If we already had a procedure called "display_room" that took in a list like
[room3 15 11 8]to print out the information regarding room3, then we could repeatedly use that procedure in display_data to print out information about each room.
Using Pop-11 we could express this as follows:
We might define define display_data as follows, using two `local' variables `list_of_lists' to hold the complete list of information, given as input to the procedure, and `room' to refer to the data for each room in turn:
define display_data(list_of_lists); lvars room; pr('ROOM INFORMATION'); pr(newline); ;;; Now print information about each room in the list for room in list_of_lists do display_room(room) endfor; pr('------------------------------------------------'); pr(newline); enddefine;This tells Pop-11 that a new procedure is to be defined.
1. The first line says that the procedure is to be called `display_data', and that when it runs it requires as input one object which, within the procedure, will be called "list_of_lists". The object can be called anything else in other places: the name, or variable, list_of_lists is private, or `local' to the procedure.
2. The second line declares the additional variable "room" as "lvars" (a special kind of local variable called a lexical variable, explained in a later chapter), The variable "room" will be used to refer in turn to the list of information about each room. Since Poplog version 15 the input variable "list_of_lists" in the procedure header will be automatically declared as "lvars". In earlier versions it would default to "vars" (explained below), unless explicitly declared otherwise.
3. The third line says that the string of characters `ROOM INFORMATION' should be printed out on the terminal.
4. The fourth line says that a "newline" should be printed. That simply means that the next lot of printing will continue on a new line, instead of to the right of `INFORMATION'.
5. The next line, starting ";;;" is a comment, which is ignored by Pop-11.
6. The next three lines give a Pop-11 looping (or iterative) instruction of the form:
for <variable> in <list> do <actions> endforThis tells Pop-11 that the <variable> should take a succession of values from the elements of the <list>. For each value it should perform the <actions> specified. When there are no more values, i.e. when it has got to the end of the <list>, it should continue with the instructions following "endfor".
In the definition above, this "for" instruction uses the local variable `room' to refer to the first element of the list, then the second element of the list, then the third element, etc. It does not matter how long the list is each element will be referred to in turn, and the repetition (or "iteration" as it is sometimes called) will continue as long as necessary, till every room, i.e. every individual list in the larger list_of_lists has been dealt with by display_room.
Thus when you design this procedure you do not need to know how many rooms will be in the list.
The instruction in the middle of the "for loop"
display_room(room)states that the procedure called `display_room' should be applied to whatever is referred to by `room'.
But display_room has not yet been defined, and we shall have to define it later.
7. The next instruction tells Pop-11 to print a string made of a lot of hyphens.
8. Finally the word "enddefine" merely signals the end of the procedure definition.
Notice how the word "define" starts a procedure definition that ends with "enddefine" and the word "for" starts a loop instruction that ends with "endfor". There are many similar pairs of matching opening and closing brackets in Pop-11. Most languages have such things but they differ from one language to another in their details.
If you have access to Poplog you can "mark and load" the above procedure. It will complain that you are using display_room, which has not yet been defined. It will declare the variable automatically for you, and print out
;;; DECLARING VARIABLE display_roomYou can try to run display_data nevertheless, giving the command:
display_data(rooms);Try to mark and do that command. It will start by printing out ROOM INFORMATION
Then it will try to run the "for loop" and you will get an error message starting
;;; MISHAP - enp: EXECUTING NON-PROCEDURE ;;; INVOLVING: <undef display_room> ;;; ......Later you will learn to read error messages. For now just note that this one is produced because you tried to get the procedure display_data run, but it failed because it used the procedure display_room which you have not yet defined.
You can give display_room a temporary definition, thus, to suppress the above message:
define display_room(room); ;;; Note "room" is implicitly declared as "lvars room;" ;;; simply print the list room, followed by a new line pr(room); pr(newline); enddefine;This is not very interesting, but it can be used to test the previous definition. Note that each line beginning ";;;" is a `comment' and will be ignored by Pop-11.
If you are using VED, then first mark and load the above definition of display_data, then re-try:
display_data(rooms);This time it should print out the following instead of the error message:
ROOM INFORMATION [room1 14 12 8] [room2 16 11 8] [room3 15 11 8] [room4 10 9 9] [room5 21 11 9] ------------------------------------------------
We now have to define the procedure display_room to give more interesting information. Look back at the list of information assigned to the global variable `rooms'. It was a list of lists. Each embedded list contained the name of a room and three numbers, from which we want to be able to compute things like perimeter and area of the room and print them out.
So we define display_room to cope with such a four-element list, as its input.
Using the built in printing procedure `pr' and three procedures we shall define later, for computing perimeter, area and volume, we could re-define the procedure display_room like this:
define display_room(list); ;;; Print out information about the room given in list lvars list, room_name, room_length, room_width, room_height; list(1) -> room_name; list(2) -> room_length; list(3) -> room_width; list(4) -> room_height; pr('INFORMATION CONCERNING: '); pr(room_name); pr(newline); pr(' perimeter is: '); pr(perim(room_length, room_width)); pr(newline); pr(' area is: '); pr(area(room_length, room_width)); pr(newline); pr(' volume is: '); pr(volume(room_length, room_width, room_height)); pr(newline); enddefine;(Note: this could be defined considerably more compactly, using more powerful facilities, some of which will be explained below. The input variable "list" has been explicitly included in the lvars declaration list, though since Poplog Version 15 that is redundant.)
If you mark and load that procedure it will complain that you have yet to define perim, area, and volume, but it will automatically declare them for you
;;; DECLARING VARIABLE perim ;;; DECLARING VARIABLE area ;;; DECLARING VARIABLE volumeLet's look at the definition in detail. The first line says a new procedure is being defined called `display_room'. When it runs it must be given one thing as input, which will be referred to as `list' in the procedure. The second line, starting ";;;" is a comment, ignored by Pop-11.
The next line declares the input variable list, and four additional names of local variables to be used in the procedure:
lvars list, room_name, room_length, room_width, room_height;The next line is an imperative which says, take the first element in the object called `list' and assign it to the variable `room_name', which can then be used later in the procedure to refer to it:
list(1) -> room_name;Notice that the expression `list(1)' can be used to refer to the first element of a list. Similarly `list(2)' refers to the second element, and so on.
To avoid repeatedly having to extract the second element we use the assignment arrow to store the result in a variable `room_length', which is used several times in the procedure. Similarly with the remaining items of information.
In a later chapter we will see that there are alternative methods of extracting selected elements of a list using the Pop-11 pattern matcher. In this case, since we want to get all the elements out of the list and assign them to four variables, we can use a still more more compact form based on the multiple assignment operation in Poplog, and the "explode" procedure which simultaneously returns all the elements of a list on the Pop-11 stack:
explode(list) -> (room_name, room_length, room_width, room_height);Try replacing that line with the four assignments in the definition, and recompile it.
Going back to the original definition of display_room, we see that Line 8 of the definition has two imperatives,
pr('INFORMATION CONCERNING: '); pr(room_name);which print information on the screen. The first instruction includes a string delimited by the single quote character `, also known as the string quote character in Pop-11. All the items in the string are printed by the procedure pr, exactly as they are given.
By contrast the next instruction uses the unquoted variable "room_name". so instead of printing "room_name" it prints the VALUE of that variable, which, if our program is working, will be a word, e.g. "room1" or "room2", etc., depending on which list is given as input to the procedure display_room.
The imperative:
pr(newline);Causes subsequent printing to be started on a new line.
A line like the following simply prints out a string, which in this case also include several spaces:
pr(' perimeter is: ');The next imperative:
pr(perim(room_length, room_width));This is actually composed of two instructions. The first is
perim(room_length, room_width)gives the values of "room_length" and "room_width" to a procedure called `perim' (yet to be defined, since it is not built in to Pop-11), and expects that procedure to produce one RESULT. That is followed by
pr( ... );which takes the result and prints it. E.g. if room_length has the value 16 and room_width has the value 12 then this should print out 44. Here we are using the expression
perim(room_length, room_width)to refer to the number which is the perimeter of the room. So when we define the procedure called "perim", we have to ensure that it produces a result which is a number, i.e. something which will be referred to by this sort of expression, and printed.
The remainder of our procedure uses similar techniques, first using the procedure area, which has yet to be defined, then a procedure volume, also to be defined. Note that the line:
pr(volume(room_length, room_width, room_height));implies that VOLUME will be given three inputs, not two like the others.
Here is how you might define perim to calculate the perimeter. It should take two numbers, add them, then multiply by 2 to get the total perimeter, which is then the result of the procedure.
define perim(len, breadth) -> total; lvars len, breadth, total; (len + breadth) * 2 -> total enddefine;If you are using VED, mark and load that procedure. (If you are using Poplog version 15 or later, the "lvars" line is not needed.)
The first line has two new features. First we are here defining a procedure with two input variables `len' and `breadth', not just one as before. (Note: we cannot use `length' for the first argument, as that's already a name of a Pop-11 system procedure. If we used `length' we'd get a mishap message. Hence `len'). Secondly the procedure header ends with `-> total', which implies that this procedure is to produce one result. What that result will be will depend on what is assigned to the variable `total' in the procedure.
The occurrence of `-> total' in the header is not itself an assignment. It is merely an indication of how the variable "total" is being used in the procedure. I.e. it is an `output local' variable, whereas "len" and "breadth" are `input locals'. Although the output local declaration looks like an assignment, it does not cause the procedure to assign anything to the variable. Rather it indicates that after the procedure is used there will be a `result' available, and the user must take care to assign something to the variable "total" within the procedure, to be used as the result.
We can test the procedure thus, using "==>" to print out the result:
perim(5,3) ==> ** 16(Mark and load that command. Try it with different numbers to make sure it always gives the right answer.).
Actually, "==>", the `pretty print' arrow is not needed for printing out something as simple as a number. It is intended for printing more complex structures, like lists of lists. So in this case, we could use the simple print arrow "=>", thus:
perim(5,3) => ** 16Pop-11 has many built in procedures besides "+" and "*" which can be used to create either complex expressions or complex imperatives. More of them will be introduced later.
Similarly we can define AREA, using the multiplication symbol "*":
define area(len, breadth) -> total; len * breadth -> total; enddefine; ;;; and now test it: area(18,12) => ** 216Prior to Poplog version 15 the procedure header should be followed by
lvars len, breadth, total;
define volume(len, breadth, height) -> total; len * breadth * height -> total; enddefine;Mark and load that, and test it:
volume(5,5,5) => ** 125Having defined all the required subsidiary procedures, we can now test display_room, remembering that it requires a list of information about one room:
display_room([room17 9 6 7]);And this command causes the following to be printed out:
INFORMATION CONCERNING: room17 perimeter is: 30 area is: 54 volume is: 378We could arrange `prettier' formatting, but for now we shall ignore that. We can now run the master program on the list rooms, which we created above. Just to check, we can print out the contents of the list:
rooms ==> ** [[room1 14 12 8] [room2 16 11 8] [room3 15 11 8] [room4 10 9 9] [room5 21 11 9]]Where the asterisks are again produced by "==>". Then run the master procedure, giving it the list rooms as input:
display_data(rooms);mark and load that, which produces all the following printout:
ROOM INFORMATION INFORMATION CONCERNING: room1 perimeter is: 52 area is: 168 volume is: 1344 INFORMATION CONCERNING: room2 perimeter is: 54 area is: 176 volume is: 1408 INFORMATION CONCERNING: room3 perimeter is: 52 area is: 165 volume is: 1320 INFORMATION CONCERNING: room4 perimeter is: 38 area is: 90 volume is: 810 INFORMATION CONCERNING: room5 perimeter is: 64 area is: 231 volume is: 2079 ------------------------------------------------Notice that only two of our procedures do any printing, namely display_data, and display_room. The procedures perim, area, and volume produce RESULTS which they do not print out themselves. Instead they leave their results on the Pop-11 stack, explained in a later chapter. Items left on the stack can then be used by other procedures.
In this case, the results are printed in display_room. They could have been used for other purposes. For instance, we could use area to define volume, by first computing the area, and then multiplying by the height:
define volume(len, breadth, height) -> total; area(len, breadth) * height -> total; enddefine;Here `area(len, breadth)' produces a result, which is then multiplied by the value of `height' to produce the volume. This version of volume should produce the same results as the previous version.