A I Languages
A I Languages
Languages-1:
UNIT 1 A.I. LANGUAGES-1: LISP LISP
1.0 INTRODUCTION
The task of solving problems using computer as a tool, in general, is a quite a LISP has jokingly been called „the
most intelligent way to misuse a
comprehensive task. Ever since the use of computers in solving problems, it has been computer‟. I think that description
found that the solving of problems can be facilitated by using appropriate is a great compliment, because it
style/paradigm for a given type of problem and using a language designed and transmits the full flavour of
developed according to the basic principles of the style. liberation; it has assisted a number
of most gifted fellow humans in
thinking previously impossible
Some of the well-known programming languages like C support imperative style of thoughts‟
programming for solving problems with the help of a computer. The major feature of
imperative style is that the proposed solution is expressed in terms of variables, Edsger W. Dijkstra
declarations, expressions and commands. Declarations assign names to locations in
the memory and associate types with the values. Commands may be thought of as
names for actions, that are required to be executed by a computing system, mainly to
change values stored in the memory locations. Commands are generally executed in
the order, from top to bottom, as these appear in the program, though through
conditional and unconditional jumps, flow of execution can be changed. One of very
important concept in imperative style of programming is that of the state (of memory),
i.e., the set of values assigned to various locations in the memory at a particular point
of time.
1.1 OBJECTIVES
LISP has been one of the most popular languages for AI applications. LISP was
specifically designed for A.I. applications, and we have already mentioned that AI
applications involve symbolic processing, instead of mere numeric processing. Also,
AI systems are generally large and complex. Their development requires that the
implementation language and support environment provide flexibility, rapid
prototyping and good debugging tools. On all these counts, LISP wins over all other
*LISP is based on a formal system called -calculus (Lambda-calculus) originally proposed by Alonzo church and
who developed it later alongwith Stephen Kleene as a foundation for Mathematics.
6
programming languages. Hence, the language was used in its earliest applications for A.I. Languages-1:
writing programs which performed symbolic differentiation, integration and LISP
mathematical theorem proving. Later applications mainly written in LISP, include
expert systems and programs for common sense reasoning, natural language
interfaces, education and intelligent support systems, learning, speech and vision.
LISP has been found quite useful for the purpose of systems programming to the
extent that LISP machines have been developed in which whole of the programming
from top to bottom is in LISP. In LISP machines, which are personal computers, the
operating system, the user utility programs, the compilers, and the interpreters are all
written in LISP.
(ii) The above example also shows that prefix notation is used in LISP, i.e., the
operator „*‟ comes before operands x and y. The prefix notation has the
advantage that the operators (like +, , * etc.) can be easily located in an
expression.
Product = x * y;
Difference = z u
Result = product + sum
It is easily seen that the emphasis is on assigning values to the variables, viz,
product, difference and result.
(iii) The language LISP allows programs to be used as data and vice-versa.
LISP has mainly one data structures viz list, in addition to the elementary
data types: number and symbol. All expressions, whether data or programs,
mainly are expressed in terms of lists.
The main advantage of this property of LISP is that the declarative knowledge, i.e.,
information about properties of an object can be easily integrated with procedural
knowledge, i.e., information about what actions to be performed. The facility of
uniform representation in LISP is also useful in the sense that it allows us to write
LISP programs which can modify other programs (written in any language)
including themselves.
LISP program that can write entirely new LISP programs.
AI programs that learn new tasks.
7
A.I. Programming (iv) LISP is a highly modular language and hence suitable for development of
Languages
large software.
(v) The LISP environment provides a facility called Trace by using which
programs written in LISP can easily keep track of the various instructions that
have been executed, the number of times each has been executed and the
order in which the instructions have been executed.
(vi) LISP, being based on the mathematical discipline of -calculus, is the most
well-defined of all the programming languages. Hence, programs in LISP
are more reliable. The well-definedness of a language is a very important
issue as can be seen from the fact that due to a small error in FORTRAN
program of the type quoted below and FORTRAN environment’s
incapability to detect it, lead to the loss a spaceship.
In FORTRAN, the statement
DO 12 I = 1,5
denotes the beginning of a loop, whereas the statement
DO 12 I = 1.5
is an assignment statement. Through the execution of the second statement,
the value 1.5 is assigned to the variable DO12I, because blanks are ignored at
all places in FORTRAN.
(vii) Comments in LISP are given by using the character for semicolon i.e. „;‟ as
the first symbol on the line which is to be treated as a comment. Sequence of
characters on a given line after semi-colon, is treated as a comment.
(viii) The types of the variables are not required to be declared in the beginning
as is done in imperative languages like, FORTRAN or C. Also a variable
name say x, we can assume any type for the values of the variable within the
same program or procedure.
can be written in any one of the following (or even other) formats:
(x( (x(y
yz) or z
u )
) u)
(x) We should note that the two parentheses viz the left parenthesis denoted by
„(„ and the right parenthesis denoted by ‟)‟ are two most important
characters in LISP and must be used very carefully. They are used to
denote lists and for each left parenthesis, there is a right parenthesis for any
valid LISP expression. In the light of the above fact, the expressions
xyx ) or (fg
Next most important character in LISP is quote, the role of which is explained
after some time under (ii) of evaluation of S-expr.
8
(xi) Separator. Blanks are used to separate S-exprs, i.e., any valid Lisp entity A.I. Languages-1:
or object, specially numbers and symbols. Lists are always separated LISP
automatically from other S-exprs. Comma is used for special purposes
and not as a separator.
(3a(cd))
( this is a list of only symbols)
( )
But the following expressions, one on each of the next three lines is
not a valid list.
(ba3
this is not a list
) a b c (
Notation for a valid object in LISP is called S-expressions or just S-exprs (shorthand
for Symbolic Expression). Even objects themselves are also referred to sometime as
S-exprs. The main data types of LISP objects and their interrelationships are shown
in the following diagram:
S-exprs
atom string
list
symbol
number character
Note that 3 + 11 is not a number but a symbol. The reason for this is that any
arithmetic expression involving at least one operator, when represented in LISP, has
to be a list with first and the last characters as „( ‟ and „)‟ respectively, and first
element within the list being necessarily an operator. If we intend to represent the
arithmetic expression 3 + 11 which is equivalent to 14, then it is represented as
( + 3 11). 9
A.I. Programming String: A sequence of characters enclosed within double quotes is a string, e.g.,
Languages
“abc 2 @ string ”
is a string
Initially LISP was designed as an interpreted language, though later compiler based
versions also became available. In the interpreted mode, the prompt visible on the
screen is the symbol „„ , which, of course, may be changed.
(+x(*yz))
‟Colour
; where colour is a symbol we get respectively the
; following situations after printing
colour
(ii) For evaluating a symbol say colour, first of all, some value or binding must have
been associated at some stage before the current evaluation. And then evaluation
of the symbol returns the associated value or binding. Suppose, earlier at some
stage, the symbol colour is given the value RED, then, if give the input colour,
i.e., if we have
colour
;then RED is returned, i.e., we get after evaluation
RED
(iii) Evaluating a number: A number evaluates to itself
For example if the input is given as
41
;then after evaluation the number 41 is returned.
(+(*x3)(y1))
Next, assume sum-sq is a function defined in a program which returns the sum of the
squares of its arguments and again suppose x is bound to 2 and y is bound to 5, then if
we have the following expression is given as input
( sum-sq ( * x 3 ) ( y 1 ) )
with < special-word > being a special word in LISP (to be discussed) and < parami >
an S-expr, is called a special form. The evaluation of special form depends upon the
special word. The parameters < parami > may or may not be evaluated depending
upon the
< special-word >.
Some of the well-known special words are: defun, cond, do, quote. We shall discuss
evaluation of these special forms at appropriate place. The special form
(quote x) is just equivalent to ’x and hence evaluates to x.
(quote (* 3 7 ) );
evaluates to
(* 3 7 )
We have already mentioned that the action of return includes printing of the
returned value. Also the action of return includes the fact that returned value can
be utillised in further processing, e.g., the S-expr
( + ( setq x 7 ) ( setq y 3 ) )
not only binds x to 7 and y to 3 but also the values 7 and 3 respectively are used
in further evaluation and
( + ( setq x 7 ) ( setq y 3 ) )
Sometimes the pairs of arguments to several occurrences of SETQ are run together
and given to a single SETQ. In such a situation, odd-numbered arguments are not
evaluated and even-numbered arguments are evaluated. Further each even-numbered
value is associated or bound to the immediately preceding (or bound to the
immediately preceding) odd numbered argument, e.g., the S-expr
( setq x ‟( 1 2 ) y 7 z 11)
12
A.I. Languages-1:
1.5 EVALUATION OF PRIMITIVE FUNCTIONS LISP
LISP has a number of basic functions. In this section, we discuss how S-exprs
involving these primitive functions are evaluated.
We have already mentioned that LISP uses prefix notation for representing functional
expressions.
We have already mentioned that the programming language LISP is mainly designed
for symbolic processing though it may be used for numeric purposes also. Symbolic
processing in LISP is mainly about manipulating lists. Here we consider main list
processing operations.
(i) car: It takes a list as an argument and returns the first element of the list. ( car ‟(d b
c)) returns the element d, ( car ‟( ( d b ) c ) ) returns ( d b ). We should note that
argument has to be a quoted list. This is in consonance with our earlier statement
that for all functions denoted by an atom, the parameters are evaluated to return
arguments for the function.
Further
(ii) cdr (pronounced as ‘KUDDR’) also takes a list as its argument and returns a list
obtained from the given list by deleting its first element e.g.
(IS AN AI LANGUAGE ).
Also if the statement ( setq x ‟( a b c ) ) is followed by the statement ( cdr x ) then the
value returned is ( b c ).
But note (cdr ‟x) returns error, because, ‟x evaluates to x and not to the list (a b c) and
the cdr of a symbol, in this case x, is not defined.
LISP provides facilities to simplify notation for a sequence of cars and cdrs e.g. the
S-expr
( car ( car ( cdr ( car ( cdr ( cdr given-list ) ) ) ) ) ).
14
where given-list is bound to some valid list, can be simplified to A.I. Languages-1:
( caadaddr given-list) LISP
or to any other S-expr like
( caar ( cdadr ( cdr given-list ) ) ).
Remark: The functions car and cdr take things apart. Next we describe three
functions cons, list and append which put things together.
(iii) Cons: takes two arguments, the first may be any (valid) S-expr but the second
must be a list. Then cons returns a new list in which the first argument is the first
element in the returned list followed by the elements of the list given as second
argument, preserving the earlier order of occurrence in the second argument.
(iv) list: may take any number of parameters, each of which is an S-expr, it evaluates
the parameters and then the arguments so obtained are grouped into a list in the
same order as the corresponding parameters are given initially, e.g., if the inputs (
setq x ‟ (a b ) ) and ( setq y ‟intelligence) are followed by the input s-expr (list ‟x y
x ‟knowledge ‟y ) then on evaluation of the last expr, we get the list ( x
intelligence ( a b ) knowledge y )
(v) Append: the parameters of the function append can not be arbitrary S-exprs but
must evaluate to lists. It removes the parenthesis from the arguments obtained by
evaluating the parameters and puts all the lisp objects so obtained into a list.
For example, if the S-exprs ( setq x ‟ ( a b ) ) and (setq y ‟ (c d ) ) are followed by
( append x y ‟ ( ( a b ) ) )
then the last S-expr on evaluation returns ( a b c d ( a b ). If we try to evaluate
( append ‟ x y ) then an error is returned, because value of ‟x is a symbol x and a
symbol can not be an argument of append.
Next, we define some more built-in list processing functions, which are quite useful in
writing LISP programs for solving problems requiring symbolic processing.
(vi) Reverse: takes a list as its argument and reverses the top level elements of the
argument e.g.
( reverse ‟ ( a b ( a b ) ( c d ) ) )
returns
((cd)(ab)ba)
(vii) Length: again takes a list as its argument and returns the number of the top level
elements, e.g.,
( length ‟ (a ( a b ) ( c d ) e ) ) returns the number 4.
(viii) Last: again takes a list as argument and returns the last top-level element of the
list, e.g.,
15
A.I. Programming (ix) Subst (stands for substitution): takes three arguments, such that each occurrence
Languages
of second argument in the third argument are replaced by the first argument.
Second argument must be an atom, e.g.,
( subst ‟ A ‟B ‟ ( D B A ) )
returns
(DAA)
Also
( subst ‟ ( A B ) ‟C ‟ ( D B A C ) )
returns
(DBA(AB))
(x) eval: In some situations, we may need another evaluation, in addition to the
evaluation provided by read evalprint loop. The function eval is explained with
following examples:
( setq x ‟y )
( setq y z ) ; then x evaluates to y but ( eval x ) evaluates to z.
Predicates are functions which return nil or t depending upon the values of their
arguments. The evaluation by some important predicates is explained in the following
table:
Further if we have (setq fifty 50) before the next S-expr then
( numberp fifty ) t
16
But A.I. Languages-1:
( number ‟fifty ) nil LISP
( zerop 0 ) t
( zerop x ) t
But
( zerop ‟x ) nil
The predicate ‘null’: null returns t if its argument is nil else returns nil, e.g.,
( null ( ) ) returns t
( null ‟man ) returns nil
( null ‟ (a b ) ) returns nil
The predicate ‘member‟: The predicate ‘member’ has a little different behaviour. It
may not return t and/or nil. The predicate member tests an atom for the membership
of a list. If an atom is not a member of the list then it returns nil, else it returns the
portion of the list starting with the atom in the list up to the last element of the list. For
example, if we define
(setq last-alphabet ‟( u v w x y z ) )
; then
( member ‟a last-alphabet ) returns nil
( member ’w last-alphabet ) returns ( w x y z )
However, the predicate member tests the atom only for top membership of the
argument list. Hence,
( member ‟w ‟ ( u v ( w x y ) z ) returns nil,
because w is not a member of the list given by the second argument, but w is a
member of a member, viz of (w x y) of the list given by the second argument.
The predicate eql: We considered two forms of predicates for equality viz. „equal‟
and „=‟. We consider another predicate eql for equality. The predicate eql checks the
equality of the internal structure of its arguments. If the structures of arguments are
identical, it returns t else nil. In order to explain the behaviour of eql, we need the
following additional information:
Each time we use the function list even with the same elements, it takes new memory
cells and creates the list. Thus the two s-exprs,
creates two lists viz list1 and list2 which are internally different though each of them
consists of the same three elements x, y and z. However, further we have
then list3 and list1 point to the same memory locations and hence
17
A.I. Programming ( eql list1 list2 ) returns nil
Languages
( eql list1 list3 ) returns t
( eql list2 list3 ) returns nil
The main differences in the behaviour of the logical operators in LISP from their
behaviour in Boolean Algebra or in some other/programming languages are:
i) The operators AND and OR may take one, two or more than two arguments
ii) The values operated by logical operators in LISP are not exactly true or false
but the values are nil and non-nil, i.e., in LISP any S-expr which is not nil has the
same logical status as that of true in Boolean Algebra. Hence, modified definitions of
the three logical operators are:
AND: The arguments of AND are evaluated from left to right until some S-expr
evaluates to nil then other arguments are not evaluated. If, at any stage, an
argument evaluates to nil, then nil is returned. However, if none of the
arguments evaluates to nil then the value of the last argument is returned.
OR: The arguments of OR are evaluated from left to right, until either some value
returned is non- nil, then the value is returned as the value of application of
OR and the rest of the arguments are not evaluated. However, every argument
evaluates to nil, then nil is returned.
Examples:
So far we have discussed only built-in functions including predicates and relations.
The special word DEFUN allows us to write our own functions and build our own
programs. To build up highly complex programs, we need
where < function-name > is a symbol which names the function being defined,
< parameter-list > is a list of distinct symbols, which forms a list of parameters to the
function and < function-body > is the sequence of S-exprs which describes (or
denotes) the desired computation.
(defun sumcube ( x y )
(+(*xxx)(*yyy)))
Note 1: We have already mentioned that in interpreter mode, every S-expr, which
appears after the LISP prompt ‟‟ is read, evaluated and printed. The execution of an
S-expr that defines a function, returns the name of the function. The name, acting as
a symbol, has a value obtained through execution, associated with it and the
associated value can be used in further processing. For example,
Note 2: Applying a function to its arguments is termed as making a function call. For
the above definition of the function sumcube, the following sequence
(setq x 3 ) ; returns 3
( sumcube 2 x) ; returns 2 * 2 * 2 + 3 * 3 * 3, i.e. 35
(cond
( < test-1 > < S-expr > < S-expr > … < S-expr > )
( < test-2 > < S-expr > < S-expr > … < S-expr > )
( < test-n > < S-expr > < S-expr > … < S-expr > )
)
Each list of the form ( < test-i > < S-expr > . . . < S-expr > ) in the above is called a
clause.
The COND form is evaluated according to the following rule :
19
A.I. Programming Evaluate the first test viz. < test-1 > in clause 1. If it evaluates to non-nil then the
Languages
remaining < S-expr > ‟s in the clause are evaluated in the order from left to right and
the value of the whole COND is the same as the value of the last S-expr in clause1.
If < test-1 > evaluates to nil, the same sequence of steps is repeated for second clause,
and so on. Until either some < test-i > evaluates to non-nil, for which earlier described
sequence of steps for the non-nil case, is followed. However, if all < test-i > evaluate
to nil then COND form evaluates to nil.
A special case of COND form is the one in which < test-i > is the first test which
invariably evaluates to non-nil and the corresponding ith clause has no other S-expr,
i.e., ith clause is of the form ( < test-i > ).
In this case, the value of < test-i > which is assumed to be non-nil is returned.
Comment (a): The example given above graphically demonstrates a style of placing
corresponding parentheses in the code. This is allowed as LISP code is format free.
Comment (b): We also know that t evaluates to itself and hence is non-nil. Therefore,
the two occurrences of the clause ( t z ) state that in case ( > x y ) is true but ( > x z )
is false then z is returned. Similarly, if ( > x y ) is false then the next clause as given
below is executed
(
( (>y z) y)
( t z )
)
In this clause, first of all, condition ( > y z ) is evaluted which, if the value happens to
be nil then the condition t in the next sub-clause ( t z ) is tested. As t always evaluates
to non-nil, hence z is returned.
The special form Do provides the power of iteration to LISP. We may recall from
our earlier studies that iterative constructs are very useful, specially for denoting long
sequences of actions by shorter code.
20
Also, LET is special form useful in LISP because, it facilitates the creation of local A.I. Languages-1:
variables and often yields code which is both compact and efficient. Let us first LISP
discuss the special form Do in detail.
end-form1 ; is to be terminated
end-form-n return-value
)
body1 ; body of Do-loop
body2
Step 1: Variables var-i are initially bound to init-i for all i in parallel
Step 2: If the S-expr viz end-test is present, it is examined. If end-test evaluates to nil
then the following sub-steps are followed:
(i) Each of body-j is evaluated, and if in body-j any S-expr of the form (return value)
is encountered, do is exited and its value is the value in return value.
(ii) We should note that only utility of the body is for exiting or for side-effects.
(iii) Next iteration starts (only if end-test evaluated as nil) with binding of each of the
var-i to the value of step-i. If step-i is omitted, the var-i is left unchanged.
(iv) Repeat whole of step2 again if in step2, end-test evaluates to nil else go to step3
The next two steps are executed when end-test evaluates to non-nil.
Step 3: Each of end-form-i is evaluated, the utility of which is for exiting or side-
effects.
( print-beginnning-integers 12 )
123456789101112 done
; here done is used to indicate the successful
As we have already mentioned that LET is used for creating local variables. The
purpose and use of LET may be explained through the simple example:
( defun explainlet ( x y )
( let
(
(x 1)
(y 2)
)
( print ‟x = x )
( terpri )
( print ‟ y = y)
( terpi )
)
( print ‟x = x )
( print ‟y = y )
The printing command ( terpri ) asks the printer to leave the line and start
printing on the next line.
x=1
y=2
( through let, we define a local loop in which x is 1 and y is 2. But, once execution
exits the loop, then x and y assume the assigned values.
x=8
y=9
Remarks: As in Do, the values of the variables within LET structure are bound in
parallel. If we wish to bind values in sequential order then we use LET *
Ex 5: Write a LISP program expo to compute i raise to power j where i and j are
natural numbers.
22
A.I. Languages-1:
1.11 INPUT / OUTPUT PRIMITIVES LISP
In this section, some frequently used I/O primitives are introduced example:
(i) The read statement for input is explained through the following:
( + 7 (read) );
8 ; the value given by the user;
15 ; the value returned by the system.
(ii) The primitive TERPRI directs the printer to start on a new line.
(iii) The primitive PRINT takes one argument. It prints its argument in the same
form in which it is received and also returns the argument as value of the print
statement.
Example :
One of the occurrences of (X Y Z U) is for the value returned by the function print
and the other occurrence is printed by the print statement.
Another Example:
( print ‟good morning‟ ); the statement an execution returns the following two
;lines
‟good morning‟
‟good morning‟
(iv) The primitive prin1 is the same as print except that the new-line characters and
space are not provided
Example
(v) The primitive princ : is the same as prin1 except it does not print the double-
quotation marks at of the beginning and end of its argument, if given, e.g.,
princ statement has eliminated the double quotes, but returned value is still having
double quotes.
(vi) Formatted Printing through the primitive FORMAT. LISP allows us to have
cleaner output through the primitive FORMAT which has the following syntax.
( format < destination > < string > arg1 arg2 ……) 23
A.I. Programming Here < destination > specifies where the output is to be directed, e.g., to the printer or
Languages
to the monitor or some other external file. Default value is generally the monitor. The
word < string > in the format clause indicates the desired output string which is mixed
up with format directives. The format directives specify how each argument is to be
represented. The order of occurrence of the directives is the same as the order in
which the arguments are to be printed. The character is used before each directive to
identify the directives. Most common directives are:
Next, field widths for appropriate argument values are specified by an integer between
tilde (i.e., ) and the directive, e.g., 3D indicates the integer field width is 3.
Recursive function: is a function which calls itself repeatedly, but each call with
simpler arguments than the arguments used by the preceding call.
FACT
( fact 5 )
24
120 A.I. Languages-1:
Another simple example is given below to explain recursion in LISP: LISP
We define our own function LEN that returns the number of top-most elements in a
given list say L:
( defun LEN ( L )
( Cond
(
( ( null L ) 0 )
( t ( + 1 ( LEN ( Cdr L ) ) ) )
)
)
)
)
Ex 6: Write a function deep-length that counts the number of a atoms (not necessarily
distinct) in a given list. The atoms may be in a list which is a member of another list
which at some level occurs as an element of the given list. For example, for the list
L = ( 1 ( 2 ( 3 4 )) ( 5 ))
length L is three. However, deep-length of L is five.
Association lists are useful tools to associate attributes and their values with objects.
For example, to describe a particular book viz. LISP by Winston & Horn published by
Addison-Wesley Publishing Company in 1984, we may use the representation:
The value of the attribute title is a list viz. (LISP second edition) whereas the value of
the attribute year is 1984. The values of the attributes may be any S-expr, e.g.,
number, symbol or list. Formally we define.
Association List is a list of embedded sublists, in which first element of each sublist
is a key. In the example of book given above, the symbols title, author, year and
publisher are keys.
For a given object, ASSOC looks down the sublist (each sublist representing key s
associated value of the key) starting from the first sublist in the list, and matches the
car of each sublist with the key given as first argument of ASSOC. If the two do not
25
A.I. Programming match, ASSOC goes further down to next sublist. However, if the key and the car of
Languages
the sublist match then whole of the sublist is returned.
Another way of associating properties and their associated values to objects in LISP,
is through property lists. Considering again the earlier example of book with title as
LISP, author as Winston & Horn, Publisher as Addison-Wesley, year as 1984, we can
put this information in the database using the above-mentioned primitives as follows:
(putprop ‟book ‟LISP ‟Title ) ; putprop returns the attribute values LISP
Addison-wesley
( putprop < object – name – symbol > < attribute – value > < attribute – name > )
where < object – name – symbol > and < attribute – value > must be symbols and
< attribute –value > may be any S – expr.
The newer version of COMMON LISP avoid PUTPROP and instead use SETF.
The primitive SETF : SETF is like SETQ. However, SETF is more general than
SETQ. The primitive SETF also takes two arguments, but the first argument is
allowed to be an access function in addition to being an atom. An access function
includes car, cdr and get. Second argument to SETF is the value, as is in the case of
SETQ. The above-mentioned LISP statements using purtprop can equivalently be
replaced by the following statements using SETF and GET :
The general format for associating values to attributes of an object using SETF and
GET is
( SETF ( GET < object – name –symbol > < attribute – name > )
< attribute – value > )
SETF can also be used to replace values of car or cdr of a list as follows:
26 ( SETQ L ‟ ( x y z ) )
(xyz) A.I. Languages-1:
( SETF (Car L ) ‟a ) LISP
(ayz)
( SETF (Cdr L ) ‟ ( u v w ) )
(auvw)
In general ( SETF ( car < list > ) < expr > ) replaces the car of < list > by < expr >
This usage of SETF allows us to change the values of attributes, whenever required.
etc.
The general format of GET, in order to find the value of the attribute having name
as < attribute – name > of the object having name as < object – name >, is :
If there is no value in the data-base for < attribute –name > and < object – name >, the
value nil is returned.
Note : When more than one SETF or PUTPROP are used to give different values to
the same attribute of a given object then the effect of only the latest remains. Earlier
values are overwritten. In order to change values of attributes, we write another
statement using SETF or PUTPROP. Thus, in continuation of our example about the
book entitled LISP by Winston & Horn, if in addition to the earlier statement, we give
the following statement:
It may be noted that if &optinal keyword had not been available and/or had we
defined exponentiate by replacing the parameter-list ( n & optional m ) by ( n m ),
then there would have been an error if m is not supplied assuming it to be 10.
( our-sum-3 5 6 )
(our-sum-3 ) 5 6 7 )
18
(our-sum-3 5689)
ERROR
The above ERROR occurred, because for correct response by our-sum-3, minimum
number of arguments in this case must be 2 ( i.e., number of parameters before
&optimal and maximum number in this case, must be 3 (i.e. number of all parameter
before or after &optimal ), but in the last call to our-sum-3, we supplied four
arguments viz. 5, 6, 7, and 8.
Now, it is not always possible to remember the exact number of optional parameters
and hence not always possible to check erroneous function calls. To remedy this
situation, LISP provides for the keyword &REST which is followed by exactly one
argument say „remaining‟. Then if m denotes number of parameters before &optional
and n the number of parameters after &optional but before &rest and whenever k
arguments are supplied and k > m + n, then all the remaining ( k – ( m + n ) )
arguments are grouped into a list and bound to &rest. In order to explain the ideas
explained above, let us define a function say specialsum-3 as follows.
(special-sum-3 5 7)
12
( special-sum-3 5 7 9 )
21
( special-sum-3 5 7 9 11)
21 ( 11 )
( special-sum-3 5 7 9 11 12 13 )
21 ( 11 12 13 )
When a function is to be called only once in a program then we may not like to give a
name to the function in the definition of the function. In such a situation, instead of
the keyword DEFUN we use the keyword LAMBDA. Rest of the definition of the
function remains the same as it would have been under DEFUN. Suppose we need to
compute (x2 – y2)2, the following LAMBDA expression will accomplish the task:
(LAMBDA ( X Y )
(*(–(*XX )(*YY))
(–(*XX)(*YY))
)
)
( ( LAMBDA (XY)
(* (–( *XX)(*YY)
( * X X ) ( * Y Y)
)
)
) (3 4); returns
49
APPLY takes two arguments, each of which is evaluated. The first argument, which
is either a function-name or LAMBDA expression, is applied to second argument
which is a list. FUNCALL is similar to the function APPLY with the difference that
arguments are supplied without boundary parentheses of a list. Function-names are
preferably quoted with #‟ in stead of just quote.
Examples:
( APPLY #‟* ( 2 3 ) )
6
(FUNCALL #‟* 2 3 )
6
29
A.I. Programming For the earlier defined function our-sum-3 which returns sum of 2 or 3 arguments,
Languages
what ever number of arguments out of 2 or 3, are supplied. Let us consider
( APPLY #‟ our-sum-3 ( 4 5 ) )
9
( FUNCALL #‟ our-sum-3 4 5 6 )
15
( APPLY #‟ ( LAMBDA ( X Y ) ( * ( + x x ) ( + y y ) ) ) ( 3 4 ) )
48
( funcall #‟ ( LAMBDA ( X Y ) ( * ( + X X ) ( + Y Y ) ) ) 3 4 )
48
The Backquote facility : The backquote is just like quoted expression and evaluates
to itself except the following difference : Those subexpressions of the expression that
are preceded by a comma or by the comma followed by the symbol @ are evaluated
and substituted appropriately before returning the result. Let A be bound to
‟ ( 3 x 4 ) then
„ ( A B C ) ; evaluates to
(ABC)
„ ( ,A B C ) ; evalutes to
((3x4)BC)
„ ( A ,A B ,@ A C ) ; evaluates to
(A(3x 4)B3x 4 c)
In the contexts in which a symbol has or is expected to have some object or S – expr
associated with it, it is called a variable. The symbol book1 becomes a variable,
when associated with the object which represents a book entitled LISP, authored by
30
Winston & Horn in the year 1984. The association between the symbol and the object A.I. Languages-1:
may be achieved through the LISP statement: LISP
The associated object may be referred to as the value of the symbol. The variable may
be considered as the ordered pair: (symbol, value).
Also, a symbol used in the parameter list of a function definition, though does not
have any associated value or object at the time of definition, yet is a variable because
it is expected to be associated with some object at the time of application of the
function.
Bound & Free Variables: A symbol that appears in the parameter list of a procedure,
is called a bound variable w.r.t the procedure. A symbol, that does not appear in the
parameter list of a procedure, is called a free variable w.r.t the procedure.
Representation of Symbols: In LISP environment, the link between a symbol and the
associated object is unique and is achieved through the following mechanism.
LISP system maintains a Symbol Table (in some part of the memory) in which each
symbol, when encountered for the first time, is entered alongwith some starting
address, say 3000, of some location in the memory where the associated object is
stored. We may note that some of the components of the object may be changed over
time, e.g., if the copies of book1 are again printed in the in the year 1988. In such a
case, the component „ (printing first 1984)‟ of the object is changed by „ ( printing
second 1988)‟. However, the entry (book1 3000) remains unchanged. Next time when
book1 occurs in a program, the LISP system searches through its possible occurrences
in the symbol table and on finding it there, does not attempt to associate with it
another address or location in memory. Further, the statements like,
will associate address 3000 ( i.e. the address associated with book1 ) with symbols
book2 and book3 as their address parts of (symbol, address) pairs. Thus, we may also
say that a variable is an ordered pair ( symbol, pointer ).
The Predicate EQ : EQ returns t if and only if the internal structures of its arguments
are identical. Hence, continuing with the earlier discussion, the value t is returned in
all the following three cases:
( EQ ‟ book1 ‟ book1 )
( EQ ‟ book1 ‟ book2 )
( EQ ‟ book2 ‟ book3 )
B
2. The list ( ( A B ) C ) is represented by the cons cell structure:
A C
B
3. The list ( A ( B C ) ) is represented by the cons cell structure:
32
C
A.I. Languages-1:
Remarks: The name cons in the cons-cell structures, is justified on the LISP
following grounds:
Let L1 be a list and A be an atom and we have LISP statement ( setq L ( Cons ’ A L1))
then L is represented by adding one cons cell in the memory as shown below:
A Structure
for L 1
Also if L2 is another list then, on the command, ( setq M ( Cons L2 L1) ) resultant list
M is obtained by adding one cons cell in memory as shown below:
M:
It can be easily seen that using the Cons cells representation for lists, operation like
CAR, CDR, ATOM etc can be efficiently implemented.
The cons-cell structure suggests that a cons-cell in memory may represent a LISP
object in which the CDR need not be a list but may be an atom or a symbol.
Dotted Pair: A LISP data structure pair is a structure like list with the difference that
CDR of a dotted pair may be an atom also. A dotted pair with CAR as symbol A and
CDR as symbol B is denoted by (A . B) with spaces around dot on both sides. Thus,
cons-box representation for dotted pair (A . B) is
A B
Now we explain the two primitives. Both RPLACA and RPLACD take two
arguments. For RPLACA, first argument is a non-empty list say bound to a symbol,
say, X and second is an arbitrary LISP object say bound to a symbol, say, Y. Then
(RPLACA X Y ) replaces ( Car X ) by Y in the given list and the resulting list is still
bound to X. For RPLACD, the first argument is again a non-empty list bound to the
symbol, say, X and the second argument also must be a list, say, bound to Y then
(RPLACD X Y ) replaces ( CDR X ) by Y and the resulting list is still bound to X.
Examples:
( SETQ X ) ‟( ( a b ) c d ) ; returns
((ab)cd )
( SETQ Y 3 ) ; returns
3
( REPLACA X Y ) ; returns
( 3 c d); Further if we give
( SETQ Z ‟ ( 3 7 9 ) ) ; returns
(379)
( SETQ U ‟( c e f ) g ) ) ; returns
((cef))
( REPLACA Z U ); returns;
( ( ( c e f ) g ) 7 9 ); this list is bound to Z
( REPLACD Z V ) ; returns
( ( (e e f ) g ) ( ( a b ) c ) )
The above two primitives viz RPLACA and RPLACD can be obtained from SETF as
follows:
( RPLACA L S ) is same as ( SETF ( CAR L ) S )
and ( RPLACD L S ) is same as (SETF ( CDR L ) S )
In general we can make changes to lists in arbitrary positions in stead of just to the (
CAR L ) and ( CDR L), as follows:
Example :
( SETQ X ‟ ( ( a b ) ( c ( d e ) f ) ) ) ; returns
((ab)(c(de)f))
( RPLACA ( Cdadr X) ‟p )
( ( a b ) ( c p f ) ) ; still bound to X
(SETQ Z ‟( ( a b ) ( c d e ) ) )
((ab) (cde))
For example to create an array structure named Matrix–3 with dimensionality 3 and
<dim–1>as 2, <dim–2> as 2 and <dim–3> as 3 of integers, the following LISP
statement is used:
The above statement creates an array Matrix–3 of 12 elements. The slots in Matrix–3
are empty and we will discuss how to fill values in the slots. The 12 slots in Matrix-3
are referred to as
The primitive AREF is used to refer to a particular slot in the array, e.g., .
( AREF Matrix–3 1 0 2) refers to the slot Matrix–3 ( 1, 0, 2)
The above value-assigning statement can be easily used to give value say integer g to
( i, j, k )th element of Matrix–3 as
In order to retrieve values from any slot, say ( i , j , k) of Matrix–3 we use the
following LISP statement:
(AREF Matrix–3 i j k)
; the value g is returned
; if g is the value stored at
; Matrix–3 ( i, j, k ) then g is returned
Note that the two-character sequence viz. # \ is used preceding a character to indicate
that the following is to be interpreted as character.
The structure created by the above type of LISP statement will be named
< structure-name > and will have <slot-i>‟s as slots. We may recall that „<entity>‟
enclosed between angular brackets indicate place-holder for entity to be suitable
replaced. The above type of LISP statement automatically generates the keyword
constructor called MAKE – <structure-name> and also automatically creates the
selector functions as <structure–name> – <slot–i> for each i.
For this purpose, a node of binary tree will have three slots: left-tree, value, right-tree.
The left-tree and right-tree are pointers.
36
Using the above description let us create the following binary tree to be called T1. A.I. Languages-1:
The children and grand-children nodes are named as T2, T3 and T4. And a diagonally LISP
crossed cell indicates nil pointer.
T1: 3
T2: 2 5 :T3
7 :T4
The following sequence of LISP statements create the tree shown above:
In the above the symbol #S indicates the fact that the part following # is a structure.
In order to access values of: left-tree, : right-tree or : value the selectors bin-tree-left-
tree, bin-tree-right-tree and bin-tree-value respectively are used, for example
Also if we may give the name of a structure then the whole of the structure would be
available, e.g.
37
A.I. Programming Further, in order to change or even create value of any component, we use the
Languages
primitive SETF. For example, if we wish to change : value component of T2 to – 5 we
can use
T1: 3
T2: –5 5 :T 3
The new shape of tree T1 after the sequence of two changes mentioned above is like:
T1: 3
T3: 5 5 :T3
T4: 7 T4: 7
1.18 SUMMARY
In order to define objects in terms of their attributes and attribute values, association
lists and property lists are used in LISP. These concepts are discussed in Section 1.13.
Some more general and robust facilities in LISP defining and applying functions in
the form of Lambda Expression, Apply, Funcall and Mapcar are discussed in
Section 1.14. The representation of symbols and associated/represented objects in
LISP environment and representation of operations on such representations are
discussed in the next three sections.
1.19 SOLUTIONS/ANSWERS
Ex 1: (i) The variable x is bound to 5 and the variable y is bound to 7. Further the
value ( 5 + 5 ) ( 7 + 7) is evaluated to 140
Ex 3: (Defun ( X Y)
( cond ( = Y 0) ‟infinity)
(t (/ X Y )))
Ex 4:
Stepwise explanation
Ex 5:
( defun expo ( i j )
( do
( answer i ( * i answer ) )
; initially answer is i and is
; multiplied in each iteration by i
(power j ( power 1 ) )
(counter ( – j 1) (– counter 1 ) )
; initially power is j and in each iteration power is reduced by 1.
; counter is an auxiliary variable
) 39
A.I. Programming ( ( zerop counter ) answer )
Languages
)
)
( expo 2 3 )
8
Remarks: The clause (power j ( – power 1) ) is actually not required. However, it is
introduced to explain. It can be deleted without affecting the overall (final) result. But
it has been introduced to explain an important point about do-loop. We may be
tempted to write the above function expo by replacing the clause ( counter ( j 1)
( counter 1 ) ) by ( counter ( j 1 ) ( power 1 ) ) i.e. replacing last occurrence of
counter by power; because it is also being computed in the earlier clause. But this
replacement will be wrong, leading to incorrect result because of the fact that in Do
loop all the variables, viz answer, power, and counter in the above example are
computed in parallel, using values from the previous iteration/ initialization. Current
values are available only in the same clause. Therefore, if „power‟ replaces „counter‟
then previous value of power would be available for processing whereas we require
the current value.
In many situations, we need sequential computation of the variables in the loop. For
this purpose LISP Provides do*. Now, the function of Example 2 above may be
rewritten using the earlier computed values of power as is given below:
( defun expo ( i j )
( do* (
( answer i ( * i answer ) )
( power j ( power 1 ) )
(counter ( power 1 )
( power 1 )
)
)
( ( zerop counter ) answer )
)
)
Ex 6: (defun deep-length (L)
( cond
(( null L ) 0)
(( list p ( car L )) ( + deep-length (car L )) (deep-length ( cdr L ))
( t ( + 1 ( cdr L )))
)
)
B
C
40
D
Ex 8: A.I. Languages-1:
(i) ( ( u v w ) ( s ( a b c) m ) ) LISP
(ii) ( ( u v w) (s ( t u ) a b c) )
41