1
0
mirror of https://github.com/PDP-10/its.git synced 2026-03-25 01:47:08 +00:00
Files
PDP-10.its/src/teach/lesson.prog
2018-10-28 16:47:17 -07:00

191 lines
6.2 KiB
Plaintext
Executable File

.comment -*- Mode:TEXT; -*-
.comment all the PROG constructs
.document PROG - Description of the functions PROG, PROG2, and PROGN.
.tag PROG
Lesson PROG, Version 2 Kent M. Pitman, 5/25/79
revised by Victoria Pigman, 9/3/82
--- PROGN ---
(PROGN <form1> <form2> <form3> ... <formN>)
evaluates each <form> in sequence, returning the value of the last form.
PROGN's uses are primarily historical. In Pure Lisp, many forms allowed
only one Lisp form in a certain position. For example, LAMBDA's could
evaluate exactly one Lisp form in their binding context - as in:
((LAMBDA <bvl> <form>) <arg1> <arg2> ... <argN>)
rather than the more modern flavor of:
((LAMBDA <bvl> <form1> <form2> ... <formN>) <arg1> <arg2> <argN>)
In such a situation, PROGN was useful because it allowed the person to make
the <form> a PROGN which contained all of the forms he wanted to evaluate:
((LAMBDA <bvl> (PROGN <form1> <form2> ... <formN>))
<arg1> <arg2> ... <argN>)
In fact, things like the body of a lambda-operator are today called implicit
PROGN's because they act exactly like a PROGN, evaluating the forms from left
to right and returning the value which is returned by the last form in the
sequence.
Some uses of PROGN which are still valid today are:
[1] 'Sneaking in' a statement where there wasn't room for one.
For example:
(SETQ A (PROGN (PRINT '(I AM NOW SETQING A TO 5))
5))
which will behave very much the same as:
(PRINT '(I AM NOW SETQING A TO 5))
(SETQ A 5)
[2] 'Hiding return values'
When evaluating a lot of things in a sequence, it may be the case
that you don't care to see the return values of some expression.
Doing
(PRINC 'FOO)
(PRINC 'BAR)
in the interpreter is not satisfactory for printing FOOBAR because
the evaluator takes off and prints the "FOO" probably before you get
the second form typed in - and even if it didn't, you'd get the return
value from the first form printed in between. By doing
(PROGN (PRINC 'FOO) (PRINC 'BAR))
you can achieve the desired effect.
.pause
--- PROG2 ---
(PROG2 <statement1> <statement2> ... <statementN>)
This statement is tricky, but sometimes useful. As a good rule, don't ever
TRY to find uses for it. One of these days when you get experienced you will
suddenly develop a need for it and can come back and learn it. What it does,
however, is to evaluate <statement1> through <statementN> in order, but
remembering the value of <statement2> and returning that.
Examples:
(PROG2 (SETQ A 4) (LIST A A) (SETQ A 5))
will return
(4 4)
but A will be set to 5 when it is through executing.
(PROG2 () (CAR A) (SETQ A (CDR A)))
will return the CAR of A, but will side-effect on A by removing setting it to
its CDR.
.try
If you wanted to implement a push-down stack in Lisp, for instance, this is
one possible way...
.eval-print
(DEFUN INIT-STACK () (SETQ STACK-POINTER NIL))
.eval-print
(DEFUN PUSH-STACK (X) (SETQ STACK-POINTER (CONS X STACK-POINTER)))
.eval-print
(DEFUN POP-STACK ()
(PROG2 () ; Ignore first argument
(CAR STACK-POINTER) ; Return top of stack
(SETQ STACK-POINTER ; Pop stack
(CDR STACK-POINTER))))
Then watch the following:
Input: (INIT-STACK)
Output: NIL
Input: (PUSH-STACK 'FOO)
Output: (FOO)
Input: (PUSH-STACK 'BAR)
Output: (BAR FOO)
Input: (PUSH-STACK 'BAZ)
Output: (BAZ BAR FOO)
Input: (POP-STACK)
Output: BAZ
Input: (POP-STACK)
Output: BAR
Input: (PUSH-STACK 'MORE)
Output: (MORE FOO)
and so on... Try it for yourself with the following warnings...
Do not define functions called PUSH or POP because there are Lisp built-in
functions by that name which do something in the way of PUSHing and POPping
but in a different way and you will break your Lisp if you redefine them
randomly!
The functions INIT-STACK, PUSH-STACK and POP-STACK are now defined in your
environment. Experiment on your own now. (And don't forget to call
(INIT-STACK) before you start or you'll find our STACK-POINTER variable is an
unbound variable.)
.try
--- PROG ---
(PROG <bound-variable-list> <body>)
This is a very hairy offshoot of PROGN which is much more powerful but at the
cost of readability in most cases. The user is STRONGLY discouraged from using
this, since there is almost always a more readable way of doing the same thing.
<bound-variable-list> is a list of variable names which will all be locally set
to NIL at the beginning of the PROG.
<body> is a sequence of statements and tags. Any atom in the list is a tag, or
label. Any non-atom is a form to be evaluated.
Tags can be used as references in a GO statement. They are not evaluated, but
rather flow of control is continued at the statement just after the tag.
Additionally, the RETURN function may be called from any point inside of a
PROG form. The RETURN function takes exactly one argument, which is the value
to be returned as the value of the innermost PROG which lexically encloses the
RETURN statement. What this means is that when a RETURN is encountered in the
middle of executing a PROG, the argument to RETURN is evaluated and then that
value is returned by the PROG, and the rest of the body of the PROG (if any)
is not executed. Clear? Probably not, but don't worry about it now. Try some
simple examples of your own to try to figure this out. Here's an example
of a simple use of PROG:
(PROG (A)
(SETQ A 0.)
LOOP
(PRINT A)
(SETQ A (1+ A))
(COND ((LESSP A 3) (GO LOOP)))
(RETURN 'DONE))
This program will print out 0, 1, and 2 and then return the atom DONE. If all
of the forms in a PROG are executed successfully and no RETURN statement is
seen, the PROG will "fall out the bottom", returning NIL.
Note also that the above form is very hard to read because it contains GO
statements. GO statements are looked down upon strongly by most programmers.
Maclisp also provides a DO statement for doing looping which can accomplish
the same action in a much more elegant way.
NOTE: ESPECIALLY if you are a Fortran programmer, avoid using PROG since
it will probably allow you to sink back into the ease of your old [bad]
programming habits instead of exploring some of the more elegant programming
constructs which Lisp has to offer.
.try
.next DO