mirror of
https://github.com/PDP-10/its.git
synced 2026-03-25 01:47:08 +00:00
191 lines
6.2 KiB
Plaintext
Executable File
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
|