Queinnec - LISP
Queinnec - LISP
Consulting Editor
Professor F. H. Summer, University of Manchester
*The titles marked with an asterisk were prepared during the Consulting Editorship of
Professor J. S. Rohl, University of Western Australia.
LISP
Christian Queinnec
M
MACMILLAN
© Editions EYROLLES, Paris 1983
Queinnec, C.
LISP.
I. LISP (Computer program language)
I. Title
001.64'24 QA76.73'L23
((Preface vii)
(1 (LISP Data 3)
(( 1.1 Atoms and Lists 3»)
((1.2 Primitive Handling Functions 4)
(1.2.1 Definition of First 4)
(1 .2.2 Definition of Rest 4)
(1.2.3 Definition of Insert 5)
(1.2.4 Definition of Atom 5)
(1.2.5 Definition of Null 6)
(1.2.6 Definition of fj'q 6)
(( 1.3 The LISP Language 7)
(1.3.1 Program and data identification 7)
(1.3.2 Logic values 8)
(1.3.3 The empty list 9»
(( 1.4 Historical Background 10))
(( 1.5 Exercises 11))
(3 (Micro-manual 21)
((3.1 Basic Functions 22))
((3.2 Exercises 32»
(3.3 Dealing with Numbers in LISP 33»
((3.4 Exercises 40»
((3.5 Input/Output 41»
((3.6 Exercises 45»
((3.7 Functions 45)
(3.7.1 Different types of function 45)
(3.7.2 Environment 46)
(3.7.3 Evaluation of an EXPR form 47)
(3.7.4 Conclusions 66»
((3.8 Exercises 66»»
v
vi CONTENTS
LISP was conceived around 1960. At that time there were few high level
languages in existence and even these were in their infancy. FORTRAN II was
beginning to spread, ALGOL 60 was in the process of being defined and BASIC
did not exist. Since that time, considerable improvements have transformed
these languages, whereas LISP has remained virtually unchanged, a significant
compliment to its creator John McCarthy. Historically, LISP has been linked
with the development of Artificial Intelligence for which it has been the major
programming language. The greatly increased interest in Artificial Intelligence
in recent years has seen LISP established among the major languages. The last
decade has witnessed the appearance of an ever-growing quantity of both
theoretical and practical work concerning LISP.
LISP exists on almost all computers from the largest mainframe to the
simplest micro, and there are even machines that have been specially designed
for the language. Twenty years after its conception, LISP remains one of the
better programming languages in existence.
This book intends answering three questions:
What is LISP?
How do we program in LISP?
What can we do in LISP?
It was not my intention to make the book simply a user manual (having to be
linked to some sort of interpreter and therefore having to devote itself to deal-
ing with the particular features of one system), or just a collection of
commented examples (which risks focusing the reader's interest on the
algorithms themselves rather than on the language in which they are written),
or even merely a discussion of the language (describing its syntax and the
semantics of its basic primitives but never touching the area of programming
in LISP, its general techniques). If I seem to have ignored some areas of the
subject, I freely admit that some concessions have been made to achieve the
objectives set out above. A basic LISP nucleus is presented, which forms a
solid foundation on which to build the rest of the LISP structure: one
advantage of LISP is that it can, to a large extent, be defined in itself, and
hence can possess such a simple 'core'. On this basis, four distinct program-
mingstyles are shown one after another, each explaining how LISP may be
used efficiently. At the end of the book, programs describing a small
vii
viii CONTENTS
software robot are given, which will familiarise the reader with the rudiments
of what has become known as Artificial Intelligence.
I hope that this book will intrigue you, arouse your interest and finally win
you over to LISP.
CHRISTIAN QUEINNEC
PART ONE
FIRST STEPS IN LISP
1 LISP Data
3
4 LISP
Several functions are necessary in order to be able to handle these objects (lists
or atoms): First, Rest, Insert, Atom, Null and Eq. These functions facilitate the
building and decomposition of lists and can also be used to ascertain the nature
of the handled objects.
In order to correctly define the scope and effects of these functions, we will
use the following conventions.
As any LISP object (for historical reasons, we also use the term a Sexpression,
short for 'Symbolic Expression', or to be more brief, an expression) is either an
atom or a list, we can write the general statement
SEXP = LIST U ATOM
where
LIST is the collection of lists
ATOM is the collection of atoms
and where SEXP will therefore be the collection of LISP expressions.
Still speaking in general terms, LIST - {()} will be the collection of lists
apart from the empty list, in other words the collection of non-empty lists.
We will meet similar approaches later on when we need to define adequate data
structures for certain specific problems.
LISP DATA 7
1.3 The LISP Language
The LISP language is based upon three conventions, each of which will be
discussed with comments.
In these examples we see that the atom + represents normal addition, and the
atom SIN represents the sine function in trigonometry. The convention there-
fore requires that a name be given to the functions defined previously, in order
that they may be used. It is quite suitable to call
First by the atom FIRST
Rest by the atom REST
etc.
We can write the following programs (the meaning of the 'quote' will be given
in section 1.3.2)
8 LISP
tStrictly speaking, eq is a function, and not EQ, which is an atom. But as the user does
not usually modify the natural link between EQ and eq, we will refer to the function
EQ (the function ATOM, etc.) and will abandon the notations eq (atom, etc.).
LISP DATA 9
is the list
(ATOM T)
which is a program whose value is the atom T .
• By virtue of convention 1, programs arc lists; lists disallow the presence of
this 'quote', although it is nevertheless a very usual and practical facility. We
should point out that
'expression
is a typographical variant of
(QUOTE expression)
Expression represents any LISP object. Hence, the program (QUOTE(C6HSOH))
returns the list (C6HSOH) as its value and may also be written as '(C6HSOH).
The value of the program (QUOTE(ATOM 'A)) is (ATOM 'A) or
(ATOM(QUOTE A)) according to whether you wish to write everything or
condense the format of this resulting list. The value of the program (QUOTE
QUOTE) is the atom QUOTE, which in no circumstances should be written as
a single 'quote', since our convention stipulates that the 'quote' always precedes
an expression and is not used on its own.
This typographical convention is necessary because of the very frequent use
of this construction.
As far as logic values are concerned, any LISP object apart from
NIL, is considered as representing True
Thus T represents True (tt), and C6H50H also represents True (tt), as does
(ACID BASE ALDEHYDE) or 'T or even (NIL).
As we will be able to establish later on, this convention, which is more lax
than convention 2, simplifies program writing to a large extent. Note, neverthe-
less, that this convention does not imply that the predicates encountered so far
(NULL, EQ, ATOM) may have any object as their value (NIL or not-NIL): they
only return the atom NIL or T as their value, T being a particular representative
of the class of objects signifying True.
For reasons that are once again historical, the functions FIRST, REST and
INSERT, which have already been mentioned, are better known by the less
memorable names of CAR, CDR and CONS respectively.
CONS means CONStructing lists whilst CAR and CDR originate from
'Content of Address Register' and 'Content of Decrement Register' and are
operations that are totally related to the first computer on which LISP was
created.
Of course, it is always possible to define the functions FIRST, REST and
INSERT, if they do not already exist, on any LISP interpreter that is in use
today; however, as the names CAR, CDR and CONS are used world-wide, we
will abandon the mnemonics FIRST, REST and INSERT. Therefore
(CAR '(ONE TWO THREE)) will yield the value ONE
(CDR '(FOUR FIVE)) will yield the value (FIVE)
(CONS 'ONE '(1)) will yield the value (ONE 1)
• The names CAR and CDR, which differ only by a single letter, have led to a
very useful convention. When you program in LISP, it is quite often necessary
to write long sequences of CAR and CDR. If we recall the example where we
wished to obtain the first term of the third term of a list, the sequence was
(CAR(CAR(CDR(CDR '((WE) (LOVE) (LISP))))))
This can be made more concise by writing
(CAADDR '«WE) (LOVE) (LISP)))
The atom CAADDR is therefore the name of the function
car 0 car 0 cdr 0 cdr
LISP DATA 11
Hence
(CADR '((WE) (LOVE) (LISP))) will yield the value (LOVE)
(CDDDR '((WE) (LOVE) (LISP))) will yield the value NIL
Consequently ~ a combination of no CARs and no CDRs is denoted by CR:
(CR '((WE) (LOVE) (LISP))) will yield the value ((WE) (LOVE) (LISP))
CR is the identity function which it is important not to confuse with
QUOTE. Simply compare
(CR(CONS 'T 'NIL)) which gives (T)
and
(QUOTE(CONS 'T 'NIL)) which gives (CONS 'T 'NIL)
1.5 Exercises
1.5.1 Give the type (atom, list, program, badly structured expression) of the
following expressions:
ATOM
(LIS)P
(L(I(S(P)))
(CAR(l I S P))
(CONS 'CAR "T)
(CAR '(NIL»
Whatever the type of computer being used (micro, mini or even mega) and
whatever the way it requires to start the LISP interpreter, once loaded and
about to execute, the interpreter will indicate its readiness in terms such as
LISP 1.5 IS WINNING AGAIN
?
The first line contains identification of the LISP being used and provides you
with a customary welcome message. The question mark on the second line
indicates that the interpreter is at your disposal from now on. These terms and
the way of showing that LISP is waiting for a program from you are not
universal to all existing interpreters; for example, some show their readiness
to receive a program by printing EVAL: instead of a question mark. However,
whatever method the computer uses to indicate its readiness, we can put it to
the test, without further delay, by using the few programs that you already
know how to write.
We shall use the following typographical convention: expressions will be
input on lines commencing with a question mark, whereas the replies will be
output, after a jump to the next line, starting in the fourth column (that is,
preceded by three spaces). Note that there is no set format for replies either;
for example, certain interpreters may print VALUE: before the reply.
Let us start then:
,?(CAR '(A ROSE»
A
The reply is received immediately. The calculation is actually very simple since
the atom A is the first term of the list (A ROSE). The question mark that then
appears indicates that LISP is again ready to accept a new form.
,?(CAR (CAR I «ONE) NIL»)
ONE
( CAAR (QUOTE (
( ONE
) ( )
) )
? )
ONE
ONE
ONE
12
FIRST STEPS IN LISP 13
These last examples show that expressions input to LISP may be written in
free format on one or several lines, and that any superfluous spaces have no
effect on the interpreter. If there are not enough closing brackets, LISP will
require these to be supplied on as many new lines as needed, each one com-
mencing with a question mark.
Let us now continue by considering how to handle numbers:
? (+ 1 2)
3
1(+ 1 2 3 4 5)
15
?(* 2 3.14159)
6.28318
? (SET I P 3)
3
?P
3
1(+ P P)
6
? (* P (+ P 1»
12
14 LISP
? ( SET (QUOTE P) (* 2 P»
6
I'
6
p
(6 )
P
(CONS P NIL)
P
P
In a way, SET allows an atom to have a value. When you use LISP you will
find that a certain number of atoms have already been defined, for example:
T
T
NI L
NI L
The atoms T and NIL have themselves as value. All the atoms representing
the basic functions of the interpreter (CONS, CAR, CDR, NULL, EQ, ATOM,
QUOTE, SET, as well as others) are also pre-defined. As far as these atoms are
concerned, it is as if LISP itself evaluated the forms (SET 'T 'T) and
(SET 'NIL 'NIL) before 'giving you control', in other words, before allowing
you to enter expressions.
In the preceding examples, you will have noticed that the value of the form
SET (the program whose first term is the atom SET) is that of its second
argument. Unlike in BASIC, where only the value to be assigned is the result of
a calculation, in LISP both the name of the variable and the value to be assigned
to this variable may be calculated. This last point therefore justifies using the
'quote' (that is, the function QUOTE) in the assignments given as examples:
FIRST STEPS IN LISP 15
?['
a
?O
3
Since the value of P is the atom Q, the second assignment corresponds to the
form (SET 'Q 3) which assigns the value 3 to the atom Q and changes neither
the atom P nor its value!
This concept of value is at the heart of LISP. When you enter a program
(a form or an atom), the LISP interpreter calculates its value which is then
printed on a new line: we say that it evaluates the expression. LISP evaluates
an atom by looking for the object that it stores. It evaluates a form by firstly
evaluating all the terms in the form and then applying the function (value of
the first term) to its arguments (values of terms one, two, three, etc.). But note
that this is true only in general terms, since we have already met an exception:
the function QUOTE which does not evaluate its argument but returns it as it is.
Later on we will see other functions of this same type.
The following, more detailed, example will clarify this fundamental concept
of evaluation. We will write a function which we will call FACT and which may
be used to calculate factorials. In mathematical terms: factorial (n) is written as
n! and is defined as
n! = 1 x 2 x 3 x .. . X (n - 1) x n
We may translate this 'word for word' into English by the following.
Definition (DE) of a function (called FACT), which has a single variable
(called N), and which is defined as follows:
If N is equal to I (EQN N 1)
then the value of the function will be the constant 1
else it is N times the value of factorial N-l
Such a function is said to be recursive as it appears in its own definition!
We shall prove straight away that this type of situation does not cause any
problems for our in terpreter:
?(FACT S)
120
?(FACT (+ 6 1»
6221020800
In order to explain the way in which LISP arrived at these results, let us
evaluate the following form by hand, just as the interpreter would do it:
?(FACT 3)
In this example, the symbol t will indicate the current position of the
evaluation:
(FACT 3)
t
Having received a request to evaluate a program, LISP makes enquiries as to t
nature of this program: atom or form? It is a list (hence a form), so LISP will
evaluate the first term of this list in order to determine the function to be
applied.
(FACT 3)
t
FACT is a known atom whose value is a function. LISP will therefore evaluat
the argument of this function.
(FACT 3)
t
FIRST STEPS IN LISP 17
The argument is a number. A number is an atom that represents its own value,
in this case, the integer number 3.
(FACT 3)
t
There are no more arguments. We may therefore apply the function FACT
to its argument: the number 3. This means calculating the following form for
which the value ofN will be 3 (we say: in the environment where N has the
value 3).
(IF(EQN N 1) 1 (* N (F ACr( _.- N 1))))
t
We continue with the evaluation by establishing the fact that we have a form
whose first term will be evaluated.
(IF(EQNN 1) 1 (* N(FACT(-N 1))))
t
The first term is the IF operator which necessitates the evaluation of its first
argument and will, according to the returned logical value, force the evaluation
of the second or third argument. It is thus necessary to evaluate the first
argument.
(IF(EQN N 1) 1 (* N (FACT(- N 1))))
t
It is a form whose first term
2.1 Exercises
2.1.1 Calculate
(FACT (FACT 2»
(FACT -1)
Calculate
(FACT 3)
20 LISP
21
22 LISP
the part corresponding to if will be the form (LE N 1) the then part will be the
constant 1, whilst the else ... part will correspond to the single form
(* N (FACT (- N 1))).
In order to make this chapter comprehensive, we will briefly revise the
functions that have already been described.
• (CAR expression)
Argument: The value of expression must be a non-empty list.
Value: The first term of the non-empty list that is the value of expression.
Example
• (CDR expression)
Argument: The value of-expression must be a non-empty list.
Value: The entire list (that is, the value of expression) with its first term
omitted.
Examples
Notes
When the value of list is an object that is not a list (empty or not), the interpre-
ter will not display an error, but will form an unusual object called a dotted
pair. Dotted pairs will be analysed and discussed in greater detail in chapter 8.
?(CONS 'DOTTED 'PAIR)
(DOTTED. PAIR)
Dotted pairs are survivors of an earlier stage of the language, derived from
the dot that separates their two components: the CAR part and the CDR part.
• (ATOM expression)
Argument: expression may have any value.
Value : ATOM is a predicate returning a value of T or NIL depending on
whether the value of expression is an atom or not.
Example
?(ATOM I ATOM)
T
?(ATOM NIL)
T
?(ATOM (+ 1 2»
T
Note
Atoms were introduced in chapter 1 as indivisible objects represented by a
sequence of contiguous characters. Atoms whose name consists purely of
24 LISP
figures are numbers that may be used freely by arithmetic operators. By con-
vention, any object that is not a non-empty list, in other words, that has not
been built by CONS, is an atom. This convention allows the following program:
?(ATOM ATOM)
T
?(ATOM CAR)
T
the question arises whether the value of the identifier CAR is an atom or not; in
other words, whether the object that we call car is an atom or not, assuming that
the user has not altered CAR. Conventionally the reply is yes.
We may therefore write
MICRO-MANUAL 25
?(ATOM LOCARITHM)
T
?(EQ (+ 1 2) (- 4 1»
T
Note
In many dialects, equality between two numbers is tested by a special predicate
(EQN) and not by EQ as is the case here. The value of EQN is True (T) if, and
only if, its two arguments are equal numbers, otherwise its value is NIL.
If, with your interpreter, EQ may not be applied to numbers, you can
redefine EQ as
? (EQ 1 (+ 1 0»
T
The former function EQ is now the value of identifier OLD-EQ which means
that EQ may be restored to its initial value if so desired. Without taking this
precaution, the function eq would have been lost as it would be inaccessible:
26 LISP
• (NULL expression)
Argument: expression may have any value.
Value: NULL is a predicate that returns the value T or NIL according to
whether the value of expression is the empty list (the atom NIL)
or not.
Example
? (NULL T)
NIL
? (NU L L (C DR I (L 1ST) ) )
T
Note
NULL is not one of LISP's primitive functions and may therefore be defined as:
With regard to logic values (see section 1.3.2), NULL behaves like the
negation function called NOT, which is defined as
Of course, we get
? (NOT T>
NIL
? (NOT NI L)
T
• (CONSP expression)
Argument: expression may have any value.
Value: CONSP is a predicate that returns the value T or NIL according to
whether the value of expression is a non -empty list or not.
MICRO-MANUAL 27
Example
? ( CONSP I (L I ST) )
T
?(CONSP ATOM)
NI L
?<CONSP 314)
NI L
?(CONSP 'ATOM)
NIL
Note
CONSP is the predicate that complements ATOM and which we define as
• (lOP expression)
Argument: expression may have any value.
Value: lOP is a predicate returning T or NIL depending on whether the
value of expression is an identifier or not.
Example
?(IOP 'ABCOEF)
T
?(IDP (+ 2 3))
NIL
Note
lOP may be considered as a sub-predicate of ATOM since any expression that
satisfies IDP will also satisfy ATOM. IDP is sometimes known as SYMBOLP,
the identifiers being referred to as 'atomic symbols'.
Examples
?(IF T 1 2)
1
? ( I F ( NULL I ( LIST» 1
2 3 4 5 )
5
? ( I F (NOT T) T)
NI L
Note
IF is the basic conditional expression which, along with the predicates, allows
us to control the evaluation so as to apply the correct operators to the correct
arguments. For example
• (PROGN forms )
Argument: forms is any sequence of forms.
Value: PROGN evaluates its arguments in order. The value of the last
argument is the value ofPROGN. By convention, the value of
(PROGN) is NIL.
Example
? (PROCN 1 2 3)
3
?(PROGN 1 2)
2
MICRO-MANUAL 29
?(PROGN 1)
1
?( I r (NOT T) 1)
NI L
Note
PROGN is only beneficial to functions that cause side-effects (for example,
SET, DE, PRINT, READ, etc.). Generally, functions are characterised by their
value, but occasionally they produce lasting effects which affect the sequence
of evaluations: they are therefore considered to produce side-effects. For example
?N
3
?P
Although the value of (SET 'P 1) is 1 (this value being added to 2 to become
the value of the identifier N), the basic and essential side-effect is the association
of the value 1 with the atom P.
There is no point in writing the form
?(PROGN (+ 1 2 3 4)
(CDR LIST) )
f (
(NOT NI L) )
T
?(PROGN (SET IN (+ N 1»
I INCREMENT )
INCREMENT
• (QUOTE expression)
Argument: expression is some object.
Value: The value of the form (QUOTE expression) is expression. This
function is by no means trivial and must not be confused with
the identity function.
Example
?(QUOTE QUOTE)
QUOTE
?(QUOTE IATOH)
(QUOTE ATOM)
?(QUOTE (* 1 2 3»
(* 1 2 3)
Note
The Identity function is defined as
?(IDENTITY I ATOM)
ATOM
?(IDENTITY (* 1 2 3»
6
?(£Q TIT)
T
This is the same for numbers that also have themselves as value:
MICRO-MANUAL 31
1<£Q 3 (QUOTE 3»
T
• (EV AL expression)
Argument: The value of expression may be any object.
Value: The value of this form will be the value of the value of expression.
If the value of expression is a non-empty list, then the returned
value will be the value of this list, which is considered to be a form.
If the value of expression is an atom, then either it is an identifier,
in which case the result will be the value of this identifier, or it is a
number or a function and, by convention, the returned value will
be this number or this function.
Examples
?(EVAL T)
T
? (EVAL (EVAL N»
3
Note
The main purpose of EVAL is to tum a list into a form and hence calculate its
value. Since we know how to build lists, and therefore programs, by using
CONS, we can now use EVAL to evaluate (that is, execute) them. Thus
? EMPTY
? LIST )
? NIL »
? NIL » )
NOT
32 LISP
,?(DE TOPLEVI:L ()
(PRINT (EVAL (READ»)
(TOPLEVEL) )
TOPLEVEL
which indicates that the interpreter reads an expression, evaluates it, prints its
resuIt an d starts again.
If only we could write EVAL in LISP we would have a definition of LISP in
LISP. This is possible, and such a function will be shown in the Appendix.
The function EVAL is one of the most noticeable characteristics of LISP,
which distinguishes it from other languages. You will see many uses of it in
section 3.7 when we deal with special functions.
3.2 Exercises
3.2.1 Evaluate
3.2.3 Write a function that returns the type of its argument: that is, LIST if it
is a list, ATOM if it is an atom.
3.2.4 Write a test function. The value of the atom PROGS is a list of forms to
test. The call (TEST) evaluates the first program in this list and removes it.
MICRO-MANUAL 33
3.3 Dealing with Numbers in LISP
?<FACT 20)
2432902008176640000
As we saw above, num bers are special atoms that have themselves as value:
?-121
-121
? ( s I::T 12 13 )
*****FIRST-ARGUMENT-IS-NOT-A-SYMBOL:12
since, of course, each number has its own distinct value. The conventions used to
write arithmetic expressions are the usual LISP conventions. Let us refresh our
memory: any operation is represented by a list whose first term is the name of
the function to be applied (the operator) and whose other terms are the operands
of the function. Thus
3+4 will be written as (+ 3 4)
3 + (4 x 2) will be written as (+ 3 (* 4 2))
(3 + 4) x 2 will be written as (* (+ 3 4) 2)
1 (n)n will be written as ( / (EXP (/ N E) N)
v(21Tn) e/ (SQRT(* 2
PI
N)) )
having suitably initialised E and PI.
This notation is clumsy and is not as elegant as the mathematical representa-
tion. Nevertheless, it is simple to write a function in LISP itself, which translates
arithmetic expressions from BASIC notation to LISP notation, as well as the
other way round. We could, for example, write
34 LISP
• (NUMBERP expression)
Argument: expression may have any value.
Value: NUMBERP is a predicate that returns the value T or NIL,
according to whether the value of expression is a number or not.
Example
?(NUMBERP 3)
T
?(NUMBERP (+ 3 4»
T
Note
NUMBERP allows numbers to be recognised as such, which means that in the
last example, FACT is applied only when it makes sense. Generally, it is in your
interest to protect your functions by making them test the nature of their
arguments before any calculation. You may also rely on the interpreter's
abilities (which vary a great deal depending on the one you use) to perform
tests and to allow these types of error to be corrected, for example
MICRO-MANUAL 35
r c t (+ 1 'A) 2)
*****ERRONEOUS-FORM:(+ 1 (QUOTE A»
***VALUE-OF-FORM?
~(PROGN (PRINT (LIST 'A ': A»
(+ 1 A) )
(A a: 7)
4
• (PLUS numbers )
Argument: numbers is a sequence of expressions whose values must be
numerical.
Value: The sum of the values of the expressions present in numbers . . .
By convention the value of (PLUS) is zero.
Example
Notes
The function PLUS is often abbreviated to the symbol '+', in other words, at
the initialisation stage, LISP evaluated (SET '+ PLUS) which means we have
the same function (addition) under two different names.
Note also that PLUS takes any number of arguments and evaluates them all.
The following function will be found in any self-respecting LISP interpreter:
Notes
The function DIFFERENCE is also abbreviated to '-'. As with ADDl, the
function SUB 1 also exists and is defmed as
36 LISP
?(HINUS -1981)
1981
• (TIMES numbers )
Arguments: numbers is a sequence of expressions whose values must be
numerical.
Value: The number produced by multiplying the values of the expressions
present in numbers . . . By convention, the value of (TIMES) is 1.
Example
? (T I HE S (S ET • N - 1) ( M I NU S N»
-1
Notes
The function TIMES is also abbreviated to '*'. As with the function PLUS,
TIMES deals with any number of evaluated arguments.
Example
Note
QUOTIENT is often abbreviated to 'I'.
Notes
REMAINDER is often abbreviated to 'MOD'.
If we wish to obtain the quotient and remainder by using a single function,
we define for example
1(DE DIVIDE (N P)
(CONS (I N P)
(CONS (MOD N P) NIL) ) )
DIVIDE
?(GREATERP 1 (MINUS 1»
T
,?<GREATI:RP 0 0)
NI L
Notes
GREATERP is often abbreviated to 'GT' (as in FORTRAN).
All other predicates may be defined by using EQN and GREATERP as
their starting point. If you do not have EQN, the definition will be as follows:
38 LISP
?(DE EON (N P)
(AND (NUHBERP N)
(NUHBERP P)
(EQ N P) ) )
EON
You will note that most of the predicates in LISP end with the letter 'P'
(for predicate) except, unfortunately, for the ones used most often: ATOM,
NULL, EQ, etc. (this is due once again to historical influences). Other
predicates used for comparisons may supplement the collection of arithmetic
predicates.
1(DE GE (N P)
<NOT (L T N P» )
GE
? (DE LE (N P)
(NOT (GT N P» )
LE
?(DE LT (N P)
(GT P N) )
LT
• Examples of functions
It may seem as though some functions are missing (for example the
exponentiation or the square root); let us prove that from now on it is
possible to write them .
The exponentiation uses the following function which returns the square
of its argument.
and
"td P ~ 0 n 2 p +1 =n(n 2P )
the termination case is nO = 1.
Notice that we test (LE P 0) and not (EQN PO), so that we protect ourselves
from a negative power which would lead to an infinite calculation (see exercise
2.1.1).
As it contains fewer multiplications, this method is a lot quicker than the
following one which involves multiplying N by itself P times:
? <DE EXP (N P)
(IF (LE P 0) 1
(* N (EXP N (SUB1 P») ) )
EXP
Examples
Suppose that we wish to evaluate (EXP 7 (ADDI 4)) and that we have opted
to 'trace' (that is, to write down) all the calls to the functions EXP and
SQUARE (see chapter 9). This gives us
?(EXP ? (ADDl 4»
(eXp 7 S)
(EXf ? 4)
(SQUARE 1)
49
40 LISP
(EXP 49 2)
(SQUARE 49)
2401
<EXP 2401 1)
(I:XP 2401 0)
2401
2401
2401
16307
16CO?
• (SQRT number)
We suggest the following definition:
?(SORT 51)
7
We leave it to the reader to invent quicker methods than the one suggested
above, whose equivalent in ~ASIC is:
100 REM SUB-PROGRAM SQRT
101 REM ARGUMENT IN N
102 REM RESULT IN S
110 LET S == 1
120 IF S * S ~ N THEN GOTO 150
130 LET S == S + 1
140 GOTO 120
150 LET S == S - 1
160 RETURN
The function SQRT1 corresponds to lines 120 to 160.
3.4 Exercises
In other words, the number of distinct groups of p objects that you can
ex tract from n. Use
or
with
(~) = 1
(:) = 1
3.5 Input/Output
There are a few, simple functions that enable the transfer of information from
the user to the LISP interpreter and vice versa. We will give only the two main
ones here, but in chapter 9 we will add some special functions which allow
more detailed control over printing.
The aim of the input/output functions is to convert LISP objects into
character sequences and vice versa. The main functions are called PRINT
and READ.
42 LISP
• (PRINT expression)
Argument: The value of expression must be a list or a number or an identifier.
Value: The value of this form is the value of expression, the main purpose
of this function being to print the value of expression (or to display
it on a screen). Once this object has been printed (or displayed) the
interpreter automatically goes to a new line.
Examples
?{PROGN (PRINT (CONS 'TESTING '(PRINT)))
? T)
(TESTING PRINT)
T
?(PRINT (PRINT 'PRINT-NEW-LINE))
PRINT-NEW-LINE
PRINT-NEW-LINE
PRINT -NEW-LINE
Noles
Notice that the atom PRINT-NEW-LINE is printed three times. There are, in
effect, two calls to PRINT, therefore the atom concerned is printed twice at
the beginning of the line (with an automatic new line after each print). Finally,
the value returned by the initial form is this same atom, which is therefore
printed once again (but this time with three spaces preceding it, as is the case
with all values of expressions input to the interpreter).
If you insert printing instructions in function definitions, then PRINT
allows you to trace functions. For example, let us redefine factorial:
?<FACT 6)
(FACT 6)
(FACT 5)
(FACT 4)
(FACT 3)
(FACT 2)
(FACT 1)
(FACT 1 = 1)
(FACT 2 a 2)
<FACT 3 - 6)
MICRO-MANUAL 43
(FACT 4 ~ 24)
(FACT 5 ,. 120)
(FACT 6 ?20)
?20
• (READ)
Argument: None
Value: READ requests a LISP expression which it reads and returns as value.
Example
?EXI'
(THAT LIST WILL BE READ)
Notes
The expression that is read is not evaluated and therefore has no need to be
QUOTED.
We are able to insert comments in LISP. These comments are ignored by the
reading process. A comment starts with the symbol ';', and finishes at the end
of the line (this is by far the commonest convention).
Example
The calculation has continued without any error and has given the correct
result. This example shows us how we can use PRINT and READ in a simple
way, to produce a small evaluator that allows environmental errors to be
corrected.
PRINT produces a straightforward print. For example:
?(PRINT I (PROGN (DE FACT (N)
(ZEROP N) (IF
1
(* (FACT (SUBl N» N»)
(DE TOPLEVEL ()
(PRINT (EVAL (READ»)
(TOPLEVEL) ) »
(PROGN (DE FACT (N) (IF (ZEROP N) 1 (* (FACT (SUB1 N»
N») (DE TOPLEVEL NIL (PRI"NT (EVAL (READ») (TOPLEVEL)
) )
This form of printing is not easy to read and totally justifies the following
acronym of LISP: 'Lot of Insipid and Stupid Parentheses'. All the LISP
expressions in this book are laid out according to a certain number of rules
for alignment and indentation (for example, arguments belonging to the same
form are often written in columns, closing brackets that correspond to open-
ing brackets on a same line are printed with no intermediate space). We can
write a function in LISP that provides us with this improved printing. This
function is commonly known as PRETTY-PRINT.
When reading this chapter, functions will appear to be 'unnamable' objects
since they cannot be printed. What, in fact, can we associate with a function?
Perhaps its name (although it may not even have one), or its definition (which
always exists); but in the latter case we cannot distinguish the function from
the list (that is, the form) defining it. We have therefore to admit that functions
MICRO-MANUAL 45
are objects that can be created in LISP, but for which there is no simple
equivalent as far as character sequences are concerned. Hence
?( (PRINT CAR) '(PRINT RETURNS THE FUNCTION
? NEVERTHELESS))
***UNPRINTABLE-OBJECT
PRINT
An error is reported but the value of CAR is correctly applied to the inserted
form; that is, in all cases, whether the argument is printable or not, the value of
(PRINT expression) is the value of expression.
3.6 Exercises
( P:l I NT (+ 2 3»
( r R r NT I (+ 2 3»
3.6.2 Write a function that prints all the terms of its single argument one after
another. Its value will always be NIL. For example
~(LPnrNT (A AB ABC»
A
AB
ABC
NIL
3.7 Functions
This section will conclude this micro-manual. Functions are by far the most
useful objects in LISP. It is by using functions that a user may build his
program. Functions are identified by a definition made up of a list of variables
and a 'body' that shows how to calculate the value of this function. Generally,
this body uses variables, present in its list of variables, whose values are the
arguments used when calling the function.
etc. These functions are concerned with the value of their arguments, of
which there is a fixed number. We say that these functions are of the type
SUBR (for 'subroutine'). They are generally primitive functions which cannot
be defined in LISP.
A second class comprises the functions ATOM, NULL, INSERT, FIRST,
REST, LE, LT, GT, ADDl, SUBl, FACT, TOPLEVEL, UNKNOWN,
SPRING-WATER, etc. As with the SUBR, these functions are concerned with
the value of their arguments of which there is a fixed number. Their only
difference is that they are defined in LISP itself (usually by using DE). These
functions are said to be of the type EXPR (for 'Expression').
We have been briefly introduced to two other function classes. The third
class is made up of the functions TIMES and PLUS (as well as + and *) which
are concerned with the value of their arguments of which there may be any
number. If they were defined in LISP, they would be said to be of type NEXPR.
As they are primitive in our case, we will say they are of type NSUBR (we have
not as yet given an example of a function of type NEXPR).
The fourth class comprises the functions QUOTE, PROGN, IF and DE,
which are not interested in the value of their arguments but in their non-
evaluated representation. Although QUOTE has only a single argument, whereas
PROGN, IF and DE require at least 0, 2 and 3 respectively, these functions are
all said to be of type FSUBR. We will see that in addition to FSUBR we have
FEXPR, as well as a fifth class of functions that have not been used up until
now, but which are very useful: the macro-functions (of type MEXPR).
All these function types are defined by the functions LAMBDA, NLAMBDA,
FLAMBDA and MLAMBDA (these themselves being of type FSUBR).
To summarise, the LISP function is not only defined by a list of variables
and a body, but also by its type which indicates the way in which a function is
calculated; in other words, it answers the questions:
Should the arguments be evaluated or not (EVAL/NOEVAL)?
Should we gather them up with a single list (however many of them
there are), or distribute them to the different variables (SPREAD/
NOSPREAD)?
Finally, what do we do with the value returned after evaluating the
function body (MACRO/NOMACRO)?
3.7.2 Environment
The environment, kept by the interpreter, is the data structure which is used to
associate each variable with its value. The environment is usually drawn as a
sequence of boxes linked together by arrows. Each box contains a group of
rows, each of which is made up of an identifier on the left and its associated
value on tne right. For example, the interpreter's natural environment is as
follows:
MICRO-MANUAL 47
T T
NIL NIL
CAR car
~__
NULL ---,-~_-':-_-_--l_.:; function (LAMBDA (EXP) (EO EXP NIL)I
~l- N 4
global
Step 1: The first term of the form is evaluated. This must be a function, in which
case its type may be deduced. It is an EXPR, so on to Step 2.
Step 2: Successive evaluation of the arguments. In our case, the values of the
arguments are 4 and 1 respectively. When all the arguments are evaluated, go on
to Step 3.
Step 3: Linking of variables and arguments. Remember that LE is defined as:
(DE LE (P N)
(NOT (GT P N)) )
A new environment is created, and is shown below:
P 4
N 1
N 4
global
The value of an identifier in any environment is the one that is stored in the
right-hand side of the first row containing the name of this identifier. So, in this
case, the value of Pis 4 and the value of N is 1 (thus hiding the former value
N =4).
Step 5: When the value of the function (in this case, NIL) is returned on exit,
the original environment is restored, in other words, the evaluation continues
with
N 4
global
and there will be no trace of the former environment. For example, if we enter
the following program
'(FACT 4)
:?ACTOR:
N 2
as (FACT 4) calls (FACT 3) which
calls (FACT 2) which calls (FACT 1)
N 3
N 4
global
?(* N N)
24
MICRO-MANUAL 49
The answer is 24 as the value of N was 1 in the environment in which the atom
FACTOR: was printed.
We will see how the corresponding forms are evaluated for NEXPR, FEXPR,
and MEXPR .
Example 1
? ( ( LAM nDA( X) (P RI NT X» I £ XAMP LE )
EXAMPLE
EXAMPLE
The function, which is the value of the expression (LAMBDA (X) (PRINT X)),
is the EXPR equivalent of the function print, which is of type SUBR.
But although they have the same value and effect, we find
1<£0 PRINT (LAMBDA(X)(PRINT X»>
NI L
Example 2
Suppose that we have a list of numbers (1 2 3 4 5 6) and that we want the list
of the cubes of these numbers. We write
The function cube, which is the value of the form (LAMBDA (I) (* 1 1 I)),
is coded anonymously so that it may be forgotten once the evaluation is
complete.
MAPCAR is a standard, recursive function which is defined as
The value of MAPCAR is the list that is obtained from the list of the first
argument, where the function of the second argument has been applied to each
term. This function must of course be monadic, in other words, it must require
only a single argument. MAPCAR is one of these functions that you find in any
LISP interpreter, but nevertheless, as you may realise, its absence may easily be
remedied.
Example 3
This example shows how to create an anonymous function:
?«NLAMBDA(L)L)
«NLAHBOA(L)L) 'OF 'ANY 'NUMBER 'OF
'EXPRESSIONS) )
«OF ANY NUMBER OF EXPRESSIONS»
Notes
When the value of the functional term is a function of type NEXPR, the method
for evaluating an EXPR form is altered only in step 3 where the single function
variable is linked to the list of the values of the calling arguments.
Certain interpreters mix EXPR and NEXPR together by using a cunning
representation of the list of variables. If the latter is an identifier other than
NIL, then the function is an NEXPR, otherwise it is a normal EXPR. For these
interpreters we write
52 LISP
?(01:: LIST L
L )
LIST
This may even allow the simple creation (using dotted pairs) of functions
having, at least, a certain number of arguments. For example
?«LAMBOACNl N2 . N3)
(PRINT (LIST 'Nt '~ N1»
( P R I NT (L I !) T • N2 '= N 2 ) )
(PR I NT (L I ST 'N3 'r: N3»
T )
'CA) 'S IC (LIST) )
(Nl (A»
::
(N2 :: B)
C N3 :: (C NIL»
T
?«FLAMBDA (ARCS)
? (PRINT (LIST 'ARCS '= ARCS»
? T)
ARC! ARC2 'ARC3 )
(ARCS =CARCI ARC2 (QUOTE ARC3»)
T
Notes
The way to evaluate an FEXPR form is as follows. Let us evaluate
«FLAMBDA (ARGS) (PRINT (LIST 'ARGS '= ARGS))T)
ARG 1 ARG2 'ARG3 )
in the global environment.
Step 1 is common to the evaluation of all forms and involves evaluating the first
term of the form in order to find out the function type. Here it is an FEXPR.
MICRO-MANUAL 53
Step 2: Linking the single variable (in this case ARGS) to the arguments of the
!
calling form, the following environment is created:
global
The forms present in expressions . . . will be evaluated for as long as the value
of condition is non-NIL. Condition is evaluated at the top of the loop whose
value will always be NIL.
By using WHILE, we could, for example, rewrite the basic function
54 LISP
1(OE TOPLEVEL ()
(WHILE T
(PRINT (EVAL (READ») ) )
TOPLEVEL
But FEXPR functions are not used just for that. Consider
The value returned by LISTI is none other than the program that must be
evaluated in order to obtain the result. This programming method uses the
following relations:
(LIST) may be replaced by NIL
(LIST expression 1 other expressions . . .) may be replaced by
(CONS expression 1 (LIST other expressions . . .))
One of LISP's characteristics that should be appreciated is that occasionally
it is simpler to calculate the program (the form) whose value is the required
result rather than directly to calculate this value.
Notes
In LISP jargon, we say that functions of type
EXPR are of type EVAL, SPREAD
NEXPR are of type EVAL, NOSPREAD
FEXPR are of type NOEVAL, NOSPREAD
EVAL/NOEVAL indicates whether the arguments are evaluated or not, and
MICRO-MANUAL 55
SPREAD/NOSPREAD whether the arguments are distributed among the
different variables or not.
You will note that NOEVAL, SPREAD functions have not been defined
here. They are not generally available, but may easily be written using the
FEXPR as a starting point.
You will have noticed that two functions were required when programming
LISP as an FEXPR:
LIST whose only aim is to gather up all its arguments into a single
list and to give them to
LISTl which builds the equivalent program
The previous programming method combines these two functions into one
and removes the unpleasant result that could arise if we were to write (using
the FEXPR definition of liST)
56 LISP
A NIL
global
A NIL
global
-1- A
global
Nil
?(SKIP 'PLEASE-STOP)
SKIP
?IGNOREO
SKIP
?ATOMS
SKIP
?(LIST ALSO)
SKIP
?PLEASE-STOP
END-OF-SKIP
58 LISP
• (FUNCTIONP expression)
Argument: expression may have any value.
Value: FUNCTIONP is a predicate that returns T or NIL according to
whether the value of expression is a function or not.
Four types of function have been analysed. Some interpreters offer others,
which may complete those that have been discussed here (for example
NOEVAL, SPREAD or MACRO, SPREAD) or which may be totally new
(FUNCTION, CLOSURE ...), but this is beyond the scope of this book.
• Four definition operators correspond to these four function types: DE, DN,
DF and DM. These operators are not primitive since they may be programmed
in the following manner:
• (SET identifierexpression)
Arguments: The value of identifier is an identifier. expression may have any
value.
60 LISP
-y 1
Z NIL
N 5
N 3
X 2
U 1
global
CAR car
CDR cdr
TRY (1234)
y 1
Z NIL
N 5
N 3
X 3
U 1
- global
MICRO-MANUAL 61
The form
y 4
Z Nil
N 4
N 3
global
X 3
CAR cdr
U 1
CDR cdr
TRY Lii!341
Finally, supposing that the identifier TEST does not appear anywhere in the
environment, the form (SET 'TEST (* N N)) will change the environment to
y 4
Z NIL
N 4
N 3
X 3 CAR cdr
U 1 CDR cdr
~
TRY (1 234)
TEST 16
62 LISP
In other words, the assignment of a variable means changing the first line
where it appears in the current environment. If the variable is not defined in
the environment, it is added to the global environment: it will therefore be
available to the entire interpreter and will remain there. If you want an assign-
ment to be only temporary, you must make sure that the identifier concerned
appears in a list of variables of some function that will cause the assignment to
disappear when it returns a value. Consider
?X
GLOBAL
The function PGCDI codes the loop with label 100. This programming
method is of use only when trying to imitate the FORTRAN model as closely
as possible. In fact, it is not far short of resemblingits model, but is far from
possessing the elegance of the following definition:
The (MOD NX NY) calculation is executed only once and assigned to the
local variable NZ, which will disappear once PGCD has been calculated. The
form (LET bindings expressions . . .) allows the evaluation of expressions . . .
using local variables which appear in bindings. LET is a macro-function replacing
(identifier n expression n) )
expressions . . .)
64 LISP
by
«LAMBDA (identifier 1 identifier 2... identifier n)
expressions . . .)
expression 1
expression 2
expression n)
The macro-function LET is defined as follows:
Notice that LET is simultaneous in the sense that its different local variables
are all assigned values in parallel. If you wanted to exchange the values of A
and B in a certain environment, then you would write
(LET «A B)
(B A) )...)
Example
"
(B A) )
(PRINT (LIST A B» ) )
(ADD1 A)
(ZEROP A) ) )
(0 NIL)
<NIL 0)
(NIL 0)
On other occasions, you may prefer to assign the values sequentially. For
example, if we wished to calculate the first N terms of the Fibonacci series
which is defined as
and
U» = 0, U1 = 1
MICRO-MANUAL 65
we would write
? (F I B to >
(55 34 21 13 8 5 3 2 1 1 0)
iidentifier n expression n) )
expressions . . .)
into
((LAMBDA (identifierl)
((LAMBDA (identi[ier2)
((LAMBDA (identifier n)
(PROGN expressions . . .) )
expression n)
expression2 )
expressionl)
66 LISP
LET and LETS are useful macro-functions which are standard in good
interpreters. They may be compared with the BEGIN...END blocks of certain
languages (ALGOL 60, 68, etc.) where local variables may be declared.
3.7.4 Conclusions
The micro-manual is now complete. We have discussed most of the primitive
functions in LISP and some of the more common functions. We have purposely
not exhausted the subject area because, firstly, this would require a much larger
and more detailed book and, secondly, the analysis of all the different functions
and the main characteristics that they have on different interpreters would prove
to be quite tedious. Equipped with this basic knowledge of LISP, we will now
start to tackle the second part of this book: how to program in LISP, with style.
3.8 Exercises
3.8.1 Write the function WHILE (given in the text as an FEXPR) as a macro-
function.
Compare the two programming methods by using
This style has been called 'unsophisticated' as it is certainly the most generally
used and the most lacking in characteristics that have given rise to the names of
other styles. In this chapter we will try to give an idea of the techniques used to
handle recursion by explaining the main points that comprise the writing of a
function.
Consider the programming of the function REVERSE, which returns, as
value, its argument list in reverse. Thus
? (REVERSI: I (A) )
(A)
?(REVERSE '(A B C»
(C B A)
We shall show how we can solve this problem in LISP, which is arecursive
language; in other words, it is a language that allows recursive functions to be
defined.
To solve a problem recursively is to deal with a problem in such a way that
either (1) we can rewrite it as one or several simpler problems of the same type,
or (2) we know how to solve it quite simply (using 'the test for end of recursion'
or the 'trivial case ').
To return a list, the trivial case is when the empty list is obtained after
reversing the empty list. In the case where the list is not empty, we can diagram-
matically represent this list, as well as the result obtained, by
(terms. .. term)
~REVERSE
(tenn terms in r.,...)
If we assume that we have a function called LAST that gives the last term of
a list, as well as the complementary function ALL-BUT -LAST, then we may write
69
70 LISP
The function LAST will be defined recursively in the following way. If the
list has only a single term, then this term is the result:
(result)
If it has several terms, we will represent this as
(term terms. . .)
As term cannot be the last term of this list, we must look for this last term in
the new list tterms. . .).
We therefore write
(term terms. . .)
REVERSE
(terms~n reverse. . . term)
which corresponds (although NCONC (see chapter 8) would, in this case, be a
more efficient substitute for APPEND) to
UNSOPHISTICATED STYLE 71
<DE RCVCRSE <L)
( I F < CONS P L)
(APPEND (REVERSE (CDR L»
(LIST (CAR L» ) ) )
The functions APPEND and REVERSE are found in any interpreter worth
the name.
• FLAT
Let us look more closely at a problem that has been briefly introduced. The
argument required by FLAT may be any expression; if it is an atom, we will
return a list comprising this single atom. Thus
?(FLAT 'ATOM)
(ATOM)
On the other hand, if this atom is NIL, we will disregard it. This decision has
been taken because we have said that we wish to remove internal brackets, so
(FLAT '(ATOM NIL)) should be treated as
(FLAT '(ATOM ()))
whose value will be (ATOM).
First solution
If the argument is a non-empty list, then we can distinguish between two
situations, one where the first term is a non-empty list and the other where
it is not.
72 LISP
This is a rough program which has a lot of potential. Firstly, we can rewrite
(IF (NULL L) NIL (LIST L)) as
(IF L (LIST L))
Then we can note that there are two tests for atomic values (use of the predicate
ATOM). We also have the equality
(APPEND (LIST (CAR L)) (FLAT (CDR L)))
=(CONS (CAR L) (FLAT (CDR L)))
(To prove this equality you just need to return to the definition of APPEND or
use a theorem prover (written in LISP of course) which will easily be able to
establish this equality.)
All these simplifications lead to
Second solution
The previous solution was based on the fact that we were treating lists as being
(first term other terms . . .)
UNSOPHISTICATED STYLE 73
which is useful since this structure results frOITI consideration of the CAR and
CDR facilities. We will develop another solution for FLAT, where a list is
considered as comprising a list of terms; the solution plan will be
We have already COITIe across the recursive function MAPCAR (see LAMBDA in
chapter 3). MAPCAN is another recursive function which is only slightly different
(CONS is replaced by APPEND).
4.1 Exercises
4.1.1 Write the function MIRROR that gives the complete mirror image of its
argument. Thus
74 LISP
? (M I KROR (A D) ( (C D)
I ( E»)
«E (D C» (8 A»
4.1.2 Write a function EGYPT which, for a fraction plq calculates a list of
Egyptian fractions whose sum is equal to plq, Remember that the
numerator of an Egyptian fraction is 1. For example
?(EGVPT 34)
(24)
which is obtained by saying
3 I I
-=-+-
4 2 4
We apply the following method (from Gauss). Find n such that
I p I
-~-<--
n q n-l
Keep lIn and repeat with the new fraction
p
q n
4.1.3 Write the predicate MEMBER, which tests whether the first argument
atom is, or is not, a term of the second argument list; for example
Functions that are built in this style have one or several extra arguments which
are used to store parts of future results, or data to be handled later on. Here is
an example:
FLAT is merely the interface between the user and the function FLATI
which carries out the flattening process:
? (TRACE FLAT1)
(FLATt)
75
76 LISP
(FLAT1 D NIL)
(D>
(D>
(FLAT1 (C) (D»
(FLAT1 NIL (D»
(D)
( FLAT 1 C (D»
(C D)
(C D)
(C D)
(C D)
(FLAT1 (A B) (C D»
(FLAT1 (B) (C D»
(FLAT1 NIL (C D»
(C D)
(FLAT1 8 (C D»
(B C D)
(8 C D)
(rLAT1 A (B CD»
(A BCD)
(A BCD)
(A BCD)
(A BCD)
This style is similar to the one known as 'continuations', which we will not
show here, but which consists in being able to explicitly handle the sequence
of remaining calculations in order to get a final value. In our case, a partial result
can be handled indefinitely (see exercise 5.1.1).
Generally, functions that use buffer variables simply initialise these variables,
and let the sub-functions perform the necessary evaluations. In the situation
(FLATI '«A B) «C) D)))
(this form is incorrect since there is no second argument) certain interpreters
generate as many NILS as there are missing.arguments, which enables functions
to be merged (FLAT 1 will disappear here) in the case where the initialisation
values are exactly equal to NIL. Other interpreters, when faced with an incorrect
form, will link the variables having no argument to a special internal value
('UNDEFINED'). Any attempt to evaluate a variable whose value is undefined
would cause an error. The interpreter in this book is of this second sort.
In order to progress from the unsophisticated style to this present style, let
us give some indications as to the changes to be undertaken. In the simple
definition of FLAT, the value is
(1) either the constant NIL, or
(2) directly built by LIST, or
(3) the result of a concatenation (APPEND).
In brief, the final value is the result of a form containing only APPEND,
(LIST atom) and NIL. Let us introduce the variable RESULT:
BUFFER VARIABLES 77
(DE FLAT1 (L RESULT)
(IF (ATOM L)
(IF L (APPEND (LIST L) RESULT)
(APPEND NIL RESULT) )
(APPEND (APPEND (FLAT (CAR L»
(FLAT (CDR L» )
RESULT ) ) )
If you examine the properties of APPEND, you will notice that the following
relations can be proved:
We also have
(APPEND (FLAT expression 1) expression 2) =
(FLATI expression 1 expression 2)
So we arrive at the final version of FLAT 1.
This sort of handling may be carried out automatically by programs (written
in LISP) that are used to alter other programs and that are able to apply these
alterations correctly, by using a knowledge database (containing, for example
the equalities used above).
• The following example uses this same technique. What we are doing is
defining a function that takes a labyrinth as input and that produces the escape
path from this labyrinth as its value.
• (COND(predicate 1 expressions 1 )
(predicate 2 expressions 2 )
(predicate n expressions n . . .) )
COND is defined as follows:
BU FFER VARIABLES 79
(DM CONO (CALL)
(IF (CONSP (COR CALL»
(LIST 'IF
(CAAOR CALL)
(CONS 'PROeN (CDAOR CALL»
(CONS 'CONO (Coon CALL» ) ) )
COND is a conditional expression (like IF), and is often taken as being the
basic conditional expression instead of IF (one may be defined from the other
and vice versa). A COND form is more legible than the equivalent form:
(I F predicate 1 (PROGN expressions 1 ... )
(IF predicate 2 (PROGN expressions 2...)
(OM OR (CALL)
(IF (CONSP (CDR CALL»
(LIST 'IF (CAOR CALL) T
(CONS 'OR <CDOR CALL» )
NI L ) )
? (AND (AND)
( NU L L 'L 1ST)
(PRINT IERROR) )
NI L
(DE MEMBER (A L)
(AND (CONSP L)
(OR (EQ A (CAR L»
(MEMBER A (CDR L» ) ) )
ASSOC allows us to search the second argument A-list for a pair (if there is
one) whose indicator is indicator. The (indicator values. . .) pair is returned,
instead of just the associated value, so that we can distinguish between the
absence of a pair and an associated NIL value.
The A-list allows us therefore to store values. . . which may be retrieved by
associated indicators.
82 LISP
a
b
c
d
e
2 3 4 5 6 7
(SET 'LABYRINTH'
( ( AI) ; a linVI. tut
(D4 C4 E4)(DI Al B2)(B2 81 83 A2)(B3 82 A3)(A2 B2 A3)
(A3 A2 B3 A4)(A4 A3 AS}(AS A4 A6)(A6 A5 A?)(A? A6 B?)
CB? A? C?)(C? B? O?)(O? C? 06)(06 C6 E6)(C6 06 CS)
(C5 OS C6)(E6 06 ES)(£5 £4 £6)(£4 £5 £3 04)(Al Bl)
(C4 B4 04)(84 C4 8S)(8S 84 86)(86 BS)(£3 £4 £2 D3)
(03 £3 C3)(C3 03 C2)(C2 Cl C3 02)(D2 C2 £2)(£2 02 £3)
(Cl C2 Dl)(OI Cl £1)(£1 01 Fl)(FI £1 F2)(F2 Fl F3)
(F3 F2 F4)(F4 F3 FS)(F5 F4 F6)(F6 F5 F?)(F? F6 E?)
CE? F7) ) )
5.1 Exercises
5.1.1 Write the function ATOMS which, for any expression, gives the list of all
the different atoms that make up this expression.
5.1.2 Write a version of factorial using a buffer variable.
5.1.3 Define the conditional expression IF using CONDo
5.1.4 Change the function THESEUS and its associates so as to avoid the
situation where, if two points a and b are next to each other, we have both
(a . . . b . . .) and (b . . . a . . .)
in the list representing the labyrinth.
As the neighbourhood relation is symmetrical (if a is next to b, then
b is next to a), it is useless having this duplicate information.
6 Data Driven Programming
This style is one of the most exciting in LISP. Data driven programming is
modular and expandable. It is based on the idea of a property list (abbreviated
to 'P-list'). Each identifier has an associated P-list. We handle these P-lists by
using the functions PUT and GET. P-lists are often (but not always) A-lists
which, like the environment, are handled only by the interpreter.
84
DATA DRIVEN PROGRAMMING 85
?(PUT 'JOJO 'ALIAS 'JOE)
JOJO
• You will have noticed in previous chapters, and especially during the discus-
sion of macro-functions, that we kept on building lists where often the variable
parts were a small proportion compared to the fixed parts. For example, the
aim of the OR definition (see chapter 5) was to build the list
(CDDR CALL) must be replaced by the terms that make up its value
- - - - - --- (which must therefore be a list)
For example, if the value of CALL is (OR(OR) (ADD! A)T), the list to be
generated will be
(DM 0 R (C A LOL )
(IF (CONSP (CDR CALL»
(BUILD' (IF (: = (CADR CALL»
T
(OR c . t (CDOR CAL L ) » » ) )
The symbols := and :t indicate the places and the substitution modes with
which BUILD deals. We write BUILD as
86 LISP
Without forgetting
The differences in the second version compared to the first have been under-
lined. Any special processing to be done when := or : t are encountered is
transferred outside of the central algorithm and dealt with by the functions
placed in the P-list of BUILD. The two programs are equivalent apart from the
introduction of the extra local variable FN. The basic point of data driven
programming is that if you wish to increase the set of existing operators (:= and
:t), then the first solution means changing the code for the function BUILD,
which may cause errors and may make the entire function illegible, while the
second simply requires a new property on the P-list of BUILD. For example
DATA DRIVEN PROGRAMMING 87
with
By doing this, (FACT (ADD I 4)) will generate (TIMES I 2 3 4 5) whose value
is the one required.
With data driven programming, the data are decomposed according to their
list structure. Each list undergoes the treatment indicated by the identifier in
function position (or a standard treatment if it has no associated property). In
the case of BUILD, in order to exploit the difference between CONS and
APPEND (required by := and :t respectively), we are not interested in the
CAR, but in the CAAR of the lists.
Data driven programming is characterised by the form
(DE function (variable)
(IF (CONSP variable)
(LET «FN (GET Junction (CAR variable))))
(IF FN (FN variable)
standard treatment for list) )
standard treatment for atom) )
A variation on this may be to store an expression, and not a function, on the
P-list of/unction. Therefore the 3rd and 4th lines are replaced by
(LET ((EXP (GET 'function (CAR variable))))
(IF EXP (EVAL EXP)
An interesting characteristic of GET is that if the value of its second
argument (indicator) is not an identifier, then the value returned by GET is
always NIL. This means we can avoid testing the atomicity of the CAR of the
list which would otherwise mean having to write something like
(DE function (variable)
(IF (CONSP variable)
88 LISP
( . - (CADR CALL» »)
(CADR CALL) ) ) )
OR
!'(AND 1 2 3)
3
Now that once these definitions have been established, we may write the
new version of FLAT:
6.1 Exercises
FLAT is one of the many examples of a process that involves scanning an entire
expression and performing a certain function on each atom. Rather than pro-
gramming this scan each time, we can write a single function that carries out
this process:
(DE SWEEP (TREE BUILD PROCESS)
(SWEEPl TREE) )
(DE SWEEPl (TREE)
((NEW-CONSP TREE)
(BUILD (SWEEPl (CAR TREE))
(SWEEPl (CDR TREE)) )
(PROCESS TREE) ) )
(DE NEW-CONSP(L)
(IF (CONSP L)
(MLAMBDA(CALL) (CADR CALL))
(MLAMBDA(CALL) (CADDR CALL)) ) )
NEW-CONSP is a somewhat peculiar function that we have introduced in
order to show all the subtle variations that LISP allows. The value of the
expression
((NEW-CONSP argument) expression 1 expression 2)
is the value of expression 1 or that of expression 2 according to whether the
value of argument is a non-empty list or not. Note that only one of the expres-
sions expression 1 or expression 2 is evaluated.
The variables BUILD and PROCESS, which belong to the function SWEEP,
are all that you need to specify in order to carry out a particular scan of an
expression. PROCESS is applied to each atom in the original expression, and
the returned values are formed by BUILD.
Using this schema, we may write FLAT as
90
INSTANTIATIONS OF SCHEMAS 91
Many other functions may be adapted to this schema: we will simply use
SUBST, LENGTH and MIRROR as examples:
<DE nUBST (X Y Z)
(SWEEP Z CONS
(LAMBDA (A) ( IF (EO A Y) X A» ) )
where SUBST and LENGTH are two standard LISP functions. SUBST allows all
the atoms Y in the expression Z to be substituted by the expression X.
LENGTH gives the number of terms in its argument:
?CLENGTH NIL)
o
?CLENGTH '(A (8 C»>
2
OR, EQ, NIL and L are free variables. Usually this does not matter for OR, EQ
and NIL, as they are global variables (recognised everywhere and having a
constant value). On the other hand, what is the value of L in this expression?
Everything depends on the environment! Without going into the solution to this
problem (which requires delicate, precise and clever handling of the environments
by using the functions FUNCTION or CLOSURE which we will not discuss
here, rich though they are in interesting developments (programming by con-
tinuations, suspensions, etc.) generating new and very important efficient and
elegant styles), since we would then to a large extent go beyond the aim of this
book, which is to provide an introduction to LISP, let us mention that the
problem of capturing variables has already been met (when programming WHILE
or LIST, see chapter 7) and stems from the collision between free variables and
variables of functions of type FEXPR or NEXPR which have similar names. As
a precaution, the variables TREE, BUILD and PROCESS are banned as free
variables of the function arguments of SWEEP.
7.1 Exercises
7.1.1 Write the functions SUBST and LENGTH in their simple form.
7.1.2 Using the schema SWEEP, write a function that counts the number of
non-NIL atoms in an expression.
7.1.3 Define the schema LIT such that
(LIT '(term 1 term 2 .. . term n) 'end '[unction) generates
(function 'term 1
(function 'term 2
(function 'term n 'end) . . .))
7.1.4 Using LIT, write a function that removes all the occurrences of an atom
as a term in a list.
? ( DEL ETE I A I (A B C (A B) C B A»
(8 C (A B) C B)
8 Inside LISP
The chapter does not deal with a particular programming style, but provides an
interlude, the purpose of which is to introduce two extra primitives as well as to
offer a quick look into the organisation of the LISP memory.
We have not, as yet, talked about memory - perhaps readers who are already
familiar with other programming languages have wondered about how to reserve
vectors and arrays in LISP (as is done in BASIC, FORTRAN, ALGOL, etc.). One
great advantage of LISP is that it totally relieves you of this concern. In order to
quickly explain to you how this is possible, let us go back a bit.
In LISP we have lists and atoms. We have, however, mentioned the existence
of dotted pairs (which we can do without, as we have done up until now). The
dotted pair is represented as a box divided into two:
The dotted pair is made up of an opening bracket, any LISP object (atom,
list or dotted pair), a dividing dot, another object of any sort and finally a
closing bracket. The dotted pair (A • B) will be
(A • (B • C)) will be
A B c o
93
94 LISP
(A) (A B C)
~
~
ep=jJ
~
Therefore, «A) B C) will be
We can see straight away that the value returned when CAR is applied to a
dotted pair is the object placed in the left-most (right-most when dealing with
CDR) division of this dotted pair. Thus the value of (CDR '(A)) - that is,
(CDR '(A • NIL)) - is, of course, NIL. CONS creates a new dotted pair, for
example:
(CONS '(NON) '(EMPTY LIST)), that is
(CONS '(NON. NIL) '(EMPTY. (LIST. NIL)))
or diagrammatically
---.,.-_~~~~E0
? (CONS •A • B)
(A . B)
Of course, a dotted pair will be printed in this way only if its CDR is non-
NIL, otherwise it is a list and it will be printed as such. The function READ
can read dotted pairs on the condition that they respect the syntax
(expression 1 . expression 2). Therefore, READ can read the expressions
(term. NIL) or (term)
without any problem.
• The LISP memory comprises a collection of dotted pairs (numbered from
1 to n) as well as a collection of atoms (numbered from 1 to p). The numbers
n and p represent the maximum number of dotted pairs and of atoms
respectively that it is possible to use at the same time.
At any time there will be used dotted pairs (values or properties of known
atoms) and unused ones. When we write
?(SET 'P '(WE LOVE LISP))
(WE LOVE LISP)
(recalling the definition of TOPLEVEL), READ has, for example, constructed
the following list (when talking about dotted pairs, we have mentioned their
associated number: a dotted pair is defined by what it contains and not by its
number - this means that the list remains unaltered whatever the numbers of
the dotted pairs used):
17000 17012
1 023
~
4017
~
2019
and has given it to EVAL which will return the dotted pair 1 023 - that is, the
list (WE LOVE LISP), as value. After this form has been evaluated, the dotted
96 LISP
pairs 311, 1 301, 17 000, 17 012, 1 202, 4 and 511 are useless, and are recovered
by a 'garbage collection' algorithm which enables them to be used again. When
building a new dotted pair, CONS forces the garbage collector to find and
supply it with a free dotted pair. Therefore everything works automatically,
without any problem and without any worries to the user, until all the dotted
pairs are used at the same time, in which case the interpreter will stop through
lack of resources.
LISP is a language of lists (the name 'LISP' comes from 'LISt Processor')
but may be considered as a language of pointers, which is why there are two
new primitives RPLACA and RPLACD that allow the modification of the left
or right pointers of dotted pairs.
or diagrammatically
E8
17332
~
1020
The returned object will be the dotted pair numbered 17 332. Its left part
will contain 1 020; in other words, it will point to (FIRST):
17332
1 020
These primitives are dangerous as they can cause side-effects that are
difficult to debug; for example, the function APPEND (chapter 4) concatenates
two lists by recopying the first. The function NCONC directly concatenates the
second list to the first.
~~ I
I-~
I
I I
: I
I I
IL JI
established by NCONC
NCONC avoids useless recopying but may have undesirable effects. How-
ever, NCONC (or rather RPLACD, which is used by NCONC), does enable
circular lists to be built:
98 LISP
,?(TOMORROW)
TUESDAY
?WEEK ; alu!
(TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY
SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY
SATURDAY SUNDAY MONDAY TUESDAY ... )
We could talk a great deal about these primitives, discussing whether their
advantages are or are not compensated by the difficulties that they cause
(implicit modification of common sub-expressions). Combined with macro-
functions however, they produce an interesting result: the 'destructive-macros'.
Consider the new definition of LET:
?(EVAL E)
A
?E
«LAHDDA(X)(CAR X» (CONS (QUOTE A) (QUOTE B»)
The form generated by the macro-function LET destroys the calling fonn (using
RPLACB). which is why, if you have to evaluate E again, there will no longer be
any need to call the macro-function LET again, as it has disappeared! This is the
reason why we pass the entire calling expression and not just the argument list
(as with an FEXPR) to a macro-function variable.
Note
For convenience when programming interpreters, the predicate EQ actually tests
equality between the numbers given to objects of the same type. So it tests the
equality of two atoms and also the identity of two dotted pairs:
In the first case, the arguments used with EQ are one and the same dotted
pair, in the second case, the arguments are two different dotted pairs. However,
we might mention that certain LISP systems (although these are extremely rare)
treat CONS as a real function (in the mathematical sense); in other words, they
give the same result for the same arguments. In this case, the value of the second
expression would be T.
8.1 Exercises
Our aim in this chapter is to present some tools (either offered by the interpreters
or programmable, if unavailable) that enable programs to be tested. These tools
(or functions) do not use any new concepts that have not been previously
discussed.
Once your problem is solved on paper, in other words, once you have written
the function definitions and the associated dry runs, the first evaluations will
generally be catastrophic. You will find syntax errors (mostly missing brackets)
as well as logic errors: ranging from the most basic (inverting the alternate cases
in an IF expression, forgetting a terminator in a recursion) to the most subtle
(bad environment management). With the facilities that are usually available,
LISP allows a large range of interactions: LISP is the supreme interactive language.
Syntax errors will be avoided if you indent your expressions correctly, as does
PRETTY-PRINT. In return, an accurate comparison of the original definition
with the one produced by 'PRETTY-PRINT' will enable these errors to be
detected. There are as many standards for writing LISP expressions as there are
users and it would be pointless for us to impose our preference. PRETTY-PRINT
is a function (a classic example of data driven programming) that faces the
difficult problem of unfortunately having the right margin placed at a finite
distance from the left margin, whereas the complexity of LISP expressions
may vary.
The rest is just a question of aesthetics, knowing, for example, whether
(IF if then
else 1
else n)
is more attractive than
(IF if
then
else 1 ... else n)
Logic errors are less simple to fIX. They are detected mainly through the
controlled execution of the suspect functions. This last point requires that, for
a given function, we are able to inspect its definition. Up until now, no function,
when given a function as argument, allows its definition to be returned as value.
This function exists (or may be written) on all interpreters, but does not have a
standardised name. We will therefore suppose that the value of FUNCTIONP,
100
PUTTING LISP INTO PRACTICE 101
when applied to a function of type EXPR, FEXPR, NEXPR or MEXPR, is not
the atom T, but the list (non-empty, therefore equivalent to T) whose first term
is the atom LAMBDA, FLAMBDA, NLAMBDA or MLAMBDA, according to the
function type, whose second term is the list of variables and whose following
terms constitute the function body. Thus
?(FUNCTIONP FACT)
(LAMBDA (N) (IF (EQ N 1) 1 (*N (FACT (SUB1 N»»)
The simple trace presented above is not, in fact, the one that has been used in
this book. The programming for this is a little more complex and requires two
extra primitive functions .
• (PRINl expression)
Argument: The value of expression must be a printable object.
Value: The atom NIL. PRINI prints its argument (just like PRINT) but
does not jump to a new line after printing. Therefore, PRINl
enables several objects to be printed on the same line.
Example
• (TERPRI)
Argument: None
Value: The atom NIL. TERPRI (for 'TERminate PRInf) prints any objects
waiting to be printed (objects are placed in a waiting state by PRINI
- they are only printed through the execution of TERPRI, or
PRINT, or when the line is full) and jumps to the next line. If there
are no objects waiting to be output, TERPRI has no effect.
Note
PRINT may be defined using PRIN 1 and TERPRI:
Example
The special LISP characters may also be printed! These characters (opening
and closing brackets, dot, 'quote ') may be considered as normal characters if
they are immediately preceded by a special character called an escape character.
The escape character is often $ (or occasionally / or !). So, if we want to print
an atom whose name contains the characters '(' and ')', we write
PUTTING LISP INTO PRACTICE 103
?(ATOM (PRINT '$(FALSE-LIST$)))
(F ALSE-LIST)
T
Of course, the escape character is also a special character and must be written
twice in order to be represented.
For example
So, with a trace, we get correct indentations which provide improved legibility
and better guidelines to connected argument - value pairs (since they start in
the same column).
There are other ways of working with LISP. We will leave it up to the reader
to imagine what these are, as we hope to have convinced you that adding final
touches to LISP is simple, and enables you to work at the language level as well
as to create all the required interactions, while having the full power of the
interpreter at your disposal.
9.1 Exercises
9.1.1 Write a function LPRINT that prints its argument --list on a single line
without the outer brackets.
9.1.2 Write the function ADVISE, which, when applied to a function of type
EXPR
(ADVISE nan1e before after)
allows it to be redefined as
(DE name (variables . . .)
(LET ((BEFORE before)
(RESULT (PROGN body . . .))
(AFTER after))
RESULT))
10 A Software Robot
(ROBOT)
HELLO
JUST ASK ME PLEASE
? (I GIVE YOU A BLUE BOX NAMED ALPHA)
THANKS FOR THE BLUE BOX (ALPHA)
JUST ASK Me PLEASE
? (THEN I GIVE YOU A RED BOX CALLED BETA)
WAIT MY HAND IS FULL
I LAY THE BLUE BOX (ALPHA) ON THE FLOOR
THANKS FOR THE RED nox (BETA)
JUST ASK ME PLEASE
? (PLEASE TAKE ALPHA)
r MUST LAY THE RED BOX (BETA)
I LAY THE RED BOX (BETA) ON THE FLOOR
I TAKE THE BLUE BOX (ALPHA) IN MY HAND
JUnT ASK ME FLEA£E
? (\tlHAT DO YOU SEE)
I LOOK AT THe BLUE BOX (ALPHA)
JUST ASK ME PLEASE
? (PUT ALFHA ON BETA)
105
106 LISP
The robot uses two main facilities (MICRO-MATCH and MICRO-PARSE) whose
use is controlled by a database that contains facts about the universe and the
actions whose execution takes place within this universe, as well as by a collection
of rules that control the possible interactions between the facts and the actions
in the universe.
MICRO-PARSE is a function that takes a natural language sentence (the one
transmitted by the robot operator) and translates it in terms of actions.
MICRO-MATCH is a function that enables various pattern matches to be
performed.
Everything is centred around the idea of a universe. As was suggested in the
previous dialogue, the robot handles objects that have a shape (box, cube, sphere,
ctc.) and a colour (red, blue, yellow, etc.). The robot·has an articulated hand that
it can use to pick up or put down objects, one at a time. It can therefore build
piles of objects whose base stands on infinite ground (you can always put one
more object on the ground, whereas you may only put one object on to another).
The robot also has a single eye. It can look at the ground or at any object in its
universe. Its eye is connected to its hand in the sense that the actions 'take' and
'put' require the eye to determine the displaced object.
108 LISP
Cifi----
§§
~
-----
---~
The possible robot actions within this universe are: to look at or to pick up
an object, to put it down on another object or on the ground, to accept a new
object given by the operator or to return one to him. Each possible action alters
the universe. In order to be executed, an action requires that certain pre-
suppositions are checked: after completion, the relations that exist between the
eye and the hand, the ground and the objects are updated. A collection of rules
code this universe. The robot's intelligence is also included in these rules in the
sense that if an action cannot be executed, the obstacles are analysed and the
actions to remove them are forced.
The programs that follow are sub-divided into several parts:
(1) A collection of utility functions that you can skip over on the first read.
(2) Pattern-matching functions (MICRO-MATCH).
(3) Analysis functions (MICRO-PARSE) along with the grammar that the
robot recognises.
(4) Functions for database administration.
(5) Functions for linking dialogues.
Complex data will be presented in LISP form and in a more aesthetic form
achieved by a function like PRETTY-PRINT. The code for these will not be
given.
Various utility functions are necessary. Firstly we need a new version of BUILD.
This has an extra builder *ONE* which requires an extra argument at the time
of calling BUILD: this argument is an A-list and (*ONE* identifier) allows you
to insert the value associated with identifier (in the A-list) within the expression
to be built.
MPRINT is a function that accepts any numberof arguments and that prints
them one after another, separated by spaces.
(DE FLAG (A I)
(PUT A IT) )
The function LET that will be used here is slightly different from the one
presented before. If the calling form is
(LET «variable 1 value 1)
(variable 11 value n) )
body . . .)
then the generated form will be
«LAMBDA (SELF variable 1 ... variable n)
body . ..)
(LAMBDA (variable 1 ... variable n)
body . . .)
value 1
value n)
SELF is a functional variable (connected to a function) and can therefore
be applied to arguments just like a function.
The modification enables the body of LET to be called by SELF, thus
producing a kind of anonymous, recursive LET. This characteristic is taken
from VLISP.
(OM LET (CALL)
(BUILD '«LAMBDA(SELF (:, (MAPCAR (CACR CALL) CAR»)
(: t (CDOR CALL» )
(LAMBDA (:~ (MAPCAR (CAOR CALL) CAR»
(:1 (COOR CALL» )
( : f (MAPCAR (CACR CALL) CADR» » )
Note
This construction allows us to dispense with auxiliary constructions; for example
(variable n initialisation n) )
(IF test for end
(PROGN final value . . .)
bod)' . . .
(SELF modification 1 modification 2 ... modification n) ) )
(OM DO (CALL)
(BUILD '(LET (:= (HAPCAR (CADR CALL)
(LAKBDA(P)(LIST (CAR P)
(CADR P) » »
(IF (:= (CAADDR CALL»
(PROCN c : ~ (CDADDR CALL»)
( :, (CDDDR CALL»
(SELF (:, (MAPCAR (CADR CALL)
CADDR) » ) » )
Example
The previous factorial function may be written as
You will notice that in the use of DO above, there is no body. This construction
is taken from MACLISP.
Pattern matching is a technique that allows you to strip a list down according to
a pre-determined framework, in order to extract the parts of interest. For
exam pIe, if we wish:
112 LISP
(1) to check that a list comprises a term, which is itself composed of two
terms, the first of which is the atom HAND
(2) to be able to recover the CADR of this same term with a view to future use
we write
(MICRO-MATCH 'list '(? - (HAND! X) ?-)t T)
In the case of failure the reply will be NIL (in other words, if there is no
term whose CAR is HAND or, if there is, either because the CDR of this term
is NIL, or because the CDDR is not NIL); in the case of success, the environment
will be
((X. CADR of the first term whose CAR is HAND) • T)
By using the function ASSOC, we can find the 'value' associated with the
'pattern-matching variable': X in this environment.
The function MICRO-MATCH takes three arguments: an expression, a
pattern and an environment (an A-list) and returns an environment or NIL if
unsuccessful.
A pattern is either an atom, in which case it can only be successfully matched
with itself, or a list of patterns, and in this case it can only be matched with a
list where each term is successfully matched with the corresponding pattern in
the pattern list. A pattern may also be considered as special. There are three
special patterns:
(1) (*ONE*) is matched with any object. This pattern is abbreviated to !-.
(*ONE* identifiers is matched with any object, but in addition it associates
the matched value with the pattern-matching variable identifier. This
variable may be re-used later on to match the same value. This pattern is
abbreviated to iidentifier,
(2) (*ANY*) is matched with any sequence of any objects. It is abbreviated to?-
(3) (*LIST-WHERE-MUST-BE* patterns ... .) is only successfully matched with
a list that contains, for each pattern present in patterns . . ., a term that can
be matched with this pattern.
More formally, a pattern is defined by the following relations:
pattern =atom
or (pattern pattern. ..pattern)
or (*ONE*)
or (*ONE*identifier)
or (*ANY*)
or (*LIST-WHERE-MUST-BE* pattern pattern . . . pattern)
t ?-, !- and udentifier are abbreviations for (*ANY *), (*ONE*) and (*ONE*identifier) ,
just as 'expression is an abbreviation for (QUOTE expression).
A SOFTWARE ROBOT 113
Examples
? (MI CRO-MATCH I (A A) I ( !X ~ X) T)
« X . A) T)
(3R'X H'X HI
(3U, W' ~l
The function MICRO-MATCHis an example of data driven programming
(in this case, the pattern).
(DE MICRO-MATCH (E F AL)
(IF AL
(I F (ATOM F)
( IF (EQ E F) AL)
(LET «FN (GET 'MICRO-MATCH (CAR F»»
(IF FN (FN E F AL)
(MICRO-MATCH-LIST E F AL) ) ) ) ) )
(SET 'ROBOT-GRAMMAR I (
«(*ANY*) I GIYE YOU <*ONE*) (*ONE*COLOR) <*ONE* TYPE)
<*ONE*) (*ONE* I) <*ANY*) )
(WANT-TO-GIVE <*ONE* X) <*ONE* TYPE) (*ONE* COLOR» )
«(*ANY*) GIVE ME (*ONE* X) (*ANY*»
(WANT-TO-RCTRICYC (*ONE* X» )
«(*ANY*) TAKE (*ONE* X) (*ANY*»
(WANT-TO-TAKE (*ONE* X» )
A SOFTWARE ROBOT 115
«(*ANY*) LAY (*ONE* X) (*ANY*»
(WANT-TO-LAY (*ONE* X) FLOOR) )
«(*ANY*) PUT (*ONE* X) ON (*ONE* Y) (*ANY*»
(WANT-TO-LAY (*ONE* X) (*ONE* Y» )
«(*ANY*) LOOK AT (*ONE* X) (*ANY*»
(WANT-TO-SEE (*ONE* X» )
«(*ANY*) "'HAT DO YOU KNOW (*ANY*»
(WHAT-IE-KNOWN) )
«(*ANY*) ~IAT DO YOU SEE (*ANY*»
(WHAT-IS-EEEN) )
«(*ANY*) WHAT DO YOU HOLD (*ANY*»
(WHAT-IS-HELD) )
«(*ANY*) STOP (*ANY*»
(W'ANT-TO-STOP) )
) )
?- CIVE ME !X ?-
---) (WANT-TO-RETRIEVE IX)
?- TAKi; !X ?-
---) (WANT-TO-TAKE IX)
?- LAY !X ?-
---) (WANT-TO-LAY !X FLOOR)
?- PUT !X ON IY ?-
---) (WANT-TO-LAY !X !Y)
?- LOOK AT !X ?-
---) (WANT-TO-SEE IX)
?- STOP ?-
---) (WANT-TO-STOP)
There are two simple possibilities open to the user to improve the robot's
command interface (in other words, to allow it to recognise more sentences
more intelligently):
116 LISP
There are two types of action clauses. The first type is knowledge clauses, namely
(WHAT-IS-KNOWN)
(WHAT-IS-SEEN)
(WHAT-IS-HELD)
A SOFTWARE ROBOT 117
Their execution causes the robot to describe what it knows, sees or holds (their
name is prefixed by 'WHAT-IS-').
The second type of clauses deals with actions and comprises:
(WANT-TO-GIVE object identifier object form colour)
(WANT-TO-RETRIEVE object identifiers
(WANT-TO-TAKE object identifier)
(WANT-TO-LAY object identifier FLOOR)
(WANT-TO-LAY object identifier abject identifieri
(WANT-TO-SEE object identifiers
(WANT-TO-STOP)
(WANT-TO-OBEY)
These clauses correspond to the following actions:
(1) accept an object from the user
(2) return one to him
(3) pick one up
(4) put one down on the ground or on another object
(5) look at an object
(6) stop
(7) request a new order.
As you see, the name of these clauses is prefixed by 'WANT-TO'. Clauses
that represent actions are placed at the head of the database, in other words,
before the clauses that describe the state of the universe. The database is
represen ted as a list of clauses, prefixed by the atom DATA and is found to be
the value of this same atom: DATA. To start with, the database contains only
the clause (WANT-TO-OBEY). It is initialised as follows:
More formally:
database =(DATA action clauses . . . state clauses . . .)
The reason for the presence of the atom OATA at the head of the list of
clauses will be given with the function /DELETE/.
There are nine functions, five of which are more important than the other
four. These five are used to handle the database: each function (apart from
/FORGET/ which has no argument) is written in two ways (EXPR and FEXPR,
the latter being more useful for writing behaviour rules for the robot).
/ADD/ adds a clause to the database. If the clause is an action clause it is
added at the head of the action clauses; otherwise it is added at the tail of the
state clauses.
118 LISP
/DELETE/ removes the argument clause from the base. The algorithm used
by /DELETE/ does not distinguish the deletion of the first clause in the base as
a special case. The reader may prove this by writing /DELETE/ where the data-
base is represented as a list (possibly empty) of clauses.
/FORGET/ enables the first clause in the base to be deleted, whatever it may
be. /FORGET/ is much quicker than /DELETE/ when removing the current
action clause. But on the other hand, it is always possible to dispense with
/FORGET/ by using /DELETE/.
/ERASE/ deletes a clause which is specified by its single keyword. For
example
(/ERASE/ (EYE))
(/ERASE/ (EYE object identifier))
both delete the first clause (EYE anything. . .) present in the base.
/UPDATE/ allows you to update (or possibly to create) a clause that is
specified by its keyword. For example
(/UPDATE/ (EYE ALPHA))
alters the first clause (EYE anything. . .) to (EYE ALPHA).
These five functions operate by physically altering the database so as not to
perform useless recopying (direct or indirect calls to CONS).
(DE IrORGETI ()
(nET 'DATA (CONS 'DATA (CDDR DATA») )
WANT-TO-TAKE {3
Ci:fl
- ----- --
;:::
~
:::::: -----....~
and we inquire as to whether the clause (HAND BETA) is in context, then since
it is, we evaluate (after instantiation):
(PROGN (ANSWER BUT I ALREADY HAVE BETA IN MY HAND)
(/FORGET/) )
This means that the robot's reply to the operator is
'BUT I ALREADY HAVE THE RED SPHERE (BETA) IN MY HAND'
The function ANSWER allows the robot to address the user. Instead of object
identifier, ANSWER substitutes the sequence:
'THE object colour object form (object identifier)'
which appears in the sentence produced.
ANSWER recognises an atom to be an object identifier by its presence on
the P-list of the atom OBJECT.
A SOFTWARE ROBOT 121
(OF ANSWER (SENTENCE)
(LET (( SENTOCE SENTOCE))
(IF SENTOCE
(IF (FlA(:P 'O(U:CT (CAR SENTE~'E))
(PRCGH (LET (
(R (MICRO-HATCH DATA
(OOILD t (t.t.TST··loKRE-MUST-CEt
(TYPE t : = (CAR SENTENCE I )
(.ONE. n )
(C(LOR r = (CAR SENTENCE))
(*Ot£t C) ) ))
T )) )
!IF n
(PR(X;N (PRINt t THE)
(PRINt I~ )
(PRINl (CDR (AS~OC IC R)))
(PRIN1 '$ )
(PRINt (COR (ASSOC 'T nn I
(PRINt '$ )
(PRtNt (LIST (CAR SENTENCE)))
(PRINl '$ ) )
(ITRPRI)
U1PRINT 'm** IOSSCT
(CAR SENTENa::) I INCORIKCT
'IN DATA) ) )
(SELi- (COR SENTENCE)) )
III=" (EQ (CAP. 5ENTENCl) IFLOO~)
The order of the rules is important. The rules are applied in the order in
which they appear on the list placed in the P-list of RULES. For example, the
rules defining WHAT-IS-HELD are
« (WHAT-I£-HELD)
(HAND (*ONE* X» )
(!FORGET!)
(ANSWER I HOLD (*ONE* X» )
«(WHAT-IS-HELD»
(!FORGET/)
(ANSWeR I DO NOT HOLD ANYTHING AT ALL) )
(DE RUN-CLAUSES ()
(DO ()
«OR (NULL (CDR DATA»
(EO (CAADR DATA) WANT-TO-STOP 1) )
I
(DE ROBOT ()
(SET 'DATA (LIST 'DATA»
(/ADDI (WANT-TO-OBEY»
(PRINT 'HELLO)
(RUN-CLAUSES) )
The clause (WANT-TO-STOP1) stops the robot. This clause is introduced follow-
ing the evaluation of (WANT-TO-STOP) by RUN-CLAUSES through the synthesis
of the sentence 'Stop'.
«LAMBDA (L)
(HAFC L (LAMBDA (R)(PUT 'RULES
(CAR R)
<CDR R) ») )
I (
(WANT-TO-GIVE
« (WANT-TO-GIVE (*ONE* X) <*ONE* T) (*ONEw C»
(HAND <* ONE* Y» )
(/ADDI (WANT-TO-LAY (*ONE* Y) FLOOR»
(ANSWER WAIT MY HAND IS FULL) )
« (WANT-TO-GIVE (*ONE* X) (*ONE* T) (*ONE* C»)
(/FORGET/)
< IADDI (TYPE <*ONE* X) <*ONE* T»)
(/ADDI (COLOR (*ONE* X) (*ONE* C»)
(/ADDI (HAND (*ONE* X»)
(/UPDATEI (EYE (*ONE* X»)
(ANSWeR THANKS FOR (*ONE* X» )
>
(WANT-TO-RETRIEVE
«(WANT-TO-RETRIEVE (*ONE* X»
(HAND (*ONE* X»
(TYPE ( *ONE* X) (*ONE* T»
(COLOR (*ONE* X) (*ONE* C» )
124 LISP
(WANT-TO-TAKE
«(WANT-TO-TAKE (*ONE* X»
(HAND (*ONE* X» )
(/FORGET/)
(AN5WER BUT I ALREADY HAVE (*ONE* X) IN MY HAND) )
CCCWANT-TO-TAKE (*ONE* X»
CHAND (*ONE* Y» )
(/ADDI (WANT-TO-LAY (*ONE* Y) FLOOR»
(ANSWCR I MUST LAY (*ONE* Y» )
«(WANT-TO-TAKE (*ONE* X»
(ON (*ONE * Y) (*ONE* X» )
(/ADDI (WANT-TO-LAY (*ONE* Y) FLOOR»
(/ADDI (WANT-TO-TAKE (*ONE* Y»)
(ANSWER I MUST FREE (*ONE* X»
(ANSWER (*ONE* Y) IS ABOVE) )
«(WANT-TO-TAKE (*ONE* X»
(ON ( *ONE* X) (*ONE* Y» )
(/FORGET/)
(/DELETE I (ON ( *ONE* X) (*ONE* Y»)
(ANSWER I TAKE (*ONE* X) IN MY HAND)
(/U~DATCI (EYE (*ONE* X»)
(/ADDI <HAND <*ONE* X») )
)
(WANT-TO-LAY
«(WANT-TO-LAY (*ONE* X) <*ONE* X»)
(/FORGET/)
(/UPDATEI (EYE (*ONE* X»)
(ANSWER YOU ARE FOOLISH S!) )
«(WANT-TO-LAY (*ONE* X) (*ONE* Y»
(ON (*ONE* X) (*ONE* Y» )
(AN£WER BUT (*ONE* X) IS ALREADY ON (*ONE* Y»
(/FORGET/) )
«(WANT-TO-LAY (*ONE* X) FLOOR)
(HAND (*ONE* X» )
( I FORGET I )
(/UPDATEI (EYE (*ONE* X»>
(/DCLETEI (HAND (*ONE* X»)
(/ADDI (ON (*ONE* X) FLOOR»
(ANSWER I LAY (*ONE* X) ON THE FLOOR)
«(WANT-TO-LAY (*ONE* X) (*ONE* Y»
(HAND (*ONE* X»
(ON (*ONE* Z) (*ONE* Y» )
(/ADDI (WANT-TO-LAY <*ONE* Z) FLOOR»
<ANSWER I MUST FREE (*ONE* Y) OF (*ONE* Z» )
«(WANT-TO-LAY (*ONE* X) (*ONE* Y»
A SOFTWARE ROBOT 125
(HAND (*ONE* X» )
(/FORGET/)
(/UPDATEI (EYE (*ONE* X»)
(/DELETE! (HAND (*ONE* X»)
( ! ADD! (ON (*ONE* X) (*ONE* Y»)
(AN£WtR I PUT (*ONE* X) ON (*ONE* Y» )
«(WANT-TO-LAY (*ONE* X) <*ONE* Y»
(HAND (*ONE* Z» )
(!ADD! (WANT-TO-LAY (*ONE* Z) FLOOR»
(ANSWER I LAY (*ONE* Z) WHICH BOTHERS ME) )
«(WANT-TO-LAY (*ONE* X) (*ONE* Y»)
(AN£WER WAIT I MUST TAKE IN MY HAND (*ONE* X»
(/ADD! (WANT-TO-TAKE (*ONE* X») )
)
(\olHAT- IS-HELD
« (WHAT-IS-HELD)
(HAND (*ONE* X»
(!FORGET!)
(ANSWER I HOLD (*ONE* X» )
«(WHAT-IS-HELD»
(!FORCET!)
(ANSWER I DO NOT HOLD ANYTHING AT ALL) )
)
(WHAT-IS-KNOWN
( ( (\t/Jl AT- I S - KNOWN) )
(!FORGET!)
(ANSWER I KNOW THAT :)
( ! ADD! (WIIAT- IS-KNO\rINl ) )
(MAPC DATA (LAMBDA(CLAUSE)
(LET «R (MICRO-MATCH CLAUSE
'(ON (*ONE* X) FLOOR)
T »)
(IF R (IE-ADD! (LIST 'WHAT-IS-KNOWNl
(CDR (ASSOC 'X R»
») ) »
( I AD D I ( WH AT- I S - HELD) )
(I ADDI (WHAT- IS-SEEN» )
)
(WHAT-IS-KNOWNl
( ( <WlIAT- I S-KNOWNl (*ONE* X»)
( ! ADDI (WHAT- IS-KNOWN2 (,*ONE* X»)
(ANSWER THERE IS A STACK COMPOSED OF (*ONE* X» )
( ( (WHAT- IS-KNOWN1 »
(I FORGET/)
(AN£WER THAT IS ALL I KNOW) )
)
(WHAT-IS-KNOWN2
( ( (\tlHAT- I S-KNOWN2 (*ONE* X»
(ON (* ONE* Y) (* ONE* X» )
(!UPDATE! (WHAT-IS-KNOWN2 <*ONE* Y»)
(AN£WER UNDER (*ONE* Y» )
( ( (WJlAT- I S-KNO\tlN2 (*ONE* X» )
(/FORGET!)
(ANSWER THAT IS ALL FOR THAT STACK) )
126 LISP
(WHAT-I5-SEEN
« (WHAT-IS-SEEN)
(EYE (*ONE* X»
(/FORCET/)
(ANSWER I LOOK AT (*ONE* X»)
«(WHAT-IE-SEEN»
(/FORCET/)
(ANEWER I DO NOT LOOK AT ANYTHING IN PARTICULAR) )
)
(WANT-TO-EEE
«(WANT-TO-SEE (*ONE* X»)
(/UPDATEI (EYE (*ONE* X»)
(/FORGET/)
(ANSWER I LOOK AT (*ONE* X»
)
»»»»»») )
(\tlHAT-IS-KNO'JN)
::) ( IFORGETI )
(ANSWER I KNOW THAT :)
(!ADDI (WHAT-IS-KNOWN1»
(MAPC DATA
(LAMBDA (CLAUSE)
(LET «R (MICRO-HATCH CLAUSE
• (ON! X
FLOOR
T »)
(IF R
(/E-ADDI (LIST 'WHAT-IS-KNOWN1
(CDR CASSOt 'x
R » »
) ) ) )
(/ADD! (WHAT-IS-HELD»
(/-ADDI (WHAT-IS-SEEN»
(WHAT-IS-KNOWN1 !X)
::) ( IFORGETI )
(/ADDI (WHAT-IS-KNOWN2 IX»~
(ANSt,lER THERE IS A STACK COMPOS.ED OF ! X)
(WHAT-IS-KNOWN1)
::) c t FORGET/)
(ANS~lER THAT IS ALL I KNOW)
(WHAT-IS-KNOWN2 IX)
. .. (ON ! Y ! X) ...
::) ( ! UPDATI: I (WHAT-I S-KNOWN2 'Y»
(ANSWER UNDER IY)
(WHAT-IS-KNOWN2 ~X)
::)(/FORGI:T/)
(ANSWER THAT IS ALL FOR THAT STACK)
A SOFTWARE ROBOT 127
( vn AT- I S - SEE N )
· .. (EYE ! X) . . .
~:></FORGET/)
(AN£WER I LOOK AT IX)
( WI[AT - I :; - SEE N )
FORGETI )
::} ( I
(AN£WER I DO NOT LOOK AT ANYTHING IN PARTICULAR)
(WHAT-IS-HELD)
· .. (HAND ! X) . . .
::) ( I FORGETI )
(AN£WER I HOLD IX)
( vn AT- I S - II E L D )
=-=)</FORGET/)
(ANSWER I DO NOT HOLD ANYTHING AT ALL)
(WANT-TO-GIVE IX IT !C)
· .. <HAND I Y) . . .
::)(/ADDI (\l/ANT-TO-LAY IY FLOOR»
(ANSWER WAIT MY HAND IS FULL)
(WANT-TO-GIVE IX !T IC)
::) ( I FORGET' )
('ADDI (TYPE !X !T»
(/ADD' (COLOR IX !C»
( , ADD' ( HAND I X ) )
('UPDATEI (EYE! X»
(ANSWER THANKS FOR IX)
(WANT-TO-RETRIEVE IX)
... <HAND IX) <TYPE IX IT) <COLOR !X i c» ...
~)(ANSWER I GIVE YOU BACK IX)
(/ERASEI (EYE»
(/FORGET/)
</DELETE' (HAND IX»
< IDE LET E I ( TY PEl X IT»
(/DELETEI (COLOR IX IC»
(WANT-TO-RETRIEVE IX)
ADDI (~/ANT-TO-TAKE I X) )
::) ( I
<ANSWER HUMH I MUST LOOK FOR IX)
(WANT-TO-TAKE IX)
. .. <HAND I X) ...
:: ) ( I FORGET' )
(ANSWER nUT I ALREADY HAVE IX IN MY HAND)
<WANT-TO-TAKE IX}
. .. <HAND ! Y) ...
::) ( 'ADDI (~lANT-TO-LAY I Y FLOOR»
(ANSWER I HU£T LAY !Y)
128 LISP
(WANT-TO-TAKE IX)
· .. (ON ! Y ! X) ...
::)(/ADDI (WANT-TO-LAY !Y FLOOR»
(/ADDI (WANT-TO-TAKE !y»
(ANSWER I HUST FREE !I)
(ANSWER !Y IS ABOVE)
(WANT-TO-TAKE IX)
· .. (ON ! X ! Y) ...
~)(/FORGET/)
(WANT-TO-LAY !X IX)
::) (IFORGET/)
(/UPDATEI (EYE IX»~
(AN£WCR YOU ARE FOOLISH $!)
(WANT-TO-LAY !X !Y)
· .. (ON ! X I Y) ...
::) (ANSWER BUT ! X I S ALREADY ON ! Y)
( I FORGCT I )
(VANT-TO-LAY IX FLOOR)
· .. <HAND I X) ...
~:) (I FORGET/)
(WANT-TO-LAY !X !Y)
· .. (HAND ! X) <ON ! Z ! Y> ...
I:)(/ADDI (WANT-TO-LAY !Z FLOOR»
(ANSWER I MUST FREE IY OF IZ)
(WANT-TO-LAY !X !Y)
· .. (HAND ! X) ...
:: ) ( I FORG CT I )
(/UPDATEI (EYE IX»
(/DELETEI (HAND !'X»
( I ADD I (ON ! X ! Y) )
(ANSWER I PUT !X ON !Y)
(WANT-TO-LAY !X !Y>
· .. (HAND I Z) . , ,
~:) (I ADDI (WANT-TO-LAY ! Z FLOOR»
(ANSWER I LAY IZ WHICH BOTHERS HE)
(WANT-TO-LAY !X !Y)
:~ ) ( ! UP D ATE! (E Y E I X ) )
(/FORGET!)
(ANSWER I LOOK AT IX)
10.8 Conclusions
We have just discussed the entire robot. You will notice that the central part of
the code is only about one hundred lines long (not even completely full), which
is ridiculously small for a program that deals with reasoning and natural language.
A similar program in BASIC would be a lot bigger. We can cornpare these one
hundred lines to the one hundred and fifty or so that are required in order to
write the rules of the universe. The reader can thus appreciate the mass of
pragmatic knowledge to which a computing process must have access in order
to 'live' in even a simple universe.
The user can easily broaden the scope of the robot by adding new action or
state clauses along with their associated rules. As an example, he could give the
robot several other hands, rollers on which to move about, or even a
companion. He could add complex objects to the universe, requiring two hands
to be lifted, or he could introduce restrictions on building piles of objects (such
as no sphere to be put on top of a pyramid!).
11 Epilogue
Now that we have reached the end of the book, you have acquired a great
quantity of information. Perhaps you consider this to be encyclopaedic, but
in fact it represents only some of the numerous facilities made available by
LISP. You must set up your own real LISP system: and here you are in for a
surprise ! You will find differences between this book and your system:
(1) functions with the same name but which give different results
(EQ on numbers, etc.)
(2) functions with different names, but with similar results (FUNCTIONP, etc.)
(3) functions whose arguments are not in the same order as here (PUT, GET,
MAPCAR, etc.) or functions that have extra arguments specifying additional
choices (PRINT, TERPRI, etc.)
(4) other specific functions (or conventions) - CLOCK, LaC, etc., new inter-
active functions, trace functions and error recovery functions
(5) functions that will have disappeared (MLAMBDA and FLAMBDA which
were introduced here for the sake of completeness and because we were
explicitly concerned with distinguishing between lists and functions, which
certain systems do not dot).
Once your ini tial surprise has disappeared, we hope that the subject matter
in this book will have suitably prepared you for the varied use to be made of
tIn recent systems, identifiers have often had a double value: the first (which can only be a
function created by DE, DF or DM) when the atom is in the functional position, and a
second 'normal' one (in other words, one created either by SET or by the application of
a function of type EXPR, FEXPR or MEXPR, where it appeared in the parameter list).
Usually an atom represents a global function, but if we wish to see the normal value
applied, then we replace
(atom arguments . . .)
by
«EVAL atom) arguments . . .)
or we write
(FUNCALL atom arguments . . .)
with
(DM FUNCALL(FORM)
(CONS (LIST 'EV AL (CADR FORM))
(CDDR FORM) ) )
We use this conversion in uncertain cases as in the problem of SELF in the definition of
LET in chapter 10, section 10.2.
130
EPILOGUE 131
LISP, and you will not find it difficult to read and digest various user-manuals.
To the best of my knowledge, no interpreter on the market is the same as
the one suggested in this book, which incorporates a little of everything that
exists, and which represents a kind of 'middle point'. Although lacking in basic
functions (but we have seen how to remedy this), it is relatively rich in function
types. The code for this interpreter is given in the Appendix; the understanding
of the interpreter contributes a great deal to the understanding of this book
and vice versa.
Many LISP dialects co-exist and all contain their own particular constructions
(for example, DO in MACLISP, SELF in VLISP, etc.). It is always possible to
write constructions particular to one dialect in another one, but this generally
tends to lead to reduced performance. The interpreter given in the Appendix
may of course be written in these dialects, but would probably not be very fast.
A large number of the tools (and others not even mentioned here) suggested
in the exercises are available on LISP systems. One of LISP's important character-
istics is the creation of a community that is inclined to introspection. This
community mainly studies the art of programming and analyses itself analysing
this art. From this we now have some remarkable tools (PHENARETE, CAN,
DWIM etc. - see the entries under H. Wertz, D. Goossens and W. Teitelman in
the Bibliography in chapter 14) which enrich the environment of the LISP
programmer to a great extent. The functions BUILD, CONSTRUCT. MICRO-
MATCH, etc. are given as modest examples in this book.
The tools and new programming techniques that they promote stem from
the more general concept of Artificial Intelligence and go beyond the aims of
this book.
We conclude this book by expressing our wish to see the use of LISP becoming
more popular, and by hoping we have aroused your desire to discover, in greater
detail, this fantastic means of expression - LISP.
12 Solutions to the
Suggested Exercises
1.5.1
ATOM is an atom and therefore an expression
(LIS)P is a badly formed expression (comprising a list followed by an atom)
(L(I(S(P))))) is a badly formed expression (comprising a list followed by a
closing bracket)
(CAR(L I S P)) is a list and even a program if L is a function
(CONS 'CAR ''T) is a list and a program whose value is (CAR QUOTE T)
1.5.2
(CAR '(NIL)) has the value NIL
(CDR '(ONE 2 III)) has the value (2 III)
(CADR (CONS 'L '(I S P))) has the value I
(EQ 'T (ATOM 'NIL)) has the value T
(NULL (NULL (ATOM '(NIL)))) has the value NIL
2.1.1
(FACT (FACT 2)) has the same value as
(FACT 2); that is, 2
(F ACT -1) leads to an infinite calculation since
(FACT -1) is (* -1 (FACT -2)) which itself requires the calculation of
(FACT -3) which itself requires ...
2.1.2
Even with this definition, the value of (FACT 3) is 6. The only difference is in
the order of the terms to be multiplied.
The value of (FACT 3) is
(* (FACT 2) N) in the environment where the value ofN is 3.
In other words
(* (IF (EQN N 1) 1 (* (FACT (-N 1)) N)) N)
-----~---------
132
SOLUTIONS TO THE SUGGESTED EXERCISES 133
where the value of N is 3, the value of N underlined with a broken line is 2 and
the value of N in the expressions underlined with a continuous line is 1. Which
leads us to evaluate
where the value of N is 3 except in the underlined part where the value of N
is 2; that is
(* 2 N) with the value ofN as 3.
In other words, 6.
In this example we see that LISP remembers the environments and is able to
restore them.
2.1.3
When applied to a list, the value of UNKNOWN is the left-most atom in this list.
So, the value of
(UNKNOWN '«(ATOM AND OTHER
THINGS)
WITHOUT)
ANY IMPORTANCE))
is the atom ATOM.
3.2.1
(EQ (CONS (CAR '(LIST))
(CDR '(LIST)) )
'(LIST) ) enables the comparison of two similar lists, but EQ can only return
something other than NIL if we provide it with atoms. Consequently the value
is NIL.
The value of (CDR (CONS CONS '('LIST NIL))) is ('LIST NIL). The list
created by CONS is unprintable since its first term is the function CONS. Even
though it is unprintable, this does not prevent it from being able to be broken
down by CDR.
The value of (EVAL (CONS CONS '('LIST NIL))) is (LIST). Remember that
the value of a function is itself! And therefore
(CONS 'LIST NIL) has the value (LIST)
3.2.2
The function SPRING-WATER is similar to the function NOT (which is equal
to NULL). We can check that
?(SPRING-WATER NIL)
T
?(SPRING-WATER T)
NIL
The way in which the function SPRING-WATER has been written is not very
134 LISP
efficient, as the value returned by (NULL EXP) is the same as that returned by
the IF form. Compare with the shortened definition of NOT.
3.2.3
LIST ) )
I
3.2.4
We suggest two solutions. The first one uses an extra atom called PROG, which
contains the program to evaluate:
(DE TEST ()
<IF (NULL fROGS)
'END-or-TESTS
(SET 'PROG (CAR PROGS»
(SET 'PROGS (CDR PROGS»
(EVAL fROG) ) )
(DE FROG1 (X Y)
X )
PROG 1 returns its first argument as value (but has forced the evaluation of
the second):
(DC TEST ()
( I r ( NUL L PROGS )
'END-or-TeSTS
(PROG1 (EVAL (CAR PRO~S»
(SET 'PROGS
(CDR PROGS) ) ) ) )
3.4.1
1 +V5. .
- - IS wntten as
2
ct (+ 1 ( sa RT 5» 2)
xn
is wri t ten as
n!
ct (E XP X N) (F ACT N»
3.4.2
By the first method, (;) is written as:
(DE C (N P)
(/ (FACT N)
(* (FACT P) (FACT (- N P») ) )
3.4.3
(DE SEQUENCE(ONE)
(IF (EQ ONE 1) NIL
(CONS ONE
(SEQUENCE (IF (ZEROP (MOD ONE 2))
(/ ONE 2)
(ADDl (* 3 ONE)))))))
136 LISP
3.6.1
?( PR I NT (+ 2 3»
5
5
3.6.2
3.8.1
3.8.4
We give two solutions. The first one is very simple (especially as the predicate
EQUAL exists on all interpreters).
<DE TEST-SIGNATURE (S E)
(EQUAL S (SIGNATURE E» )
(DE TEST-SIGNATURE (S E)
(IF (AND (ATOM E) (ATOM S»
( IF « EVAL S) E)
T----- - - - -- ----
(PRINT (LIST 'NOT
( LIST S E) »
NIL )
(Ir <AND (CONSP E)(CONSP S»
(IF (TEST-SIGNATURE <CAR S) <CAR E»
(TEST-SIGNATURE (CDR S) (CDR E»
(TEST-SIGNATURE (CDR S) (CDR E»
NIL )
(PRINT <LIST 'INCOMPATIBLE S E»
NI L ) ) )
By evaluating
1(TEST-SIGNATURE '(NUMBERP
SYMBOLP
SYMBOLP
(NUMBERP SYMBOLP)
SYMBOLP )
, (3 A 2 r s A B» )
we will get
(NOT (SYMBOLP 2»
<NOT <NUMBERP S»
(INCOMPATIDLE NIL (B»
(INCOMPATIBLE (SYMBOLP) NIL)
NIL
You will notice the predicate names in the signature, which are used to test
the corresponding term.
The predicates AND and EQUAL will be explained in chapter 5 and
exercise 3.8.5 (below) respectively.
3.8.5
(DE caUAL (LI L2)
(OR (EQ L 1 L 2 )
(AND (CONSP Ll)
<CONSP L2)
<EQUAL (CAR Ll)(CAR L2»
(EQUAL <CDR LI)(CDR L2» ) ) )
4.1.1
<DE MIRROR <L)
( IF (CONSP L >
(APPEND (MIRROR (CDR L»
(LIST (MIRROR (CAR L») )
L ) )
SOLUTIONS TO THE SUGGESTED EXERCISES 139
4.1.2
(DE EGYPT (P Q)
(IF (ZEROP P) NIL
(LET «N (LeT «MU (LAMBDA(I)
(IF (GE (. P I) Q)
I
(MU (ADDl I» ) »)
(MU 1) »)
(CONS N
(EGYPT (- (* P N) Q)
(* a N) ) ) ) ) )
The function MU calculates the first integer number (I) which satisfies the
condition:
p*(i+l)~q
or in other words
p 1
- ~--
q i+1
The function MU is local and will disappear when all the calculations have
been done. When the integer number has been found, it becomes the local value
of N and the process continues recursively with the fraction
p
q n
4.1.3
(Dr: MEMnER (A L)
(AND (CONSP L)
(OR (EQ A (CAR L»
(MEMBER A (CDR L» ) ) )
5.1.1
ATOMS is easily defined along the same lines as the version of FLAT which
uses buffer variables:
(Dr: ATOMS (L)
( ATOMS 1 L NIL) )
( 0 E ATOMZ 1 (L R)
( IF (ATOM L)
( IF (OR (NU L L L)
(MEMBER L R) )
R
(CONS L R) )
(ATOMSl (CAR L)
(ATOMS 1 (CDR L) R) ) ) )
140 LISP
5.1.2
(DE FACTI (N R)
(IF <LE N 1)
R
(FACT1 <SUB1 N) (* N R» ) )
5.1.3
(OM IF (CALL)
(LIST 'CONO
(LIST (CADR CALL)(CAOOR CALL»
(CONS 'T (CONS NIL (CODOR CALL») ) )
5.1.4
We just need to alter the function NEIGHBOURS:
6.1.1
6.1.2
CONSTRUCT is written along the same lines as BUILD:
; VI. 1. 2
<DE CONSTRUCT (L>
(IF (CONSI' L)
(IF (CONSP (CAR L»
(LET «FN (GET 'CONSTRUCT (CAAR L»»
(IF FN <FN L)
(LIST 'CONS
(CONSTRUCT (CAR L»
(CONSTRUCT (CDR L» > > >
(LIST 'CONS (LIST 'QUOTE (CAR L»
(CONSTRUCT (Con L» ) >
(LIST 'QUOTE L) ) )
7.1.1
(DE SUDST <x Y Z)
(IF CCONSP Z)
(CON£ (£UBST X Y (CAR Z»
(SUBST X Y (CDR Z»
(IF (EQ Y Z> X Z) ) >
7.1.2
<DE COUNT (L)
(SWEEP L
PLUS
<LAMBDA(A)(IF A 1 0» ) )
7.1.3
7.1.4
(DE DELETE (A L)
<tIT L NIL
(LAMBDA(X R)
( IF (EO X A) R
(CONS X R) ) ) ) )
8.1.1
(DE REVERSE1 (L P)
( I r (CONSP L)
(REVERSEl (CDR L) (RPLACD L P»
l' ) )
.
NIL r-----------------·
: :
9--1~1---+--:+-1
I , I
A B
C N:L
I
I
L _ _ _ _ _ _ _ _ _ _ _ _ _ _II
9.1.1
(DE LI'RINT (L)
( IF (CONSP L)
(PROCN (PRIN1 (CAR L»
(PRIN1 .~ )
(LPRINT (CDR L» ) >
L )
SOLUTIONS TO THE SUGGESTED EXERCISES 143
9.1.2
(OM ADVISE (CALL)
(LET «0 (FUNCTIONP (EVAL (CADR CALL»)))
( IF (AND 0
(CONSP D)
(EQ (CAR D) 'LAMBDA) )
(BUILD 1
(DE (.... (CADR CAL L) )
( ::~ (CAOR D»
(LET «BEFORE (:-= (CADDR CALL»)
(RESULT (PROGN c . 1 (CDOR D»»
(AFTER (:= (CADDDR CALL») )
RESULT ) ) ) ) ) )
13 Appendix: LISP In LISP
144
APPENDIX: LISP IN LISP 145
(IF (~UNKNOWNP V)
(PROCN (PRIN1 MSC5)(~PRINT EXP)
(~pnINT MSC6)
(~EVAL (~READ) ENV) )
V ) ) )
( T ( ~ ERROR» » ; ... 111•• but ...
ENVARCS » )
ENVBODY ) ) )
« EQ TYPE 'FEXPR)
(~EPROCN ('BODY-FIELD FN)
'UNKNOWN
(BIND (SPREAD ('ARCS-FIELD FN)
( LIST ( ~C DR FORM» )
ENVBODY ) ) )
«EQ TYPE 'MEXpk)
('EVAL ('EPROCN ('BODY-FIELD FN)
~UNKNOWN
(BIND
(SPREAD (~ARCS-FIELD FN)
( LIST FORM) )
ENVBODY ) )
ENVARGS ) )
( (EO TYPE CLOSURE) I ; tJae faao... f1m&rv
(~APPLY (~FUNCTION-FIELD FN)
FORM
ENVARCS
(~ENV-FIELD FH) ) )
( (EO TYPE • DELAY) ; for 1&1J purpose_
(~EPROGN (~BODY-FIELD FN)
~UNKHOWN
(BIND
(SPREAD (~ARGS-FIELD FN)
(~CLOSLIS (~CDR FORM)
ENVARGS ) )
ENV80DY ) ) )
(T (~ERROR» ) ) )
(T (PRINt I1SG3) (~PRINT FN) ; first tlm.lIIt be & function
(~PRINT I1SG4) ; if not uk for onl
(~APPLY (~EVAL (~READ) ENVARGS)
146 LISP
FORM
ENVARGS
ENVBODY »
ERROR if tt'tt.ell~ge
; WARNINC if ttt.Hlav_
; environment ue a Iists
(DC ~SET (A V)
(IF (SYMBOLP A)
(COR (~CHANGE-VALUE A V ENV»
(PRIN1 MSG11)(~PRINT A)
(~PRINT MSGZ)
(~SET (~EVAL (~READ) ENV) V) ) ) ; alt for & l,abol
(DE "TIMJ:S (N P)
(IF (AND (NUMBERP N)(NUMBERP P»
(TIMCS N P)
~UNKNOWN ) )
(DC "DIFFERENCE (N P)
(IF (AND (NUMBERP N)(NUKBERP P»
(DIFFERENCE N P)
~UNKNO"1N ) )
(OJ: "QUOTIENT (N P)
(IF (AND (NUKBERP N)(NUHBERP P»
(QUOTIENT N P)
~UNKNOWN ) )
APPENDIX: LISP IN LISP 149
(DE ~REMAINDER (N P)
(IF (AND (NUMBERP N)(NUMBERP P»
(REMAINDER N P)
~UNKNOWN ) )
(DE ~CREAT£RP (N P)
(IF (AND (NUMBERP N)(NUMBERP P»
(CREATERP N P)
~UNKNOWN ) )
(DE "LESSP (N P)
(IF (AND (NUMBERP N)(NUMBERP P»
(LESSP N P)
~UNKNOWN ) )
(DE ~DE FIN I T ION (FN) ; r.tum & fOnl whOle .&lu. is fn
(CONS (LET «TYPE (CADR FN»)
(COND «EQ TYPE 'EXPR) 'LAMBDA)
«EQ TYPE 'FEXPR) 'FLAMBDA)
«EQ TYPE 'NEXPR) 'NLAMBDA)
( (EQ TYPE 'MEXPR) 'MLAMBDA)
« EQ TYPE 'DELAY) 'DLAKBDA)
( (EQ TYPE 'CLOSURE) 'CLAMBDA) ) )
(Ir (EQ 'CLOSURE (CADn FN»
(LIST (~DEFINITION (CADDR FN»
(CDDDn rN) )
(CONS (CADDR FN)
(CDDDR FN) ) ) ) )
; '1 '1••• , U NeT ION CON S T Rue TOR S '1•••••• '1 •• '1••• '1'1.'1."
(CSET 'INITIAL-ENV
'«NIL. NIL)(T . T» )
153
15 I ndex of Quoted
Functions
I ADDI 118 EPROGN 109
ADD1 35 EQ 25
ADVISE 114 EQN 37
AGAIN 80 EQUAL 66
AND 79 IERASEI 118
AND conditional 89 I E-UPDATE I 118
ANSWER 120 EVAL 31
APPEND 71 EXIT 81
APPLY 50 EXP 38
ARIADNE 78 EXPAND 87
ASSOC 81
ATOM 23
FACT 15
ATOMS 83
FACT with argument test 34
FACT with buffer variable 83
BASIC-TO-LISP 33
FACT with BUILD 87
BUILD data driven 86
FACT with DO 111
BUILD simple 85
F ACT with multiplying coefficient 48
BUILD with A-list 108
FACT with printing 42
BUILDI 108 FACT with SELF 110
FIRST 80
C 41
FLAG 110
CAR 22 FLAGP 110
CDR 22 FLAMBDA 52
COND 78 FLAMBDA with MLAMBDA 66
CONS 23 FLAT data driven 89
CONSP 26 FLAT unsophisticated 71
CONSTRUCT 89 FLAT unsophisticated, simplified 72
CR 11 FLAT with buffer variable 75
C ... R 10 FLAT with MAPCAR 73
CUL-DE-SAC? 81 FLAT with SWEEP 90
FLATI 75
DAY 98 FOR-EVERYONE 78
DE 58 IFORGETI 118
DELETE 92 FUNCTIONP 58
I DELETE I 11 9 FUNCTIONP altered 101
OF 58
DIFFERENCE 35
DIVIDE 37 GE 38
DM 58 GET 84
DN 58 GREATERP 37
GT 37
IE-ADDI 118
IE-DELETEI 119 IDENTITY 30
IE-ERASEI 119 IF 27
EGYPT 74 IF with COND 83
154
INDEX OF QUOTED rUNCTIONS 155
LAMBDA 49 PGCD with LET 63
LAST 69 PGCDI 62
LE 39 PLUS 35
LENGTH unsophisticated 92 PRINI 102
LENGTH with SWEEP 91 PRINT 42
LET 63 PRINT explained 102
LET destructive 99 PROGI 58
LET with SELF 110 PROGN 25
LETS sequential LET 65 PUT 84
LIST with FLAMBDA 52
LIST with LAMBDA 52 QUOTE 30
LIST with MLAMBDA 55 QUOTE with FLAMBDA 54
LIST with NLAMBDA 51 QUOTIENT 36
LIST with uncommon variable 56
LISTI 54 READ 43
LIT 92 REMAINDER 36
LPRINT in columns 45 REPEAT 103
LPRINT on lines 104 REVERSE with APPEND 71
LPRINT on lines with spaces 109 REVERSE with LAST 70
LT 38 REVERSE without CONS 99
RPLACA 96
RPLACB 98
MAPC 109
RPLACD 96
MAPCAN 73
RUN-CLAUSES 122
MAPCAR 50
MEMBER 81
SEEN-BEFORE 78
MICRO-MATCH 113
SEQUENCE 41
MICRO-MATCH-LIST 113
SET 59
MICRO-PARSE 114
MINUS 36 SIGNATURE 66
SKIP 57
MINUSP 38
SNAP 103
MIRROR 73
SPRING-WATER 32
MIRROR with SWEEP 91
SQRT 40
MLAMBDA 55
SQUARE 39
MPRINT 109
START 80
SUBI 35
NCONC 97 SUBST 92
NEIGHBOURS 81 SUBST with SWEEP 91
NLAMBDA 51 SWEEP 90
NLAMBDA with BUILD 89 SWEEPl 90
NLAMBDA with MLAMBDA 59
NOT 26 TERPRI 102
NULL 26 THESEUS 78
NUMBERP 34 TIMES 36
TOMORROW 98
TOPLEVEL 32
OR 79
TOPLEVEL communicative 43
OR conditional 88
TOPLEVEL with WHILE 54
OR with BUILD 85 TRACE 103
OTHERS 80
UNKNOWN 20
PGCD recursive 63 UNTIL 57
PGCD the FORTRAN way 62 UNTIL with BUILD 89
156 LISP
UNTRACE 101 WHILEI 53
UNWIND 80 WHILE with MLAMBDA 66
IUPDATEl 119
WHILE 53 ZEROP 38