SECTION 23
1
CLISP - CONVERSATIONAL LISP
23.1 Introduction
The syntax of LISP is very simple, in the sense that it can be described
concisely, but not in the sense that LISP programs are easy to read or
write! This simplicity of syntax is achieved by, and at the expense of,
extensive use of explicit structuring, namely grouping through
parenthesesization. Unlike many languages, there are no reserved words
in LISP such as IF, THEN, AND, OR, FOR, DO, BEGIN, END, etc., nor
2
reserved characters like +, -, *, /, =, _, etc. This eliminates
entirely the need for parsers and precedence rules in the LISP
interpreter and compiler, and thereby makes program manipulation of LISP
programs straightforward. In other words, a program that "looks at"
other LISP programs does not need to incorporate a lot of syntactic
information. For example, a LISP interpreter can be written in one or
two pages of LISP code ([McC1], pp. 70-71). It is for this reason that
LISP is by far the most suitable, and frequently used, programming
language for writing programs that deal with other programs as data,
e.g., programs that analyze, modify, or construct other programs.
However, it is precisely this same simplicity of syntax that makes LISP
programs difficult to read and write (especially for beginners).
'Pushing down' is something programs do very well, and people do poorly.
As an example, consider the following two "equivalent" sentences:
"The rat that the cat that the dog that I owned chased caught ate
the cheese."
versus
"I own the dog that chased the cat that caught the rat that ate the
cheese."
------------------------------------------------------------------------
1
CLISP was designed and implemented by W. Teitelman. It is discussed
in [Tei5].
2
except for parentheses (and period), which are used for indicating
structure, and space and end-of-line, which are used for delimiting
identifiers.
23.1
Natural language contains many linguistic devices such as that
illustrated in the second sentence above for minimizing embedding,
because embedded sentences are more difficult to grasp and understand
than equivalent non-embedded ones (even if the latter sentences are
somewhat longer). Similarly, most high level programming languages
offer syntactic devices for reducing apparent depth and complexity of a
program: the reserved words and infix operators used in ALGOL-like
languages simultaneously delimit operands and operations, and also
convey meaning to the programmer. They are far more intuitive than
parentheses. In fact, since LISP uses parentheses (i.e., lists) for
almost all syntactic forms, there is very little information contained
in the parentheses for the person reading a LISP program, and so the
parentheses tend mostly to be ignored: the meaning of a particular LISP
expression for people is found almost entirely in the words, not in the
structure. For example, the following expression
(COND (EQ N 0) 1) (T TIMES N FACTORIAL ((SUB1 N)))
is recognizable as FACTORIAL even though there are five misplaced or
missing parentheses. Grouping words together in parentheses is done
more for LISP's benefit, than for the programmer's.
CLISP is designed to make INTERLISP programs easier to read and write by
permitting the user to employ various infix operators, IF-THEN-ELSE
statements, FOR-DO-WHILE-UNLESS-FROM-TO-etc. expressions, which are
automatically converted to equivalent INTERLISP expressions when they
are first interpreted. For example, FACTORIAL could be written in
CLISP:
(IF N=0 THEN 1 ELSE N*(FACTORIAL N-1))
Note that this expression would become an equivalent COND after it had
been interpreted once, so that programs that might have to analyze or
otherwise process this expression could take advantage of the simple
syntax.
There have been similar efforts in other LISP systems, most notably the
MLISP language at Stanford [Smi1]. CLISP differs from these in that it
does not attempt to replace the LISP syntax so much as to augment it.
In fact, one of the principal criteria in the design of CLISP was that
users be able to freely intermix LISP and CLISP without having to
identify which is which. Users can write programs, or type in
expressions for evaluation, in LISP, CLISP, or a mixture of both. In
this way, users do not have to learn a whole new language and syntax in
order to be able to use selected facilities of CLISP when and where they
find them useful.
CLISP is implemented via the error correction machinery in INTERLISP
(see Section 17). Thus, any expression that is well-formed from
INTERLISP's standpoint will never be seen by CLISP (i.e., if the user
defined a function IF, he would effectively turn off that part of
CLISP). This means that interpreted programs that do not use CLISP
constructs do not pay for its availability by slower execution time. In
fact, the INTERLISP interpreter does not "know" about CLISP at all. It
operates as before, and when an erroneous form is encountered, the
interpreter calls an error routine which in turn invokes the Do-What-I-
Mean (DWIM) analyzer which contains CLISP. If the expression in
question turns out to be a CLISP construct, the equivalent INTERLISP
form is returned to the interpreter. In addition, the original CLISP
expression, is modified so that it becomes the correctly translated
INTERLISP form. In this way, the analysis and translation are done only
once.
23.2
Integrating CLISP into the INTERLISP system (instead of, for example,
implementing it as a separate preprocessor) makes possible Do-What-I-
Mean features for CLISP constructs as well as for pure LISP expressions.
For example, if the user has defined a function named GET-PARENT, CLISP
would know not to attempt to interpret the form (GET-PARENT) as an
arithmetic infix operation. (Actually, CLISP would never get to see
this form, since it does not contain any errors.) If the user
mistakenly writes (GET-PRAENT), CLISP would know he meant (GET-PARENT),
and not (DIFFERENCE GET PRAENT), by using the information that PRAENT is
not the name of a variable, and that GET-PARENT is the name of a user
function whose spelling is "very close" to that of GET-PRAENT.
Similarly, by using information about the program's environment not
readily available to a preprocessor, CLISP can successfully resolve the
following sorts of ambiguities:
1) (LIST X*FACT N), where FACT is the name of a variable, means
(LIST (X*FACT) N).
2) (LIST X*FACT N), where FACT is not the name of a variable but
instead is the name of a function, means (LIST X*(FACT N)), i.e., N
is FACT's argument.
3) (LIST X*FACT(N)), FACT the name of a function (and not the name of a
variable), means (LIST X*(FACT N)).
4) cases (1), (2) and (3) with FACT misspelled!
The first expression is correct both from the standpoint of CLISP syntax
and semantics and the change would be made without the user being
notified. In the other cases, the user would be informed or consulted
about what was taking place. For example, to take an extreme case,
suppose the expression (LIST X*FCCT N) were encountered, where there was
both a function named FACT and a variable named FCT. The user would
first be asked if FCCT were a misspelling of FCT. If he said YES, the
3
expression would be interpreted as (LIST (X*FCT) N). If he said NO, the
------------------------------------------------------------------------
3
Through this discussion, we speak of CLISP or DWIM asking the user.
Actually, if the expression in question was typed in by the user for
immediate execution, the user is simply informed of the
transformation, on the grounds that the user would prefer an
occasional misinterpretation rather than being continuously
bothered, especially since he can always retype what he intended if
a mistake occurs, and ask the programmer's assistant to UNDO the
effects of the mistaken operations if necessary. For transformations
on expressions in user programs, the user can inform CLISP whether
he wishes to operate in CAUTIOUS or TRUSTING mode. In the former
case (most typical) the user will be asked to approve
transformations, in the latter, CLISP will operate as it does on
type-in, i.e., perform the transformation after informing the user.
23.3
user would be asked if FCCT were a misspelling of FACT, i.e., if he
intended X*FCCT N to mean X*(FACT N). If he said YES to this question,
the indicated transformation would be performed. If he said NO, the
system would then ask if X*FCCT should be treated as CLISP, since FCCT
4
is not the name of a (bound) variable. If he said YES, the expression
would be transformed, if NO, it would be left alone, i.e., as
(LIST X*FCCT N). Note that we have not even considered the case where
X*FCCT is itself a misspelling of a variable name, e.g., a variable
named XFCT (as with GET-PRAENT). This sort of transformation would be
considered after the user said NO to X*FCCT N -> X*(FACT N). The graph
of the possible interpretations for (LIST X*FCCT N) where FCT and XFCT
are the names of variables, and FACT is the name of a function, is shown
in Figure 23-1 below.
------------------------------------------------------------------------
4
This question is important because many INTERLISP users already have
programs that employ identifiers containing CLISP operators. Thus,
if CLISP encounters the expression A/B in a context where either A
or B are not the names of variables, it will ask the user if A/B is
intended to be CLISP, in case the user really does have a free
variable named A/B.
23.4
Figure 23-1
23.5
The final states for the various terminal nodes shown in the graph are:
1: (LIST (TIMES X FCT) N)
2: (LIST (TIMES X (FACT N)))
3: (LIST XFCT N)
4: (LIST (TIMES X FCCT) N)
5: (LIST X*FCCT N)
CLISP can also handle parentheses errors caused by typing 8 or 9 for "("
or ")". (On most terminals, 8 and 9 are the lower case characters for
"(" and ")", i.e., "(" and "8" appear on the same key, as do ")" and
"9".) For example, if the user writes N*8FACTORIAL N-1, the parentheses
error can be detected and fixed before the infix operator * is converted
to the INTERLISP function TIMES. CLISP is able to distinguish this
situation from cases like N*8*X meaning (TIMES N 8 X), or N*8X, where 8X
is the name of a variable, again by using information about the
programming environment. In fact, by integrating CLISP with DWIM, CLISP
has been made sufficiently tolerant of errors that almost everything can
be misspelled! For example, CLISP can successfully translate the
definition of FACTORIAL:
(IFF N=0 THENN1 ESLE N*8FACTTORIALNN-1)
to the corresponding COND, while making 5 spelling corrections and
5
fixing the parenthesis error.
This sort of robustness prevails throughout CLISP. For example, the
6
iterative statement permits the user to say things like:
FOR OLD X FROM M TO N DO (PRINT X) WHILE (PRIMEP X)
However, the user can also write OLD (X_M), (OLD X_M), (OLD (X_M)),
permute the order of the operators, e.g.,
DO PRINT X TO N FOR OLD X_M WHILE PRIMEP X, omit either or both sets of
parentheses, misspell any or all of the operators FOR, OLD, FROM, TO,
DO, or WHILE, or leave out the word DO entirely! And, of course, he can
------------------------------------------------------------------------
5
CLISP also contains a facility for converting from INTERLISP back to
CLISP, so that after running the above incorrect definition of
FACTORIAL, the user could "CLISPIFY" the now correct LISP version to
obtain (IF N=0 THEN 1 ELSE N*(FACTORIAL N-1)).
6
This expression should be self explanatory, except possibly for the
operator OLD, which says X is to be the variable of iteration, i.e.,
the one to be stepped from N to M, but X is not to be rebound. Thus
when this loop finishes execution, X will be equal to N+1.
23.6
7
also misspell PRINT, PRIMEP, M or N!
CLISP is well integrated into the INTERLISP system. For example, the
above iterative statement translates into an equivalent INTERLISP form
8
using PROG, COND, GO, etc. When the interpreter subsequently encounters
this CLISP expression, it automatically obtains and evaluates the
9
translation. Similarly, the compiler "knows" to compile the translated
form. However, if the user PRETTYPRINTs his program, at the
corresponding point in his function, PRETTYPRINT "knows" to print the
original CLISP. Similarly, when the user edits his program, the editor
keeps the translation invisible to the user. If the user modifies the
CLISP, the translation is automatically discarded and recomputed the
next time the expression is evaluated.
In short, CLISP is not a language at all, but rather a system. It plays
a role analagous to that of the programmer's assistant (Section 22).
Whereas the programmer's assistant is an invisible intermediary agent
between the user's console requests and the INTERLISP executive, CLISP
sits between the user's programs and the INTERLISP interpreter.
Only a small effort has been devoted to defining the core syntax of
CLISP. Instead, most of the effort has been concentrated on providing a
facility which "makes sense" out of the input expressions using context
information as well as built-in and acquired information about user and
system programs. It has been said that communication is based on the
intention of the speaker to produce an effect in the recipient. CLISP
operates under the assumption that what the user said was intended to
represent a meaningful operation, and therefore tries very hard to make
sense out of it. The motivation behind CLISP is not to provide the user
with many different ways of saying the same thing, but to enable him to
worry less about the syntactic aspects of his communication with the
system. In other words, it gives the user a new degree of freedom by
permitting him to concentrate more on the problem at hand, rather than
on translation into a formal and unambiguous language.
------------------------------------------------------------------------
7
In this example, the only thing the user could not misspell is the
first X, since it specifies the name of the variable of iteration.
The other two instances of X could be misspelled.
8
(PROG NIL
(SETQ X M)
$$LP(COND
((OR (IGREATERP X N)
(NOT (PRIMEP X)))
(RETURN)))
(PRINT X)
(SETQ X (ADD1 X))
(GO $$LP))
9
See page 23.26, for discussion of how translations are stored.
23.7
23.2 CLISP Syntax
Throughout CLISP, a non-atomic form, i.e., a list, can always be
substituted for a variable, and vice versa, without changing the
interpretation. For example, if the value of (FOO X) is A, and the
value of (FIE Y) is B, then (LIST (FOO X)+(FIE Y)) has the same value as
(LIST A+B). Note that the first expression consists of a list of four
elements: the atom "LIST", the list "(FOO X)", the atom "+", and the
list "(FIE X)", whereas the second expression, (LIST A+B), consists of a
list of only two elements: the atom "LIST" and the atom "A+B". Since
(LIST (FOO X)+(FIE Y)) is indistinguishable from
(LIST (FOO X) + (FIE Y)) because spaces before or after parentheses have
10
no effect on the INTERLISP READ program, to be consistent, extra
spaces have no effect on atomic operands either. In other words, CLISP
will treat (LIST A+ B), (LIST A +B), and (LIST A + B) the same as
(LIST A+B).
23.3 Infix Operators
CLISP recognizes the arithmetic infix operators +, -, *, /, and ^.
These are converted to IPLUS, IDIFFERENCE (or in the case of unary
11
minus, IMINUS), ITIMES, IQUOTIENT, and EXPT. The usual precedence
12
rules apply (although these can be easily changed by the user), i.e.,
* has higher precedence than + so that A+B*C is the same as A+(B*C), and
both * and / are lower than ^ so that 2*X^2 is the same as 2*(X^2).
Operators of the same precedence group from left to right, e.g., A/B/C
is equivalent to (A/B)/C. Minus is binary whenever possible, i.e.,
except when it is the first operator in a list, as in (-A) or (-A), or
------------------------------------------------------------------------
10
CLISP does not use its own special READ program because this would
require the user to explicitly identify CLISP expressions, instead
of being able to intermix INTERLISP and CLISP.
11
The I in IPLUS denotes integer arithmetic, i.e., IPLUS converts its
arguments to integers, and returns an integer value. INTERLISP also
contains floating point arithmetic functions as well as mixed
arithmetic functions (see Section 13). Floating point arithmetic
functions are used in the translation if one or both of the operands
are themselves floating point numbers, e.g., X+1.5 translates as
(FPLUS X 1.5). In addition, CLISP contains a facility for declaring
which type of arithmetic is to be used, either by making a global
declaration, or by separate declarations about individual functions
or variables. See section on declarations, page 23.29.
12
The complete order of precedence for CLISP operators is given in
Figure 23-2, page 23.12.
23.8
13 14
when it immediately follows another operator, as in A*-B.
Note that grouping with parentheses can always be used to override the
normal precedence grouping, or when the user is not sure how a
particular expression will parse.
15
CLISP also recognizes as infix operators =, GT, LT, GE, and LE, as
16
well as various predicates, e.g., MEMBER, AND, OR, EQUAL, etc. AND is
higher than OR, e.g., (X OR Y AND Z) is the same as (X OR (Y AND Z)),
and both AND and OR are lower than the other infix operators, e.g.,
(X AND Y EQUAL Z) is the same as (X AND (Y EQUAL Z)). All of the infix
predicates have lower precedence than INTERLISP forms, i.e.,
(FOO X GT FIE Y) is the same as ((FOO X) GT (FIE Y)), since it is far
more common to apply a predicate to two forms, than to use a Boolean as
an argument to a function, e.g., (FOO (X GT (FIE Y))). However, again,
the user can easily change this.
Note that only single character operators, e.g., +, _, =, etc., can
appear in the interior of an atom. All other operators must be set off
from identifiers with spaces. For example, XLTY will not be recognized
------------------------------------------------------------------------
13
There are some do-what-I-mean features associated with Unary minus,
as in (LIST -X Y). See section on operation, page 23.55.
14
Note that + in front of a number will disappear when the number is
read, e.g., (FOO X +2) is indistinguishable from (FOO X 2). This
means that (FOO X +2) will not be interpreted as CLISP, or be
converted to (FOO (IPLUS X 2)). Similarly, (FOO X -2) will not be
interpreted the same as (FOO X-2). To circumvent this, always type a
space between the + or - and a number if an infix operator is
intended, e.g., write (FOO X + 2).
15
Greater Than, Less Than, Greater than or Equal to, and Less than or
Equal to, respectively. GT, LT, GE, and LE are all affected by the
same declarations as + and *, with the initial default to use
IGREATERP and ILESSP.
16
Currently the complete list is MEMBER, MEMB, FMEMB, ILESSP,
IGREATERP, LESSP, GREATERP, FGTP, EQ, NEQ, EQP, EQUAL, OR, and AND.
New infix operators can be easily added, as described in the section
on CLISP internal conventions, page 23.58. Spelling correction on
misspelled infix operators is peformed using clispinfixsplst as a
spelling list.
23.9
17
as CLISP.
: is an infix operator used in CLISP for extracting substructures from
18
lists, e.g., X:3 specifies the 3rd element of X, (FOO Y)::2 specifies
the second tail of (FOO Y), i.e., (CDDR (FOO Y)), and Z:1:2 is the
second element of the first element of Z, or (CADAR Z). Negative
numbers may be used to indicate position counting from the end of a
list, e.g., X:-1 is the last element of X, or (CAR (LAST X)), X::-1 is
19
the last tail, i.e., (LAST X).
20
_ is used to indicate assignment, e.g., X_Y translates to (SETQ X Y).
21
In conjunction with : and ::, _ can also be used to perform a more
general type of assignment, namely one involving structure modification.
For example, X:2_Y means make the second element of X be Y, in INTERLISP
------------------------------------------------------------------------
17
In some cases, DWIM will be able to diagnose this situation as a
run-on spelling error, in which case after the atom is split apart,
CLISP will be able to perform the indicated transformation.
18
The record facility, page 23.39, provides another way of extracting
substructures by allowing the user to assign names to the various
parts of the structure and then retrieve from or store into the
corresponding structure by name. The pattern match facility, page
23.31, also can be used to extract substructure. : is also used to
indicate both record and pattern match operations.
19
The interpretation of negative numbers can be explained neatly in
terms of edit commands: :-n returns what would be the current
expression after executing the command -n, and ::-n returns what
would be the current expression after executing -n followed by UP.
20
If x does not have a value, and is not the name of one of the bound
variables of the function in which it appears, spelling correction
is attempted. However, since this may simply be a case of assigning
an initial value to a new free variable, DWIM will always ask for
approval before making the correction.
21
Note that an atom of the form X_Y, appearing at the top level of a
PROG, will not be recognized as an assignment statement because it
will be interpreted as a PROG label by the INTERLISP interpreter,
and therefore will not cause an error, so DWIM and CLISP will never
get to see it. Instead, one must write (X_Y).
23.10
22 23
terms (RPLACA (CDR X) Y). Negative numbers can also be used, e.g.,
24
X:-2_Y. _ is also used to indicate assignment in record operations,
page 23.39, and pattern match operations, page 23.31.
_ has different precedence on the left from on the right. On the left,
_ is a "tight" operator, i.e., high precedence, so that A+B_C is the
same as A+(B_C). On the right, _ has broader scope so that A_B+C is the
same as A_(B+C).
On typein, $_form (alt-mode_form) is equivalent to set the "last thing
25
mentioned". For example, immediately after examining the value of
LONGVARIABLENAME, the user could set it by typing $_ followed by a form.
23.4 Prefix Operators
CLISP recognizes ' and ~ as prefix operators. ' means QUOTE when it is
the first character in an identifier, and is ignored when it is used in
the interior of an identifier. Thus, X='Y means (EQ X (QUOTE Y)), but
X=CAN'T means (EQ X CAN'T), not (EQ X CAN) followed by (QUOTE T). This
enables users to have variable and function names with ' in them (so
long as the ' is not the first character).
Following ', all operators are ignored for the rest of the identifier,
e.g., '*A means (QUOTE *A), and 'X=Y means (QUOTE X=Y), not
26
(EQ (QUOTE X) Y).
On typein, '$ (i.e., 'alt-mode) is equivalent to
(QUOTE value-of-lastword) (see Section 17). For example, after calling
prettyprint on LONGFUNCTION, the user could move its definition to FOO
------------------------------------------------------------------------
22
Note that the value of this operation is the value of rplaca, which
is the corresponding node.
23
The user can indicate he wants /rplaca and /rplacd used (undoable
version of rplaca and rplacd, see Section 22), or frplaca and
frplacd (fast versions of rplaca and rplacd, see Section 5), by
means of declarations (page 23.29). The initial default is for
rplaca and rplacd.
24
which translates to (RPLACA (NLEFT X 2) Y).
25
i.e., is equivalent to (SETQ lastword form). See Section 17.
26
To write (EQ (QUOTE X) Y), one writes Y='X, or 'X =Y. This is one
place where an extra space does make a difference.
23.11
27
by typing (MOVD '$ 'FOO).
~ means NOT. ~ can negate a form, as in ~(ASSOC X Y), or ~X, or negate
an infix operator, e.g., (A ~GT B) is the same as (A LEQ B). Note that
~A=B means (EQ (NOT A) B).
Order of Precedence of CLISP operators
'
:
28
_ (left precedence)
29
- (unary), ~
^
*, /
+, - (binary)
_ (right precedence)
=
INTERLISP forms
LT, GT, EQUAL, MEMBER, etc.
AND
OR
IF, THEN, ELSEIF, ELSE
iterative statement operators
Figure 23-2
------------------------------------------------------------------------
27
Not (MOVD $ 'FOO), which would be equivalent to
(MOVD LONGFUNCTION 'FOO), and would (probably) cause a U.B.A.
LONGFUNCTION error, nor MOVD($ FOO), which would actually move the
definition of $ to FOO, since DWIM and the spelling corrector would
never be invoked.
28
_ has a different left and right precedence, e.g., A+B_C+D is the
same as A+(B_(C+D)). In other words, _ has minimal scope on the left
and maximal scope on the right.
29
When ~ negates an operator, e.g., ~=, ~LT, the two operators are
treated as a single operator whose precedence is that of the second
operator. When ~ negates a function, e.g., (~FOO X Y), it negates
the whole form, i.e., (~(FOO X Y)).
23.12
30
23.5 Constructing Lists - the <,> operators
Angle brackets are used in CLISP to indicate list construction. The
appearance of a "<" corresponds to a "(" and indicates that a list is to
be constructed containing all the elements up to the corresponding '>'.
For example, > translates to (LIST A B (LIST C)). ! can be used
to indicate that the next expression is to be inserted in the list as a
segment, e.g., translates to (CONS A (CONS B C)) and
to (APPEND A B (LIST C)). !! is used to indicate that the
next expression is to be inserted as a segment, and furthermore, all
list structure to its right in the angle brackets is to be physically
attached to it, e.g., translates to (NCONC1 A B), and
31 32
to (NCONC A (APPEND B C)). Note that <, !, !!, and >
need not be separate atoms, for example, may be written
equally well as < A B !C >. Also, arbitrary INTERLISP or CLISP forms
may be used within angle brackets. For example, one can write
which translates to (CONS (SETQ FOO (FIE X)) Y).
CLISPIFY converts expressions in cons, list, append, nconc, nconc1,
/nconc, and /nconc1 into equivalent CLISP expressions using <, >, !, and
!!.
Note: angle brackets differ from other CLISP operators in that they act
more like brackets than operators. For example, translates to
(LIST A B (QUOTE C)) even though following ', all operators are ignored
33
for the rest of the identifier. Note however that D> is
equivalent to (LIST A B (QUOTE C>) D).
23.6 IF, THEN, ELSE
CLISP translates expressions employing IF|THEN|ELSEIF|ELSE into
equivalent conditional expressions. The segment between IF|ELSEIF and
the next THEN corresponds to the predicate of a COND clause, and the
segment between THEN and the next ELSE|ELSEIF as the consequent(s).
ELSE is the same as ELSEIF T THEN.
------------------------------------------------------------------------
30
The <,> operator was written by P.C. Jackson.
31
Not (NCONC (APPEND A B) C), which would have the same value, but
would attach C to B, and not attach either to A.
32
The user can indicate /nconc or /nconc1 be used instead of nconc and
nconc1 by declarations.
33
Only if a previous unmatched < has been seen, e.g., (PRINT 'A>B)
will print the atom A>B.
23.13
IF, THEN, ELSE, and ELSEIF are of lower precedence than all infix and
prefix operators, as well as INTERLISP forms, so that parentheses can be
34
omitted between IF|ELSEIF, and THEN. For example, (IF FOO X Y THEN --)
35
is equivalent to (IF (FOO X Y) THEN --). Similarly, CLISP treats (IF X
THEN FOO X Y ELSE --) as equivalent to (IF X THEN (FOO X Y) ELSE --)
because it does not "make sense" to evaluate a variable for effect. In
other words, even if FOO were also the name of a variable,
(COND (X FOO X Y)) doesn't make sense. Essentially, CLISP determines
whether the segment between THEN and the next ELSE|ELSEIF corresponds to
36
one form or several and acts accordingly. Thus,
(IF -- THEN (FOO X) Y ELSE --) corresponds to a clause with two
consequents. Similarly, (IF -- THEN FOO_X Y ELSE --) corresponds to a
clause with two consequents, and is equivalent to
37
(IF -- THEN (FOO_X) Y ELSE --).
23.7 Iterative Statements
The following is an example of a CLISP iterative statement:
(WHILE X_(READ)~='STOP DO (PRINT (EVAL X)))
This statement says "READ an expression and set X to it. If X is not
equal to the atom STOP, then evaluate X, print the result, and
------------------------------------------------------------------------
34
IF, THEN, ELSE, and ELSEIF can also be misspelled. Spelling
correction is performed using clispifwordsplst as a spelling list.
35
If FOO is the name of a variable, IF FOO THEN -- is translated as
(COND (FOO --)) even if FOO is also the name of a function. If the
functional interpretation is intended, FOO should be enclosed in
parentheses, e.g., IF (FOO) THEN --. Similary for
IF -- THEN FOO ELSEIF --.
36
occasionally interacting with the user to resolve ambiguous cases.
37
To write the equivalent of a singleton cond clause, i.e., a clause
with a predicate but no consequent, write either nothing following
the THEN, or omit the THEN entirely, e.g., (IF (FOO X) THEN ELSEIF -
-) or (IF (FOO X) ELSEIF --), meaning if (FOO X) is not NIL, it is
the value of the cond.
23.14
38
iterate."
The i.s. (iterative statement) in its various forms permits the user to
specify complicated iterative statements in a straightforward and
visible manner. Rather than the user having to perform the mental
transformations to an equivalent INTERLISP form using PROG, MAPC,
MAPCAR, etc., the system does it for him. The goal was to provide a
robust and tolerant facility which could "make sense" out of a wide
class of iterative statements. Accordingly, the user should not feel
obliged to read and understand in detail the description of each
operator given below in order to use iterative statements.
Currently, the following i.s. operators are implemented: FOR, BIND, OLD,
IN, ON, FROM, TO, BY, WHEN, WHILE, UNTIL, REPEATWHILE, REPEATUNTIL,
UNLESS, COLLECT, JOIN, DO, SUM, COUNT, ALWAYS, NEVER, THEREIS, AS,
FIRST, FINALLY, EACHTIME. Their function is explained below. New
operators can be defined as described on page 23.24. Misspellings of
39
operators are recognized and corrected. The order of appearance of
40
operators is never important; CLISP scans the entire statement before
it begins to construct the equivalent INTERLISP form.
DO form specifies what is to be done at each iteration. DO
with no other operator specifies an infinite loop.
If some explicit or implicit terminating condition is
specified, the value of the i.s. is NIL. Translate
to MAPC or MAP whenever possible.
COLLECT form like DO, except specifies that the value of form at
each iteration is to be collected in a list, which is
returned as the value of the i.s. when it terminates.
Translates to MAPCAR, MAPLIST or SUBSET whenever
------------------------------------------------------------------------
38
The statement translates to:
(PROG ($$VAL) $$LP(COND ((EQ (SETQ X (READ))(QUOTE STOP)) (RETURN
$$VAL)))
(PRINT (EVAL X)) $$ITERATE (GO $$LP))
39
using the spelling list clispforwordsplst.
40
DWIM and CLISP are invoked on iterative statements because car of
the i.s. is not the name of a function, and hence generates an
error. If the user defines a function by the same name as an i.s.
operator, e.g., WHILE, TO, etc., the operator will no longer have
the CLISP interpretation when it appears as car of a form, although
it will continue to be treated as an i.s. operator if it appears in
the interior of an i.s. To alert the user, a warning message is
printed, e.g., (WHILE DEFINED, THEREFORE DISABLED IN CLISP).
23.15
41
possible.
JOIN form like DO, except that the values are NCONCed.
42
Translates to MAPCONC or MAPCON whenever possible.
SUM form like DO, except specifies that the values of form at
each iteration be added together and returned as the
value of the i.s., e.g., (FOR I FROM 1 TO 5 SUM I^2)
43
is equal to 1+4+9+16+25.
COUNT form like DO, except counts number of times that form is
true, and returns that count as its value.
ALWAYS form like DO, except returns T if the value of form is
non-NIL for all iterations (returns NIL as soon as
the value of form is NIL), e.g.,
(FOR X IN Y ALWAYS (ATOM X)) is the same as
(EVERY Y (FUNCTION ATOM)).
NEVER form like ALWAYS, except returns T if the value of form is
never true, i.e., NEVER form is the same as ALWAYS
~form.
THEREIS form returns the first value of the i.v. for which form is
non-NIL, e.g., (FOR X IN Y THEREIS NUMBERP) returns
the first number in Y, and is equivalent to
------------------------------------------------------------------------
41
when COLLECT translates to a PROG, e.g., a WHILE operator appears in
the iterative statement, the translation employs an open tconc using
two pointers similar to that used by the compiler for compiling
mapcar.
42
/NCONC, /MAPCONC, and /MAPCON are used when the declaration UNDOABLE
is in effect.
43
iplus, fplus, or plus will be used for the translation depending on
the declarations in effect.
23.16
44
(CAR (SOME Y (FUNCTION NUMBERP))).
DO, COLLECT, JOIN, SUM, ALWAYS, NEVER, and THEREIS are examples of a
certain kind of i.s. operator called an i.s.type. The i.s.type
specifies what is to be done at each iteration. Its operand is called
the body of the iterative statement. Each i.s. must have one and only
one i.s.type.
FOR var specifies the variable of iteration, or i.v., which
is used in conjunction with IN, ON, FROM, TO, and BY.
The variable is rebound for the scope of the i.s.,
except when modified by OLD as described below.
FOR vars vars a list of variables, e.g., FOR (X Y Z) IN --.
The first variable is the i.v., the rest are dummy
variables. See BIND below.
OLD var indicates var is not to be rebound, e.g.,
(FOR OLD X FROM 1 TO N DO -- UNTIL --),
BIND var, vars used to specify dummy variables, e.g.,
FOR (X Y Z) IN -- is equivalent to
FOR X BIND (Y Z) IN --. BIND can be used without
FOR. For example, in the i.s. shown on page 23.14, X
could be made local by writing
(BIND X WHILE X_(READ)~='STOP...).
Note: FOR, OLD, and BIND variables can be initialized by using _,
e.g., (FOR OLD (X_form) BIND (Y_form)...).
IN form specifies that the i.s. is to iterate down a list
with the i.v. being reset to the corresponding
element at each iteration. For example,
FOR X IN Y DO -- corresponds to
(MAPC Y (FUNCTION (LAMBDA (X) --))). If no i.v. has
been specified, a dummy is supplied, e.g.,
IN Y COLLECT CADR is equivalent to
(MAPCAR Y (FUNCTION CADR)).
ON form same as IN except that the i.v. is reset to the
------------------------------------------------------------------------
44
THEREIS returns the i.v. instead of the tail (as does the function
some) in order to provide an interpretation consistent with
statements such as (FOR I FROM 1 TO 10 THEREIS --), where there is
no tail. Note that (SOME Y (FUNCTION NUMBERP)) is equivalent to
(FOR X ON Y THEREIS (NUMBERP (CAR X))).
23.17
corresponding tail at each iteration. Thus IN
corresponds to MAPC, MAPCAR, and MAPCONC, while ON
corresponds to MAP, MAPLIST, and MAPCON.
IN OLD var specifies that the i.s. is to iterate down var, with
var itself being reset to the corresponding tail at
each iteration, e.g., after
(FOR X IN OLD L DO -- UNTIL --) finishes, L will be
some tail of its original value.
IN OLD (var_form) same as IN OLD var, except var is first set to value
of form.
ON OLD var same as IN OLD var except the i.v. is reset to the
current value of var at each iteration, instead of to
car[var].
ON OLD (var_form) same as ON OLD var, except var is first set to value
of form.
WHEN form provides a way of excepting certain iterations. For
example, (FOR X IN Y COLLECT X WHEN NUMBERP X)
collects only the elements of Y that are numbers.
UNLESS form same as WHEN except for the difference in sign, i.e.,
WHEN Z is the same as UNLESS ~Z.
WHILE form provides a way of terminating the i.s. WHILE form
evaluates form before each iteration, and if the
value is NIL, exits.
UNTIL form Same as WHILE except for difference in sign, i.e.,
WHILE form is equivalent to UNTIL ~form.
UNTIL n n a number, equivalent to UNTIL (i.v. GT n).
REPEATWHILE form same as WHILE except the test is performed after the
evalution of the body, but before the i.v. is reset
for the next iteration.
REPEATUNTIL form/n same as UNTIL, except the test is performed after the
evaluation of the body.
FROM form is used to specify an initial value for a numerical
i.v. The i.v. is automatically incremented by 1
after each iteration (unless BY is specified). If no
23.18
i.v. has been specified, a dummy i.v. is supplied and
initialized, e.g., (COLLECT SQRT FROM 2 TO 5) returns
(1.414 1.732 2.0 2.236).
TO form is used to specify the final value for a numerical
i.v. If FROM is not specified, the i.v. is
initialized to 1. If no i.v. has been specified, a
dummy i.v. is supplied and initialized. If BY is not
specified, the i.v. is automatically incremented by 1
45
after each iteration. When the i.v. is definitely
being incremented, i.e., either BY is not specified,
or its operand is a positive number, the i.s.
terminates when the i.v. exceeds the value of form
(which is reevaluated each iteration) e.g.,
(FOR X FROM 1 TO 10 --), is equivalent to
(FOR X FROM 1 UNTIL (X GT 10) --).
Similarly, when the i.v. is definitely being
decremented the i.s. terminates when the i.v. becomes
less than the value of form (see description of BY).
BY form (with IN/ON)
If IN or ON have been specified, the value of form
determines the tail for the next iteration, which in
turn determines the value for the i.v. as described
earlier, i.e., the new i.v. is car of the tail for
IN, the tail itself for ON. In conjunction with IN,
the user can refer to the current tail within form by
using the i.v. or the operand for IN/ON, e.g.,
(FOR Z IN L BY (CDDR Z) ...) or
(FOR Z IN L BY (CDDR L) ...). At translation time,
the name of the internal variable which holds the
value of the current tail is substituted for the i.v.
throughout form. For example,
(FOR X IN Y BY (CDR (MEMB 'FOO (CDR X))) COLLECT X)
specifies that after each iteration, cdr of the
current tail is to be searched for the atom FOO, and
(cdr of) this latter tail to be used for the next
iteration.
BY form (without IN/ON)
If IN or ON have not been used, BY specifies how the
i.v. itself is reset at each iteration. If FROM or
TO have been specified, the i.v. is known to be
numerical, so the new i.v. is computed by adding the
------------------------------------------------------------------------
45
except when both the operands to TO and FROM are numbers, and TO's
operand is less than FROM's operand, e.g., FROM 10 TO 1, in which
case the i.v. is decremented by 1 after each iteration. In this
case, the i.s. terminates when the i.v. becomes less than the value
of form.
23.19
value of form (which is reevaluated each iteration)
to the current value of the i.v., e.g.,
(FOR N FROM 1 TO 10 BY 2 COLLECT N) makes a list of
the first five odd numbers.
46
If form is a positive number, the i.s. terminates
when the value of the i.v. exceeds the value of TO's
operand. If form is a negative number, the i.s.
terminates when the value of the i.v. becomes less
than TO's operand, e.g.,
(FOR I FROM N TO M BY -2 UNTIL (I LT M) ...).
Otherwise, the terminating condition for each
iteration depends on the value of form for that
iteration: if form < 0, the test is whether the i.v.
is less than TO's operand, if form > 0 the test is
whether the i.v. exceeds TO's operand, otherwise if
47
form=0, the i.s. terminates unconditionally.
If FROM or TO have not been specified, the i.v. is
simply reset to the value of form after each
iteration, e.g., (FOR I FROM N BY 2 ...) is
equivalent to (FOR I_N BY (IPLUS I 2) ...).
FIRST form form is evaluated once before the first iteration,
e.g.,
(FOR X Y Z IN L -- FIRST (FOO Y Z)), and FOO could be
used to initialize Y and Z.
FINALLY form form is evaluated after the i.s. terminates. For
example,
(FOR X IN L BIND Y_0 DO (IF ATOM X THEN Y_Y+1)
FINALLY (RETURN Y)) will return the number of atoms
in L.
EACHTIME form form is evaluated at the beginning of each iteration
before, and regardless of, any testing. For example,
consider (FOR I FROM 1 TO N DO (... (FOO I) ...)
UNLESS (... (FOO I) ...) UNTIL (... (FOO I) ...)).
The user might want to set a temporary variable to
the value of (FOO I) in order to avoid computing it
three times each iteration. However, without knowing
------------------------------------------------------------------------
46
form itself, not its value, which in general CLISP would have no way
of knowing in advance.
47
A temporary variable is used so that x is only evaluated once.
However, code for TO's operand appears twice in the translation,
even though it is evaluated only once.
23.20
the translation, he would not know whether to put the
assignment in the operand to DO, UNLESS, or UNTIL,
i.e., which one would be executed first. He can
avoid this problem by simply writing EACHTIME J_(FOO
I).
AS var is used to specify an iterative statement involving
more than one iterative variable, e.g.,
(FOR X IN Y AS U IN V DO --) corresponds to map2c.
The i.s. terminates when any of the terminating
conditions are met, e.g.,
(FOR X IN Y AS I FROM I TO 10 COLLECT X) makes a list
of the first ten elements of Y, or however many
elements there are on Y if less than 10.
The operand to AS, var, specifies the new i.v. For
the remainder of the i.s., or until another AS is
encountered, all operators refer to the new i.v. For
example, (FOR I FROM I TO N1 AS J FROM 1 TO N2 BY 2
AS K FROM N3 TO 1 BY -1 --) terminates when I exceeds
N1, or J exceeds N2, or K becomes less than 1. After
each iteration, I is incremented by 1, J by 2, and K
by -1.
Miscellaneous
1. Lowercase versions of all i.s. operators are equivalent to the
uppercase, e.g., (for X in Y ...).
2. Each i.s. operator is of lower precedence than all INTERLISP forms,
so parentheses around the operands can be omitted, and will be
supplied where necessary, e.g., BIND (X Y Z) can be written
BIND X Y Z, OLD (X_form) as OLD X_form, WHEN (NUMBERP X) as
WHEN NUMBERP X, etc.
3. RETURN or GO may be used in any operand. (In this case, the
translation of the iterative statement will always be in the form of
a PROG, never a mapping function.) RETURN means return from the i.s.
(with the indicated value), not from the function in which the i.s
appears. GO refers to a label elsewhere in the function in which
the i.s. appears, except for the labels $$LP,$$ITERATE, and $$OUT
which are reserved, as described in 6 below.
4. In the case of FIRST, FINALLY, EACHTIME, or one of the i.s.oprs,
e.g., DO, COLLECT, SUM, etc., the operand can consist of more than
one form, e.g., COLLECT (PRINT X:1) X:2, in which case a PROGN is
supplied.
5. Each operand can be the name of a function, in which case it is
23.21
48 49 50
applied to the (last) i.v., e.g.,
FOR X IN Y DO PRINT WHEN NUMBERP, is the same as
FOR X IN Y DO (PRINT X) WHEN (NUMBERP X). Note that the i.v. need
not be explicitly specified, e.g., IN Y DO PRINT WHEN NUMBERP will
work.
6. While the exact form of the translation of an iterative statement
depends on which operators are present, a PROG will always be used
whenever the i.s. specifies dummy variables, i.e., if a BIND
operator appears, or there is more than one variable specified by a
FOR operator, or a GO, RETURN, or a reference to the variable $$VAL
appears in any of the operands. When a PROG is used, the form of
the translation is:
(PROG variables
{initialize}
$$LP {eachtime}
{test}
{body}
$$ITERATE
{aftertest}
{update}
(GO $$LP)
$$OUT {finalize}
(RETURN $$VAL))
where {test} corresponds to that portion of the loop that tests for
termination and also for those iterations for which {body} is not
going to be executed, (as indicated by a WHEN or UNLESS); {body}
corresponds to the operand of the i.s.opr, e.g., DO, COLLECT, etc.;
{aftertest} corresponds to those tests for termination specified by
REPEATWHILE or REPEATUNTIL; and {update} corresponds to that part
that resets the tail, increments the counter, etc. in preparation
for the next iteration. {initialize}, {finalize}, and {eachtime}
correspond to the operands of FIRST, FINALLY, and EACHTIME, if any.
Note that since {body} always appears at the top level of the PROG,
the user can insert labels in {body}, and go to them from within
{body} or from other i.s. operands, e.g.,
------------------------------------------------------------------------
48
For i.s.oprs, e.g., DO, COLLECT, JOIN, the function is always
applied to the first i.v. in the i.s., whether explicity named or
not. For example, (IN Y AS I FROM 1 TO 10 DO PRINT) prints elements
on Y, not integers between 1 and 10.
49
Note that this feature does not make much sense for FOR, OLD, BIND,
IN, or ON, since they "operate" before the loop starts, when the
i.v. may not even be bound.
50
In the case of BY in conjunction with IN, the function is applied to
the current tail e.g., FOR X IN Y BY CDDR ..., is the same as FOR X
IN Y BY (CDDR X)... See page 23.19.
23.22
51
(FOR X IN Y FIRST (GO A) DO (FOO) A (FIE)). The user can also go
to $$LP, $$ITERATE, or $$OUT, or explicitly set $$VAL.
Errors in Iterative Statements
An error will be generated and an appropriate diagnostic printed if any
of the following conditions hold:
l. Operator with null operand, i.e., two adjacent operators, as in FOR
X IN Y UNTIL DO --
2. Operand consisting of more than one form (except as operand to
FIRST, FINALLY, or one of the i.s.oprs), e.g., FOR X IN Y (PRINT X)
COLLECT --.
3. IN, ON, FROM, TO, or BY appear twice in same i.s.
4. Both IN and ON used on same i.v.
5. FROM or TO used with IN or ON on same i.v.
6. More than one i.s.type, e.g., a DO and a SUM.
In 3, 4, or 5, an error is not generated if an intervening AS occurs.
If an error occurs, the i.s. is left unchanged.
If no DO, COLLECT, JOIN or any of the other i.s.oprs are specified,
CLISP will first attempt to find an operand consisting of more than one
form, e.g., FOR X IN Y (PRINT X) WHEN ATOM X, and in this case will
insert a DO after the first form. (In this case, condition 2 is not
considered to be met, and an error is not generated.) If CLISP cannot
find such an operand, and no WHILE or UNTIL appears in the i.s., a
warning message is printed: NO DO, COLLECT, OR JOIN: followed by the
i.s.
Similarly, if no terminating condition is detected, i.e., no IN, ON,
52
WHILE, UNTIL, TO, or a RETURN or GO, a warning message is printed :
POSSIBLE NON-TERMINATING ITERATIVE STATEMENT: followed by the i.s.
However, since the user may be planning to terminate the i.s. via an
error, control-E, or a retfrom from a lower function, the i.s. is still
translated.
------------------------------------------------------------------------
51
However, since {body} is dwimified as a list of forms, the label(s)
should be added to the dummy variables for the iterative statement
in order to prevent their being dwimified and possibly "corrected",
e.g., (FOR X IN Y BIND A FIRST (GO A) DO (FOO) A (FIE)).
52
unless the value of clispi.s.gag is T. clispi.s.gag is initially
NIL.
23.23
Defining New Iterative Statement Operators
The following function is available for defining new iterative statement
operators:
i.s.opr[name;form;others;evalflg]
name is the name of the new i.s.opr. If form is
not NIL, name will be a new i.s.type, and form
53
its body.
For example, for COLLECT, form would be (SETQ $$VAL (NCONC1 $$VAL BODY))
54
For SUM, form would be ($$VAL_$$VAL+BODY), others would be (FIRST
$$VAL_0).
55
For NEVER: (IF BODY THEN $$VAL_NIL (GO $$OUT))), and for
THEREIS: (IF BODY THEN $$VAL_I.V. (GO $$OUT)).
others is an (optional) list of additional i.s.
operators and operands which will be added to
the i.s. at the place where name appears. If
form is NIL, name is a new i.s.opr defined
entirely by others.
In both form and others, $$VAL can be used to
reference the value to be returned by the i.s.,
I.V. to reference the current i.v., and BODY to
56
reference name's operand.
If evalflg is T, form and others are evaluated
------------------------------------------------------------------------
53
The i.s.type is the i.s.opr that specifies what is to be done at
each iteration, e.g., performing an operation (DO), collecting
values on a list (COLLECT), adding numbers (SUM), searching for a
particular condition (THEREIS), etc. Each i.s. can have one and
only one i.s. type.
54
$$VAL+BODY is used instead of (IPLUS $$VAL BODY) so that the choice
of function used in the translation, i.e., iplus, fplus, or plus,
will be determined by the declarations then in effect.
55
(IF BODY THEN RETURN NIL) would exit from the i.s. immediately and
therefore not execute the operations specified via a FINALLY (if
any).
56
In other words, the current i.v. will be substituted for all
instances of I.V. and name's operand will be substituted for all
instances of BODY throughout form and others.
23.24
at translation time, and their values used as
described above.
Examples:
(1) To define RCOLLECT, a version of COLLECT which uses cons instead of
nconc1 and then reverses the list of values:
i.s.opr[RCOLLECT;($$VAL_(CONS BODY $$VAL));
(FINALLY (RETURN (DREVERSE $$VAL)))]
(2) To define TCOLLECT, a version of COLLECT which uses tconc:
i.s.opr[TCOLLECT;(TCONC $$VAL BODY);
(FIRST $$VAL_(CONS) FINALLY (RETURN (CAR $$VAL)))]
(3) To define PRODUCT:
i.s.opr[PRODUCT;($$VAL_$$VAL*BODY);(FIRST $$VAL_1)]
(4) To define UPTO, a version of TO whose operand is evaluated only
once: i.s.opr[UPTO;NIL;(BIND $$FOO_BODY TO $$FOO)].
i.s.opr can also be used to define synonyms for already defined i.s.
operators by calling i.s.opr with form an atom, e.g.,
i.s.opr[WHERE;WHEN] makes WHERE be the same as WHEN. Similarly,
following i.s.opr[ISTHERE;THEREIS], one can write (ISTHERE ATOM IN Y),
and following i.s.opr[FIND;FOR] and i.s.opr[SUCHTHAT;THEREIS], one can
57
write (FIND X IN Y SUCHTHAT X MEMBER Z).
For convenience, there is a prettydefmacro, I.S.OPRS, which dumps
i.s.oprs, e.g., (I.S.OPRS PRODUCT UPTO) as a prettycom will print
suitable expressions so that these iterative statement operators will be
(re)defined when the file is loaded.
This completes the description of iterative statements.
23.8 English Phrases
CLISP also recognizes a limited but expandable set of english-like
constructions of the form "A is B", e.g., FOO IS A NUMBER, Z IS NOT A
STRING, (CDDR X) ISN'T A TAIL OF Y. Both subject and relation can be
"distributed", e.g., X AND Y ARE ATOMIC is equivalent to X IS ATOMIC AND
Y IS ATOMIC. Similarly, Z IS AN ARRAY OR A LIST is equivalent to Z IS
AN ARRAY OR Z IS A LIST, and A AND B ARE NUMBERS AND LESS THAN 5 AND GT
0 is equivalent to the conjunction of the indicated six predicates.
------------------------------------------------------------------------
57
In the current system, WHERE is synonymous with WHEN, SUCHTHAT and
ISTHERE with THEREIS, and FIND with FOR.
23.25
These constructions are translated to the corresponding LISP expressions
when they are run or dwimified. In addition, clispify will convert LISP
forms into "english" when clispifyenglshflg is T.
Clisp currently knows about the following unary relations in their
singular and plural forms: ARRAY, ATOM, ATOMIC, FLOATING POINT NUMBER,
LIST, LITATOM, LITERAL ATOM, NEGATIVE, NIL (i.e., X IS NIL), NULL,
NUMBER, SMALL INTEGER, SMALL NUMBER, STRING; and the following binary
relations in their singular and plural forms: EQ TO, EQUAL TO, GEQ,
GREATER THAN, GT, LESS THAN, LT, MEMB OF, MEMBER OF, TAIL OF. All
relationships can be negated with either NOT, N, or N'T, e.g., X IS
~LESS THEN Y, A AND B AREN'T ATOMIC. New relations can be defined with
the function newisword.
newisword[sing;plu;form;vars]
sing is the singular form of the new english
construct, plu the plural without the subject.
form is the form the singular construct
translates to, and vars the parameters.
For example, "SMALL INTEGER" could have been defined by newisword[(X IS
A SMALL INTEGER); (ARE SMALL INTEGERS); (SMALLP X); (X)] and "TAIL OF"
by newisword[(X IS A TAIL OF Y); (ARE TAILS OF Y); (TAILP X Y); (X Y)].
23.9 CLISP Translations
The translation of infix operators and IF|THEN|ELSE statements are
handled in CLISP by replacing the CLISP expression with the
corresponding INTERLISP expression, and discarding the original CLISP,
58
because (1) the CLISP expression is easily recomputable (by clispify),
and (2) the INTERLISP expressions are simple and straightforward. In
addition to saving the space required to retain both the CLISP and the
INTERLISP, another reason for discarding the original CLISP is that it
may contain errors that were corrected in the course of translation,
e.g., the user writes FOO_FOOO:1, N*8FOO X), etc. If the original CLISP
were retained, either the user would have to go back and fix these
errors by hand, thereby negating the advantage of having DWIM perform
these corrections, or else DWIM would have to keep correcting these
errors over and over.
Where (1) or (2) are not the case, e.g., with iterative statements,
------------------------------------------------------------------------
58
Note that clispify is sufficiently fast that it is practical for the
user to configure his INTERLISP system so that all expressions are
automatically clispifyed immediately before they are presented to
him. For example, he can define an edit macro to use in place of P
which calls clispify on the current expression before printing it.
Similarly, he can inform prettyprint to call clispify on each
expression before printing it, etc.
23.26
59
pattern matches, record expressions, etc. the original CLISP is
retained (or a slightly modified version thereof), and the translation
60 61
is stored elsewhere, usually in clisparray, a hash array. The
interpreter automatically checks this array using gethash when given a
62
form car of which is not a function. Similarly, the compiler performs
a gethash when given a form it does not recognize to see if it has a
translation, which is then compiled instead of the form. Whenever the
user changes a CLISP expresson by editing it, the editor automatically
deletes its translation (if one exists), so that the next time it is
63
evaluated or dwimified, the expression will be retranslated. The
function ppt and the edit commands PPT and CLISP: are available for
examining translations, see page 23.64. Similarly, if prettytranflg is
T, prettyprint will print the translations instead of the corresponding
------------------------------------------------------------------------
59
The handling of translations for IF|THEN|ELSE statements is
determined by the value of clispiftranflg. If T, the translations
are stored elsewhere, and the (modified) CLISP retained as described
below. If NIL, the corresponding COND replaces the IF|THEN|ELSE
expression. The initial value of clispiftranflg is NIL.
60
The actual storing of the translation is performed by the function
clisptran, page 23.61.
61
The user can also indicate that he wants the original clisp retained
by embedding it in an expression of the form (CLISP . clisp-
expression), e.g., (CLISP X:5:3) or (CLISP ). In such
cases, the translation will be stored remotely as described in the
text. Furthermore, such expressions will be treated as clisp even
if infix and prefix transformations have been disabled by setting
clispflg to NIL, as described on page 23.61. In other words, the
user can instruct the system to interpret as clisp infix or prefix
constructs only those expressions that are specifically flagged as
such.
62
CLISP translations can also be used to supply an interpretation for
funtion objects, as well as forms, either for function objects that
are used openly, i.e., appearing as car of form, function objects
that are explicitly applyed, as with arguments to mapping functions,
or function objects contained in function definition cells. In all
cases, if car of the object is not LAMBDA or NLAMBDA, the
interpreter and compiler will check clisparray.
63
If the value of clispretranflg is T, dwimify will also (re)translate
any expressions which have translations stored remotely. The initial
value of clispretranflg is NIL.
23.27
64
CLISP expression.
65
If clisparray is NIL, translations are implemented instead by
replacing the CLISP expression by an expression of the form
66
(CLISP% translation . CLISP-expression), e.g.,
(FOR X IN Y COLLECT (CAR X)) would be replaced by
(CLISP% (MAPCAR Y (FUNCTION CAR)) FOR X IN Y COLLECT (CAR X)). Both
the editor and prettyprint know about CLISP% expressions and treat them
specially by suppressing the translations: Prettyprint prints just the
CLISP (unless prettytranflg=T, as described below), while the editor
makes the translation completely invisible, e.g., if the current
expression were the above CLISP% expression, F MAPCAR would fail to
find the MAPCAR, and (3 ON) would replace IN with ON, i.e., the editor
operates as though both the CLISP% and the MAPCAR were not there. As
with translations implemented via clisparray, if the CLISP expression is
changed by editing it, the translation is automatically deleted.
CLISP% expressions will interpret and compile correctly: CLISP% is
defined as an nlambda nospread function with an appropriate compiler
macro. Note that if the user sets clisparray to NIL, he can then break,
trace, or advise CLISP% to monitor the evaluation of iterative
statements, pattern matches, and record operations. This technique will
work even if clisparray was not NIL at the time the expressions were
originally translated, since setting clisparray to NIL will effectively
delete the translations, thereby causing the CLISP expressions to be
retranslated when they are first encountered. Note that if the user only
wishes to monitor the CLISP in a certain function, he can accomplish
this by embedding its definition in (RESETVAR CLISPARRAY NIL *).
If a CLISP% expression is encountered and clisparray is not NIL, the
translation is transferred to the hash array, and the CLISP% expression
replaced by just the CLISP. Setting prettytranflg to CLISP% causes
prettyprint to print CLISP expressions that have been translated in the
------------------------------------------------------------------------
64
Note that the user can always examine the translation himself by
performing (GETHASH expression CLISPARRAY).
65
clisparray is initially NIL, and #clisparray is its size. The first
time a translation is performed, a hash array of this size is
created. Therefore to disable clisparray, both it and #clisparray
should be set to NIL.
66
CLISP% is an atom consisting of the six characters C, L, I, S, P,
and space, which must be preceded by the escape character % in order
for it to be included as a part of an identifier. The intent was to
deliberately make this atom hard to type so as to make it unlikely
that it would otherwise appear in a user's program or data, since
the editor and prettyprint treat it very specially, as described
above.
23.28
form of (CLISP% translation . CLISP-expression), even if the
translation is currently stored in clisparray. These two features
together provide the user with a way of dumping CLISP expressions
together with their translations so that when reloaded (and run or
dwimified), the translations will automatically be transferred to
clisparray.
In summary, if prettytranflg=NIL, only the CLISP is printed (used for
producing listings). If prettytranflg=T, only the translation is
printed (used for exporting programs to systems that do not provide
67
CLISP, and to examine translations for debugging purposes). If
prettytranflg=CLISP% , an expression of the form
(CLISP% translation . CLISP) is printed, (used for dumping both CLISP
and translations). The preferred method of storing translations is in
clisparray, so that if any CLISP% expressions are converted while
clisparray is not NIL, they will automatically be converted so as to use
clisparray. If clisparray=NIL, they will be left alone, and
furthermore, new translations will be implemented using CLISP%
expressions.
23.10 Declarations
Declarations are used to affect the choice of INTERLISP function used as
the translation of a particular operator. For example, A+B can be
translated as either (IPLUS A B), (FPLUS A B), or (PLUS A B), depending
on the declaration in effect. Similarly X:1_Y can mean (RPLACA X Y),
(FRPLACA X Y), or (/RPLACA X Y), and either (NCONC1 A B) or
(/NCONC1 A B). The table below gives the declarations available in
CLISP, and the INTERLISP functions they indicate. The choice of
function on all CLISP transformations are affected by these
declarations, i.e., iterative statements, pattern matches, record
operations, as well as infix and prefix operators.
The user can make (change) a global declaration by calling the function
CLISPDEC and giving it as its argument a list of declarations, e.g.,
(CLISPDEC (QUOTE (FLOATING UNDOABLE))). Changing a global declaration
does not affect the speed of subsequent CLISP transformations, since all
CLISP transformation are table driven (i.e., property list), and global
declarations are accomplished by making the appropriate internal changes
to CLISP at the time of the declaration. If a function employs local
declarations (described below), there will be a slight loss in
efficiency owing to the fact that for each CLISP transformation, the
declaration list must be searched for possibly relevant declarations.
Declarations are implemented in the order that they are given, so that
later declarations override earlier ones. For example, the declaration
FAST specifies that FRPLACA, FRPLACD, FMEMB, and FLAST be used in place
of RPLACA, RPLACD, MEMB, and LAST; the declaration RPLACA specifies that
RPLACA be used. Therefore, the declarations (FAST RPLACA RPLACD) will
cause FMEMB, FLAST, RPLACA, and RPLACD to be used.
------------------------------------------------------------------------
67
Note that makefile will reset prettytranflg to T, using resetvar,
when called with the option NOCLISP.
23.29
The initial global declaration is INTEGER and STANDARD.
Table of Declarations
Declaration INTERLISP functions to be used
INTEGER or FIXED IPLUS, IMINUS, IDIFFERENCE, ITIMES, IQUOTIENT,
ILESSP, IGREATERP
FLOATING FPLUS, FMINUS, FDIFFERENCE, FTIMES, FQUOTIENT,
LESSP, FGTP
MIXED PLUS, MINUS, DIFFERENCE, TIMES, QUOTIENT, LESSP,
GREATERP
FAST FRPLACA, FRPLACD, FMEMB, FLAST, FASSOC
UNDOABLE /RPLACA, /RPLACD, /NCONC, /NCONC1, /MAPCONC,
/MAPCON
STANDARD RPLACA, RPLACD, MEMB, LAST, ASSOC, NCONC,
NCONC1, MAPCONC, MAPCON
RPLACA, RPLACD, corresponding function
/RPLACA, ...
Local Declarations
The user can also make declarations affecting a selected function or
functions by inserting an expression of the form (CLISP: . declarations)
immediately following the argument list, i.e., as CADDR of the
definition. Such local declarations take precedence over global
declarations. Declarations affecting selected variables can be
indicated by lists, where the first element is the name of a variable,
and the rest of the list the declarations for that variable. For
example, (CLISP: FLOATING (X INTEGER)) specifies that in this function
integer arithmetic be used for computations involving X, and floating
68
arithmetic for all other computations. The user can also make local
record declarations by inserting a record declaration, e.g.,
(RECORD --), (ARRAYRECORD --), etc., in the local declaration list.
Local record declarations override global record declarations for the
function in which they appear. Local declarations can also be used to
------------------------------------------------------------------------
68
'involving' means where the variable itself is an operand. For
example, with the declaration (FLOATING (X INTEGER)) in effect,
(FOO X)+(FIE X) would translate to FPLUS, i.e., use floating
arithmetic, even though X appears somewhere inside of the operands,
whereas X+(FIE X) would translate to IPLUS. If there are
declarations involving both operands, e.g., X+Y, with (X FLOATING)
(Y INTEGER), whichever appears first in the declaration list will be
used.
23.30
override the global setting of certain DWIM/CLISP parameters effective
only for transformations within that function, by including in the local
declaration an expression of the form (variable = value), e.g.,
(PATVARDEFAULT = QUOTE).
The CLISP: expression is converted to a comment of a special form
recognized by CLISP. Whenever a CLISP transformation that is affected
by declarations is about to be performed in a function, this comment
will be searched for a relevant declaration, and if one is found, the
corresponding function will be used. Otherwise, if none are found, the
global declaration(s) currently in effect will be used.
Local declarations are effective in the order that they are given, so
that later declarations can be used to override earlier ones, e.g.,
(CLISP: FAST RPLACA RPLACD) specifies that FMEMB, FLAST, RPLACA, and
RPLACD be used. An exception to this is that declarations for specific
variables take precedence of general, function-wide declarations,
regardless of the order of appearance, as in
(CLISP: (X INTEGER) FLOATING).
Clispify also checks the declarations in effect before selecting an
infix operator to ensure that the corresponding CLISP construct would in
fact translate back to this form. For example, if a FLOATING
declaration is in effect, clispify will convert (FPLUS X Y) to X+Y, but
leave (IPLUS X Y) as is. Note that if (FPLUS X Y) is CLISPIFYed while a
FLOATING declaration is under effect, and then the declaration is
changed to INTEGER, when X+Y is translated back to INTERLISP, it will
become (IPLUS X Y).
69
23.11 The Pattern Match Compiler
CLISP contains a fairly general pattern match facility. The purpose of
this pattern match facility is to make more convenient the specifying of
certain tests that would otherwise be clumsy to write (and not as
intelligible), by allowing the user to give instead a pattern which the
datum is supposed to match. Essentially, the user writes "Does the
(expression) X look like (the pattern) P?" For example, X:(& 'A -- 'B)
asks whether the second element of X is an A, and the last element a B.
The implementation of the matching is performed by computing (once) the
equivalent INTERLISP expression which will perform the indicated
operation, and substituting this for the pattern, and not by invoking
each time a general purpose capability such as that found in FLIP or
PLANNER. For example, the translation of X:(& 'A -- 'B) is:
(AND (EQ (CADR X) (QUOTE A)) (EQ (CAR (LAST X)) (QUOTE B))). Thus the
CLISP pattern match facility is really a Pattern Compiler, and the
emphasis in its design and implementation has been more on the
efficiency of object code than on generality and sophistication of its
matching capabilities. The goal was to provide a facility that could
------------------------------------------------------------------------
69
The pattern match compiler was written by L. M. Masinter.
23.31
and would be used even where efficiency was paramount, e.g., in inner
loops. As a result, the CLISP pattern match facility does not contain
(yet) some of the more esoteric features of other pattern match
languages, such as repeated patterns, disjunctive and conjunctive
patterns, recursion, etc. However, the user can be confident that what
facilities it does provide will result in INTERLISP expressions
70
comparable to those he would generate by hand.
The syntax for pattern match expressions is form:pattern, where pattern
is a list as described below. As with iterative statements, the
translation of patterns, i.e., the corresponding INTERLISP expressions,
are stored in clisparray, a hash array, as described on page 23.26. The
original expression, form:pattern, is replaced by an expression of the
form (MATCH form WITH pattern). CLISP also recognizes expressions input
in this form.
If form appears more than once in the translation, and it is not either
a variable, or an expression that is easy to (re)compute, such as
(CAR Y), (CDDR Z), etc., a dummy variable will be generated and bound to
the value of form so that form is not evaluated a multiple number of
times. For example, the translation of (FOO X):($ 'A $) is simply
(MEMB (QUOTE A) (FOO X)), while the translation of (FOO X):('A 'B --)
is:
[PROG ($$2) (RETURN
(AND (EQ (CAR (SETQ $$2 (FOO X)))
(QUOTE A))
(EQ (CADR $$2) (QUOTE B].
In the interests of efficiency, the pattern match compiler assumes that
all lists end in NIL, i.e., there are no LISTP checks inserted in the
translation to check tails. For example, the translation of X:('A & --)
is (AND (EQ (CAR X) (QUOTE A)) (CDR X)), which will match with (A B) as
well as (A . B). Similarly, the pattern match compiler does not insert
LISTP checks on elements, e.g., X:(('A --) --) translates simply as
71
(EQ (CAAR X) (QUOTE A)), and X:(($1 $1 --) --) as (CDAR X). Note that
the user can explicitly insert LISTP checks himself by using @, as
described on page 23.34, e.g., X:(($1 $1 --)@LISTP --) translates as
(CDR (LISTP (CAR X))).
------------------------------------------------------------------------
70
Wherever possible, already existing INTERLISP functions are used in
the translation, e.g., the translation of ($ 'A $) uses MEMB,
($ ('A $) $) uses ASSOC, etc.
71
The insertion of LISTP checks for elements is controlled by the
variable patlistpcheck. When patlistpcheck is T, LISTP checks are
inserted, e.g., X:(('A --) --) translates as:
(EQ (CAR (LISTP (CAR (LISTP X)))) (QUOTE A))
patlistpcheck is initially NIL. Its value can be changed within a
particular function by using a local declaration, as described on
page 23.30.
23.32
Pattern Elements
A pattern consists of a list of pattern elements. Each pattern element
is said to match either an element of a data structure or a segment.
(cf. the editor's pattern matcher, "--" matches any arbitrary segment of
a list, while & or a subpattern match only one element of a list.) Those
patterns which may match a segment of a list are called SEGMENT
patterns; those that match a single element are called ELEMENT patterns.
Element Patterns
There are several types of element patterns, best given by their syntax:
PATTERN MEANING
$1, or & matches an arbitrary element of a list
'expression matches only an element which is equal to the given
72
expression e.g., 'A, '(A B).
=form matches only an element which is equal to the value
of form, e.g., =X, =(REVERSE Y).
==form same as =, but uses an eq check instead of equal.
atom treatment depends on setting of patvardefault.
If patvardefault is ' or QUOTE, same as 'atom.
If patvardefault is = or EQUAL, same as =atom.
If patvardefault is == or EQ, same as ==atom.
If patvardefault is _ or SETQ, same as atom_&.
73
patvardefault is initially =.
Note: numbers and strings are always interpreted as though patvardefault
were =, regardless of its setting. Eq, memb, and assoc are used for
comparisons involving small integers.
(pattern1 ... patternn) n > 1
------------------------------------------------------------------------
72
eq, memb, and assoc are automatically used in the translation when
the quoted expression is atomic, otherwise equal, member, and
sassoc.
73
patvardefault can be changed within a particular function by using a
local declaration, as described on page 23.30.
23.33
matches a list which matches the given patterns,
e.g., (& &), (-- 'A).
element-pattern@function-object
matches an element if the element-pattern matches it,
and the function-object (name of a function or a
LAMBDA expression) applied to that element returns
non-NIL, e.g., &@NUMBERP matches a number,
('A --)@FOO matches a list whose first element is A,
74
and for which FOO applied to that list is non-NIL.
* matches any arbitrary element. If the entire match
succeeds, the element which matched the * will be
returned as the value of the match.
Note: normally, the pattern match compiler constructs an expression
whose value is guaranteed to be non-NIL if the match succeeds and NIL if
it fails. However, if a * appears in the pattern, the expression
generated will either return NIL if the match fails, or whatever matched
the * even though that may be NIL. For example, X:('A * --) translates
as (AND (EQ (CAR X) (QUOTE A)) (CADR X)).
~element-pattern matches an element if the element is not matched by
element-pattern, e.g., ~'A, ~=X, ~(-- 'A --).
Segment Patterns
$, or -- matches any segment of a list (including one of zero
length).
The difference between $ and -- is in the type of search they generate.
For example, X:($ 'A 'B $) translates as
(EQ (CADR (MEMB (QUOTE A) X)) (QUOTE B)), whereas X:(-- 'A 'B $)
translates as: [SOME X (FUNCTION (LAMBDA ($$2 $$1)
(AND (EQ $$2 (QUOTE A)) (EQ (CADR $$1) (QUOTE B]. Thus, a paraphrase of
($ 'A 'B $) would be "Is the element following the first A a B?",
whereas a paraphrase of (-- 'A 'B $) would be "Is there any A
immediately followed by a B?" Note that the pattern employing $ will
result in a more efficient search than that employing --. However,
($ 'A 'B $) will not match with (X Y Z A M N O A B C), but (-- 'A 'B $)
will.
------------------------------------------------------------------------
74
For "simple" tests, the function-object is applied before a match is
attempted with the pattern, e.g., ((-- 'A --)@LISTP --) translates
as (AND (LISTP (CAR X)) (MEMB (QUOTE A) (CAR X))), not the other
way around.
23.34
Essentially, once a pattern following a $ matches, the $ never resumes
searching, whereas -- produces a translation that will always continue
searching until there is no possibility of success. However, if the
pattern match compiler can deduce from the pattern that continuing a
search after a particular failure cannot possibly succeed, then the
translations for both -- and $ will be the same. For example, both
X:($ 'A $3 $) and (-- 'A $3 --) translate as (CDDDR (MEMB (QUOTE A) X)),
because if there are not three elements following the first A, there
certainly will not be three elements following subsequent A's, so there
is no reason to continue searching, even for --. Similarly,
($ 'A $ 'B $) and (-- 'A -- 'B --) are equivalent.
$2, $3, etc. matches a segment of the given length. Note that $1
is not a segment pattern.
!element-pattern matches any segment which the given element pattern
would match as a list. For example, if the value of
FOO is (A B C) !=FOO will match the segment
... A B C ... etc. Note that !* is permissible and
means Value-of-match_$, e.g., X:($ 'A !*) translates
to (CDR (MEMB (QUOTE A) X)).
Note: since ! appearing in front of the last pattern specifies a match
with some tail of the given expression, it also makes sense in this case
for a ! to appear in front of a pattern that can only match with an
atom, e.g., ($2 !'A) means match if cddr of the expression is the atom
A. Similarly, X:($ ! 'A) translates to (EQ (CDR (LAST X)) (QUOTE A)).
!atom treatment depends on setting of patvardefault. If
patvardefault is ' or QUOTE, same as !'atom (see
above discussion). If patvardefault is = or EQUAL,
same as !=atom. If patvardefault is == or EQ, same
as !==atom. If patvardefault is _ or SETQ, same as
atom_$.
75
. The atom "." is treated exactly like !. In
addition, if a pattern ends in an atom, the "." is
first changed to !, e.g., ($1 . A) and ($1 ! A) are
equivalent, even though the atom "." does not
explicitly appear in the pattern.
------------------------------------------------------------------------
75
With one exception, namely '.' preceding an assignment does not have
the special interpretation that ! has preceding an assignment
(see page 23.36). For example, X:('A . FOO_'B) translates as:
(AND (EQ (CAR X) (QUOTE A)) (EQ (CDR X) (QUOTE B))
(SETQ FOO (CDR X))), but X:('A ! FOO_'B) translates as:
(AND (EQ (CAR X) (QUOTE A))
(NULL (CDDR X))
(EQ (CADR X) (QUOTE B))
(SETQ FOO (CDR X))).
23.35
Segment-pattern@function-object
matches a segment if the segment-pattern matches it,
and the function object applied to the corresponding
segment (as a list) returns non-NIL, e.g.,
($@CDDR 'D $) matches (A B C D E) but not (A B D E),
since CDDR of (A B) is NIL.
Note: an @ pattern applied to a segment will require computing the
corresponding structure (with ldiff) each time the predicate is applied
(except when the segment in question is a tail of the list being
matched).
Assignments
Any pattern element may be preceded by a variable and a '_', meaning if
the match succeeds (i.e., everything matches), the variable given is to
be set to what matches that pattern element. For example, if
X = (A B C D E), X:($2 Y_$3) will set Y to (C D E). Assignments are not
performed until the entire match has succeeded. Thus, assignments
cannot be used to specify a search for an element found earlier in the
76 77
match, e.g., X:(Y_$1 =Y --) will not match with (A A B C ...). This
type of match is achieved by using place-markers, described below.
If the variable is preceded by a !, the assignment is to the tail of the
list as of that point in the pattern, i.e., that portion of the list
matched by the remainder of the pattern. For example, if X is
(A B C D E), X:($ !Y_'C 'D $) sets Y to (C D E), i.e., cddr of X. In
other words, when ! precedes an assignment, it acts as a modifier to the
_, and has no effect whatsoever on the pattern itself, e.g., X:('A 'B)
and X:('A !FOO_'B) match identically, and in the latter case, FOO will
be set to CDR of X.
Note: *_pattern-element and !*_pattern-element are acceptable, e.g.,
X:($ 'A *_('B --) --) translates as:
[PROG ($$2) (RETURN
(AND (EQ (CAADR (SETQ $$2 (MEMB (QUOTE A) X)))
(QUOTE B))
(CADR $$2]
------------------------------------------------------------------------
76
The translation of this pattern is:
(COND ((AND (CDR X) (EQUAL (CADR X) Y))
(SETQ Y (CAR X))
T)).
The AND is because if Y is NIL, the pattern should match with
(A NIL), but not with just (A). The T is because (CAR X) might be
NIL.
77
unless, of course, the value of Y was A before the match started.
23.36
Place-markers
Variables of the form #n, n a number, are called place-markers, and are
interpreted specially by the pattern match compiler. Place-markers are
used in a pattern to mark or refer to a particular pattern element.
Functionally, they are used like ordinary variables, i.e., they can be
assigned values, or used freely in forms appearing in the pattern, e.g.,
X:(#1_$1 =(ADD1 #1)) will match the list (2 3). However, they are not
really variables in the sense that they are not bound, nor can a
function called from within the pattern expect to be able to obtain
their values. For convenience, regardless of the setting of
patvardefault, the first appearance of a defaulted place-marker is
interpreted as though patvardefault were _. Thus the above pattern could
have been written as X:(#1 =(ADD1 #1)). Subsequent appearances of a
place-marker are interpreted as though patvardefault were =. For
example, X:(#1 #1 --) is equivalent to X:(#1_$1 =#1 --), and translates
78
as (AND (CDR X) (EQUAL (CAR X) (CADR X)).
Replacements
Any pattern element may be followed by a "_" and a form, meaning if the
match succeeds, the part of the data that matched is to be replaced
79
(e.g., with RPLACA or RPLACD) with the value of