TEACH VARS A. Sloman May 1987 Updated 29 Sep 1996 Updated Jan 2001 Introduction to POP-11 variables ================================ If you are not familiar with the use of POP-11 and the editor, first work through TEACH VEDPOP. Then do TEACH MARK and TEACH LMR to see how to compile POP-11 programs from the editor. If you use the Menu mechanism, you may find the Compiling menu useful. CONTENTS - (Use g to access required sections) -- Introduction and overview -- Some examples of variable declarations -- Assigning a value to the variable x, using "->" -- Why variables are useful -- -- What happens when you run the procedure double? -- -- Exercise: define treble -- The input variable for a procedure is a local variable -- Declaring a local variable using "lvars" -- Input locals are implicitly declared as lvars -- -- Defining double with an output local -- -- Exercise: -- Using a global variable outside a procedure definition -- An exercise -- -- Getting back to this file -- Combining a declaration and an initial assignment -- Variables can be used to store lists -- You can have any number of variables -- Choose variable names that help you understand your programs -- Case is significant in Pop-11 -- A variable can be used to store any type of object known to Pop-11 -- Declaring and initialising several variables at once -- Local and global variables -- Variables and the pattern matcher -- Associated documentation -- Note for experienced programmers. -- Introduction and overview ------------------------------------------ This teach file introduces the notion of a variable, which has a name (e.g. "num3" "list" "x" etc) and can have a value, which could be any sort of object that Pop-11 knows about, e.g. a number, a list, a word, a procedure, an array, etc. Variables can be used inside procedures as "local variables" or outside procedures as "global" variables. Variables can be "declared" using "vars" or "lvars" depending on the type of variable, as explained below. Roughly speaking you use "lvars" for a variable that is "local" to a procedure, and "vars" for global variables used outside a procedure. "vars" is sometimes also used for variables that occur in patterns. However, if you are using the Birmingham local Pop-11 library which includes the pattern prefix "!" then you can also use "lvars" for pattern variables. These ideas will be explained below. -- Some examples of variable declarations ----------------------------- Note that in the examples that follow, any text preceded by three semi-colons, i.e. ";;;", is a "comment" and will be ignored by Pop-11. Mark and load the examples that follow, excluding the English text and the lines starting with two asterisks "**" which indicate what should be printed out when the command on the preceding line is obeyed. Examples of variable declarations are the following: ;;; Declare x as a variable vars x; ;;; Print out its "default" value x => ** This shows that x has been given an "undef object" as its value. Undef objects are special objects used to indicate that a variable has not yet been given a value by the user. ;;; Declare "long_name" as a variable and print out its value vars long_name; long_name => ** This is also an undef object, but it carries the information that it was created as the default value for the variable long_name, whereas the other one was created for x. (This sort of difference can help when you are debugging programs.) -- Assigning a value to the variable x, using "->" -------------------- You can now give x a value which is a number, and then print it out. For this you need to use the assignment arrow "->" which you can read as "goes to". Try these: ;;; Read the following as "66 goes to x" 66 -> x; x => ** 66 Note that the assignment changes the value of x, so that it no longer has the "undef object" as its value. It now has a number as its value. Besides printing out the number you can add it to another number, and then print out the result: x + 5 => ** 71 Or you can give x a word as its value ;;; Read this as: The word "cat" goes to x "cat" -> x; x => ** cat Or a list of words ;;; This assigns a list of three words to the variable x [the black cat] -> x; x => ** [the black cat] Note that x started with an undef object as its value, then had a number as its value, then a word, then a list. In Pop-11 a variable can (usually) be assigned anything as a value. Variables do not have "types" which restrict what can be assigned to them. (There is one exception: some variables are defined as "procedure" variables, so that they can have only procedures as variables. For now you need not bother about the restriction.) The value assigned to a variable can be any of the kinds of things POP-11 knows about - words, lists, numbers, procedures, etc. By associating an object with the name, you can then refer to that object in different instructions. For example, if you have worked through TEACH RESPOND, you will have seen things of this form, using the variable "list": if list matches [??x mother ??y] then [tell me more about your family] => elseif list matches [i want to ??x] then [do you know anyone else who wants to ^^x] => elseif list matches [i ??x you] then [perhaps in your fantasy we ^^x each other] => ...and so on... Note that here the variable "list" is used several times, to the left of the Pop-11 operation "matches". It is assumed to have a list as its value, which is matched against several patterns in turn until the match is successful. (Don't worry about the details for now.) Thus, in order to achieve generality in our programs, we can use a variable to refer to some unspecified object. Then we can test whether the object has some property and if it does then perform a certain action on it otherwise test if it has another property and if it does, then perform another action on it, and so on, just as in the above example from ELIZA. -- Why variables are useful ------------------------------------------- The way the word "it" was used in the previous sentence illustrates why variables are useful in programming languages. By using the word "it", I was able to say something general about an unspecified object and refer to the same object in different parts of my sentence. Similarly, in a programming language you often need to define some general procedure for operating on objects of a certain kind, and spell out the instructions in a way that will be the same for all objects. You can then use a variable to refer to that object. E.g. in the following pseudo-English instruction I use "x" as a variable referring to an unspecified number. to double x do (x + x) That isn't POP-11. The pop-11 equivalent would be something like this: define double(x); return(x + x); enddefine; Notice the three occurrences of "x". One, in the first line, saying that the procedure double is to have one input, which is going to be called x, then in the second line saying what to do with x. Compile that definition as explained in TEACH VEDPOP. (Mark and load it, or put the VED cursor in the procedure and type ESC c). POP-11 will read in and compile the definition but will not print anything out. You can check that it has read it by asking it to print out what double is. Compile the next line. double => it should print, in your 'output' file: ** saying that "double" is now the name of an object which is a procedure. Now you can get POP-11 to RUN the procedure with the number 5 as input, if you mark and load the following. double(5) => POP-11 will give x the value 5 and return (5 + 5) i.e. 10, and => will print it out. Try that. Try other variations, e.g. to calculate and print out the double of 999. -- -- What happens when you run the procedure double? When the command "double(5)" is obeyed, that tells Pop-11 to run the procedure double, defined above, but with the number 5 as the value of the variable x. So the command in the middle of the definition, which was return(x + x); becomes equivalent to return(5 + 5); which in turn is equivalent to return(10) -- -- Exercise: define treble In order to check your understanding of that example, try defining and testing a procedure called "treble", which takes a number and produces a result that is three times that number. Instead of "x" you could use some other name for the input variable, e.g. "fred". So your definition might start: define treble(fred); complete that and test it by marking and loading it, then, in your 'output' file mark and load: treble(5) => to check that it prints out the right number. -- The input variable for a procedure is a local variable ------------- The definition above looked like this: define double(x); return(x + x); enddefine; In that definition "x" is a local variable. That is to say, the value it has when the procedure is running is private to that procedure, and cannot be accessed afterwards. E.g. try the following: 66 -> x; double(4) => ** 8 Now print out the value of x. What will it be? x => The answer is that the value it had outside double is preserved, i.e. it retains the "global" value afterwards, even though inside double x had the value 4. -- Declaring a local variable using "lvars" --------------------------- Sometimes you want to introduce a new variable to hold some value while a procedure is running, so that you can use the value more than once in for each run of the procedure. (I.e. it is used in more than one place in the procedure.) For example below is a procedure called simplechat that asks the user to type in his or her name, by printing out a request. It then uses the procedure readline to read in a list of words typed in by the user. That list is stored in the temporary (local) variable "username" which is declared as lvars username; Then two new lists are printed out that make use of the value of that variable. This is the procedure define simplechat(); ;;; This procedure takes no inputs and returns no results. ;;; It merely has a simple conversation with the user. [Hello there. Please type in your name. End with RETURN] => lvars username; readline() -> username; [Thank you for telling me that your name is ^^username] => [Now ^^username I am signing off. Have a goood day] => enddefine; If you test that you could have silly conversations like this every time you invoke simplechat: simplechat(); ** [Hello there . Please type in your name . End with RETURN] ? Georgy Porgy ** [Thank you for telling me that your name is Georgy Porgy] ** [Now Georgy Porgy I am signing off . Have a goood day] and do it again. simplechat(); ** [Hello there . Please type in your name . End with RETURN] ? Musing Mary ** [Thank you for telling me that your name is Musing Mary] ** [Now Musing Mary I am signing off . Have a goood day] Each time the procedure is run, a new temporary "private" (i.e. local) version of the variable "username" gets created for the duration of that procedure's running. After that variable has been given a value (which must happen INSIDE the procedure) the value can be used, e.g. by giving its value as input to another procedure, or by using its value in constructing a list. When the procedure finishes running the variable will cease to exist. It is also possible to declare a local variable as "vars", and some of the older text books and teach files will do that. However in general this can lead to complications if you have complex programs with many procedures, so it is best to use "lvars". Using "lvars" means is that there is no possibility of any unexpected interaction between the variable in this procedure and another procedure that runs inside it, but is defined elsewhere. If this is your first experience of learning a programming language that statement may not mean much to you. Don't worry about that. Just remember that there are reasons for using "lvars" that can prevent some nasty surprises. This is because the variables not declared using "lvars" have a property known as "dynamic scope". For certain purposes dynamically scoped variables can be very powerful, by allowing certain procedures to produce different behaviour in different contexts, in a systematic way. But that very same power can cause trouble in some cases. The difference is explained, for experienced programmers, in TEACH VARS_AND_LVARS If you are not learning Pop-11 as a first language, or if you have been using it for a few months and are quite fluent you may find that file helpful. -- Input locals are implicitly declared as lvars ---------------------- The procedure double was defined above thus: define double(x); return(x + x); enddefine; The input variable "x", which is used twice in the procedure, is a local variable, as explained above. However, it is not merely local: it is implicitly defined as an "lvars" local, i.e. a "lexical" local variable, as if the procedure had been defined thus: define double(x); lvars x; return(x + x); enddefine; This is exactly the same as the previous version, except for the new line lvars x; Since Version 15 of Poplog that is redundant since all the input local variables of a procedure are now automatically declared as "lvars". Before Version 15 they were declared by default as "vars", and some of the older books on Pop-11 may say that. -- -- Defining double with an output local Instead of using "return" it is possible to specify explicitly that the procedure double produces a result, by using an output local variable in the header: define double(x) -> result; x + x -> result; enddefine; Here "x" is an input local variable as before, and "result" is an output local variable. (Usually they are just referred to as "input locals" and "output locals"). Like the input locals, the output locals are automatically declared as lvars variables. I.e. it is as if this had been included inside the above procedure definition, after the header: lvars x, result; You can include such declarations but they are redundant. Some of the older teaching material will include examples where the input and output locals are declared as lvars, because the examples were written before the default changed. Notice that the "-> result" in the header is not an assignment. It is an indication that when the procedure double finishes the value of the variable "result" will be used as the "output" of the procedure. So when you run the procedure, whatever was assigned to the variable result will be leaved on the stack, as the output of the procedure. You can use "=>" to print it out, as before. double(101) => ** 202 double(-20) => ** -40 If you define a procedure with an output local, and no instruction inside the procedure gives it a value then when the procedure runs, it will return a result that may be anything, depending on what the current implementation does with uninitialised local variables. define no_value_result() -> out; [There is no value assigned to out] => [This is the default value out has] => out => enddefine; If you run that on some implementations of Pop-11 the value out will be the number 0 (nought). But in other implementations it could be arbitrary junk. Try it: no_value_result() => ** [There is no value assigned to out] ** [This is the default value out has] ** 0 ** 0 That is what happens if the value out gets the number 0 as its default value. If lvars variables are not given 0 as default value, then the last two lines could be printed out as arbitrary junk. Whenever your program has a local variable of any sort, especially if it uses an output local, make absolutely sure that the local variable gets a sensible value. Otherwise the default may be junk that corrupts some part of your program. -- -- Exercise: 1. What is a local variable? 2. What is a global variable? 3. What is an input local variable? 4. What is an output local variable? 5. Which kind of local variable is best avoided in simple procedures? 6. Which form of declaration for local variables declares lexical locals? 7. If a procedure has an output local what happens when the procedure finishes running? 8. If you use a procedure with pattern variables (e.g. with "matches" or "present") why should you use "!" as a pattern prefix? -- Using a global variable outside a procedure definition ------------- In the definition of double, "x" and "result" were used as local variables. I.e. when the procedure "double" runs they get values, but those values cannot be accessed before or after double is run. If you want to use a variable outside a procedure, e.g. to store some value for future use, then you should tell POP-11 by "declaring" the variable using the special syntax word "vars", as shown below. vars num; Mark and load it. (Nothing should be printed out.) That is an explicit declaration of a global variable, global because it is not inside the definition of any procedure. As before, you can then give the variable a value, using the "assignment" operator "->". For example, suppose we want to give the word "num" the value 1. We can assign 1 to it thus: 1 -> num; Read that as "one goes to num". Now mark and load that line. Nothing is printed out, but you can now ask POP-11 to print the value of the variable num: num => Mark and load that. The printout will go to your 'output' file. You can now give an instruction to POP-11 to add one to num and assign the result as the new value of num: num + 1 -> num; ;;; read as "num plus 1 goes to num" num => ;;; print the value of num Compile those two lines. The new value of num is printed in your output file. If you do it over and over again you'll see the value of num grow. You can do that inside a procedure with a local variable, or outside any procedure with a global variable. You can use the value of a global variable to give as input to a procedure, e.g. try this: double(num) => Whatever value num had at the time that command is obeyed is given to the input variable in double, i.e. "x". But when double runs, there is nothing inside its instructions to say where the value of "x" came from. I.e. when double runs, it knows nothing about the variable "num". -- An exercise -------------------------------------------------------- Try the following exercise, modelled on the "num" example above. Before doing it first look back at the "num" example so that you remember all the details. (Perhaps write them down, including all the semi-colons). Then read up to the next heading, so that you know what is coming. You could type the example into a file of your own called 'test.p': ved test.p (a) Declare a variable with any name you like, e.g. num1, fred, or whatever, using "vars" as was done above with num. Mark and load it. (You may accidentally choose a name that is already reserved by POP-11. If you get an obscure error message, try changing the name of the variable.) (b) Type in an instruction to assign 2 to your variable, using "->" (c) Mark and load the assignment. (So far, nothing should have been printed out, if you made no mistakes). (d) Type in an instruction to multiply the value of your variable by 2 and assign the result to the same variable. The multiplication symbol in POP-11 is "*" (i.e. the asterisk). (e) Type in an instruction to print out the value of the variable, using the print-arrow "=>" (f) Mark and load your two instructions. This should print something into your 'output' file. (g) Then REDO the load command several times to see how the value of your variable changes. -- -- Getting back to this file If you type your POP-11 commands into another file ('test.p') and the output goes into a file called 'output', then if you are using a VDU screen that allows only two files to be shown at a time, this TEACH file will no longer be visible. You can get back to it by typing teach vars or using e as explained in TEACH BUFFERS. -- Combining a declaration and an initial assignment ------------------ If you give the following two commands: vars number; 66 -> number; You have (a) declared the variable "number" and (b) assigned the integer 66 to it. You can combine those to into a single operation, thus: vars number = 66; Compile that line, then test it with this command: number => It should print out ** 66 Exercise: Define a variable "bignumber" and simultaneously initialise it to have the value 1000000 (i.e. one million: don't put commas into numbers in POp11). Then print out the value of bignumber. You can also combine a declaration and an initial assignment for an lvars local variable, e.g. lvars num = 0; lvars list = []; Those two together are equivalent to lvars num; 0 -> num; lvars list; [] -> list; You can also combine two declarations into one, thus: lvars num, list; You can also combine the two declarations with initialisations. But you MUST put commas between them, and you must end the declaration with a semi-colon, ";", as follows: lvars num = 0, list = []; Many people find it clearer to put those on different lines: lvars num = 0, list = []; Don't leave out the comma between declarations and don't leave out the semi-colon at the end, otherwise you are likely to get some sort of mishap message when you attempt to compile the program. Try deleting the command and mark and compile the above. A combined declaration and initialisation can also be used for global variables declared using "vars", as in the next section. -- Variables can be used to store lists -------------------------------- Try giving POP-11 the following three commands, by marking and loading the next two lines: vars shopping = [fruit meat soap]; shopping => The first command declares "shopping" as a variable, and initialises its value to be a list containing the three words "fruit", "meat" and "soap". I.e it assigns the list to the variable. The second line prints out the value of the variable shopping. Square brackets should always be put around lists which are directly typed in to POP-11 and POP-11 will always put square brackets around any lists it prints out. Here's a second example for you to mark and load. vars tasks = [shopping cleaning]; tasks => Try extending both lists by typing in extra words between the square brackets. You can then mark and load the commands and get different values printed out. Later you will learn to do more interesting things with lists. (E.g. TEACH MATCHES, TEACH DATABASE) -- You can have any number of variables -------------------------------- You can create any number of variables. A variable name need not be a meaningful English word - it can be any letter followed by any sequence of letters or digits. The following example gives some idea of the range of possible names: vars cat, goat, x, y13, dx, c3po, r2d2, k9, fhuasf, goal, earlier_state_of_conciousness, people, steve; That is a single global declaration of 13 variables, some with short names some with long. After you compile that declaration, all these names are now available for use. Notice how the underscore can be used to increase legibility. The underscore "_" should not be confused with the hyphen "-". You can't use the hyphen to form long words as it doesn't "join up" with letters, in POP-11. Instead it represents "subtraction". It does "join up" with some other symbols, for instance with ">" in the assignment arrow "->". Not all languages are the same. E.g. in Lisp if you left out spaces, x-y could be a single variable whose name has three characters, whereas in Pop-11 it is equivalent to x - y Likewise "x+y" is equivalent to "x + y", and "cat*dog" is equivalent to "cat * dog". This follows from Pop-11's rules for word construction, described in HELP WORDS. -- Choose variable names that help you understand your programs ------- On the whole, it is better to use mnemonic variable names if you want your programs to be easily read by people, including you if you return to the program a week or two later. The computer doesn't care which names you use: the word "earlier_state_of_conciousness" is as meaningful to it as "fhuasf" or "x". Occasionally you will decide on a variable whose name is already reserved as a system name by POP-11. Then you will get an error message, e.g. vars length; Try compiling that line. You will get an error message on VED's status line: The mishap message says: MISHAP: IDW: ILLEGAL DECLARATION OF WORD (missing ; after vars?): If you are really curious to know what this means, you can use the HELP IDW command to get more information, but don't worry about that for now if you are learning programming for the first time. -- Case is significant in Pop-11 -------------------------------------- In POP-11, upper case and lower case letters are different, and so you can have two variables with the same letters but one in capitals and the other lower case, e.g. "x" and "X" are different, as in: vars x, X; [hello]-> x; 2-> X; x=> ** [hello] X=> ** 2 Some of the TEACH files use UPPER case for the POP-11 examples to make them stand out from the English text. You should always type them without capital letters. The rules for legal formation of variable names in POP-11 are rather more complicated than shown here. HELP * WORDS gives more details. (You can access that help file by putting the cursor on the asterisk then typing h. ) -- A variable can be used to store any type of object known to Pop-11 Any type of object known to POP-11 can be 'assigned' to a variable. We have already seen that numbers and lists can be assigned to variables. It is also possible to assign a word. The following is rather confusing example. Try to understand what is going on. vars w,z; "z" -> w; "w" -> z; z => ;;; print out the value of z ** w w => ;;; print out the value of w ** z "w" => ;;; print out the word "w" itself ** w "z" => ;;; print out the word "z" itself ** z If you wish to check those examples, mark and load each line, apart from the lines with "**", which tell you what should be printed out into your output file. -- Declaring and initialising several variables at once --------------- In the examples given above variables were first declared and then given some value (initialised). However, as explained above, these operations can be combined with the use of the '=' sign e.g., so instead of vars x; [hello] -> x; you can do: vars x = [hello]; And test the result x=> ** [hello] Try it. You can also declare and initialise several variables in one "vars" instruction, though you have to remember to put commas between them, thus: vars y = x, p = [goodbye], n = [how are you]; or perhaps in a clearer format: vars y = x, p = [goodbye], n = [how are you]; The version that is spread over several lines is exactly equivalent to the one line version, because Pop-11 treats line breaks as equivalent to spaces. That's why you need ";" to indicate the end of a declaration. You can check that the multiple initialisation worked: y => ** [hello] n => ** [how are you] p => ** [goodbye] -- Local and global variables ----------------------------------------- It is possible for the same variable to be used both globally and locally, though in general that is a bad policy, since it can cause confusion. Here is an example to illustrate the fact that local variables do not affect the values of global variables, when the procedure has finished running. define triple(list) -> output; [^^list ^^list ^^list] -> output; enddefine; This defines a procedure that can be given a list as input, and produces as a result a new list containing all the elements of the original list, three times over. It uses "list" as an input local and "output" as an output local, and both are declared implicitly as "lvars" variables. Mark and load it, and then test it: triple([hee ho]) => ** [hee ho hee ho hee ho] triple([three blind mice]) => ** [three blind mice three blind mice three blind mice] The variable "list" can also be used as a global variable, and given a list of numbers as values. (Actually, POP-11 doesn't care what value you give it, even though its name is "list".) vars list = [1 2 3 4 5]; ;;; declare and initialise "list" list => ** [1 2 3 4 5] Now if we run the procedure triple again: triple([hee]) => What will happen to the value of the variable list? list => While triple is running, list will have the list [hee] as its value. But when triple is finished, only the global value of list will be available, namely [1 2 3 4 5] Check that out for yourself, by compiling those commands. -- Variables and the pattern matcher ---------------------------------- You may find in some old text books or old teach files that when pattern variables are used with "?" or "??", they should be declared as dynamic using "vars". E.g. vars second, rest; vars list = [one two three four five six]; if list matches [= ?second ??rest] then [The second was: ^second] => [The rest were: ^^rest] => endif; That will work, but inside a procedure definition it is better to use "lvars", so it would have to be changed as follows, using "!" lvars second, rest; lvars list = [one two three four five six]; if list matches ! [= ?second ??rest] then [The second was: ^second] => [The rest were: ^^rest] => endif; For more on this see TEACH MATCHES TEACH VARS_AND_LVARS The same goes for patterns used by the Pop-11 database procedures which use the matcher, i.e.: present lookup remove flush foreach forevery as described in TEACH * DATABASE and related files. An example of the use of "!" is given near the end of TEACH * MATCHES [An extension to Pop-11 is available from the Birmingham Free Poplog directory which allows the prefix "!" to be used before a pattern to change the pattern so that it will work with "lvars".] -- Associated documentation ------------------------------------------- TEACH * DEFINE - more information on defining procedures, and local variables. TEACH * STACK - more on what happens when procedures are run and assignment instructions are obeyed TEACH * LISTS - information on lists, with examples TEACH * MATCHES - using the POP-11 pattern matcher, with variables in the patterns. TEACH * MATCHARROW For more advanced information (for experienced programmers) try: The POP-11 PRIMER (also TEACH * PRIMER) [available in printed form at Birmingham.] HELP *WORDS - the format for legal POP-11 words HELP *VARS - more advanced information on variables TEACH *VARS_AND_LVARS - explains the difference. For more information: (Recommended only for the very knowledgeable) HELP * LVARS - the use of lexically scoped variables HELP * LEXICAL - more on lexically scoped variables REF * VMCODE - for computer scientists and others interested in implementation details. -- Note for experienced programmers. ---------------------------------- Because of the history of POP-11, which followed early versions of Lisp, if input local variables, or output local variables are not EXPLICITLY declared, then Pop-11 used to IMPLICITLY declare them as dynamically scoped, as if they had been declared using "vars". This was changed in Poplog version 15.0, so that it is no longer necessary to use "lvars" to ensure that input and output locals are lexically scoped. For more information see TEACH * VARS_AND_LVARS --- Copyright University of Sussex 1996. All rights reserved. ------ --- $poplocal/local/teach/vars --- Copyright University of Birmingham 2001. All rights reserved. ------