FEDERAL UNIVERSITY GUSAU
DEPARTMENT OF COMPUTER SCIENCE
PRESENTATION
ON
TOPIC: THE LANGUAGE LISP
COURSE CODE: CSC408
COURSE TITLE: ARTIFICIAL INTELLIGENCE
PRESENTERS:
1610308003 1720308003
1710308013 1820308003
1710308023 1710308010
1710308033 1710308020
1710308053 1710308040
1710308050
INTRODUCTION
LISP is a family of high-level, general-purpose programming languages primarily used in
Artificial Intelligence (AI) research and development. The name "LISP" is an abbreviation of
"LISt Processing", referring to the fact that Lisp treats code as a series of nested lists.
LISP's strengths include its support for functional programming, its macro system for
metaprogramming, and its interactive development environment. It is also known for its
powerful list processing capabilities, making it ideal for programming in areas such as AI,
natural language processing, and symbolic computation. It continues to be used and developed
today, with applications ranging from robotics to video game development. LISP was founded
on the mathematical theory of recursive functions (in which a function appears in its own
definition).
LISP became a common language for Artificial Intelligence (AI) programming, partly owing to
the confluence of LISP and AI work at MIT and partly because AI programs capable of
“learning” could be written in LISP as self-modifying programs. LISP has evolved through
numerous dialects, such as Scheme and Common LISP.
HISTORY
The origins of Lisp date back to 1956, when a summer research meeting on artificial intelligence
was held at Dartmouth College. At the meeting, John McCarthy learned about a technique called
‘‘list processing’’ that Allen Newell, J. C. Shaw, and Herbert Simon had developed. Most
programming in the 1950s was done in assembly language, a primitive language defined directly
by the circuitry of the computer. Newell, Shaw, and Simon had created something more abstract,
called IPL (for Information Processing Language), that manipulated symbols and lists, two
important datatypes in artificial intelligence programming. But IPL’s syntax was similar to (and
as akward as) assembly language. Elsewhere in the 1950s a new language called FORTRAN was
being developed. FORTRAN was designed for the sort of numerical calculations that are
common in scientific computing. It allowed the programmer to think in terms of algebraic
expressions such as A=(X+Y)*Z instead of writing assembly language instructions. The idea that
programmers should expresss their ideas in familiar mathematical notation, and the computer
should be the one to translate these expressions into assembly language, was a radical
1
innovation. It made FORTRAN a powerful numerical computing language. McCarthy wanted to
build an equally powerful language for symbolic computing.
One approach he suggested was to build on top of FORTRAN, by creating a set of special
subroutines for list manipulation. This idea was pursued by Herbert Gelerntner and Carl
Gerberich at IBM, and was called FLPL, for FORTRAN List Processing Language. But
McCarthy himself, working first at Dartmouth and later at the Massachusetts Institute of
Technology, designed a new language, LISP (for LISt Processor), that drew on ideas from IPL,
FORTRAN, and FLPL. The first version, Lisp 1, was developed for the IBM 704 computer. Lisp
1.5 was the first Lisp dialect to be widely used. The Lisp 1.5 Programmer’s Manual by
McCarthy et al. appeared in 1962. By 1964 Lisp was running on several types of computers,
including an IBM 7094 under MIT’s Compatible Timesharing System; it was thus one of the
first interactive programming languages. Digital Equipment Corporation (DEC) also played a
prominent role in Lisp’s history. One of the early Lisp implementations ran on its first computer,
the PDP-1. The PDP-6 and PDP-10 (later DECSystem-20) computers were specifically designed
to implement Lisp efficiently. After the mid-1960s, Lisp implementations began to diverge. MIT
developed MacLisp, while Bolt, Beranek and Newman and the Xerox Corporation jointly
developed Interlisp. Stanford Lisp 1.6 was an offshoot of an early version of MacLisp; it
eventually gave rise to UCI Lisp. Each of these dialects substantially extended the original Lisp
1.5, but they did so in incompatible ways. In the 1970s Guy Steele and Gerald Sussman defined a
new kind of Lisp, called Scheme, that combined some of the elegant ideas from the Algol family
of programming languages with the power of Lisp’s syntax and data structures. Extended
dialects of Scheme began evolving, paralleling the development of Lisp. By the early 1980s there
were dozens of incompatible Lisp implementations in existence, with about half a dozen major
dialects. A project was begun, led by Scott Fahlman, Daniel Weinreb, David Moon, Guy Steele,
and Richard Gabriel, to define a Common Lisp that would merge the best features of existing
dialects into a coherent whole. The first edition of the Common Lisp standard appeared in 1984;
a revised standard will appear some time in 1989. Common Lisp rapidly became the Lisp of
choice in both academic and industrial settings. The other dialects have mostly died out, except
for Scheme, which continues to enjoy a modest popularity for educational applications.
2
Many of the more important ideas in programming systems first arose in connection with Lisp.
These include mixing of interpreted and compiled functions, garbage collection, recursive
function calls, source-level tracing and debugging, and syntax-directed editors. Today Lisp is a
leading language for sophisticated research on functional, object-oriented, and parallel
programming styles.
DEFINITION
LISP, an acronym for List Processing, is a functional programming language that was designed
for easy manipulation of data strings. A LISP program is a function applied to data, rather than
being a sequence of procedural steps as in FORTRAN and ALGOL. LISP uses a very simple
notation in which operations and their operands are given in a parenthesized list. For example, (+
a (* b c)) stands for a + b*c. Although this appears awkward, the notation works well for
computers. LISP also uses the list structure to represent data, and, because programs and data use
the same structure, it is easy for a LISP program to operate on other programs as data.
The interchangeability of code and data gives LISP its instantly recognizable syntax. All
program code is written as s-expressions, or parenthesized lists. A function call or syntactic form
is written as a list with the function or operator's name first, and the arguments following; for
instance, a function F that takes three arguments would be called as (F arg1 arg2 arg3).
FEATURES OF LISP
It’s a machine-independent programming language.
It employs an iterative design process and is easily expandable.
It enables us to dynamically construct and update programmes and apps.
It has a high-level debugging feature.
Object-Oriented Programming is supported.
It is a language based on expressions.
It may be used to support a variety of decision-making assertions, including if, when, case,
and condition.
Different iterating statements, such as do, loop, loopfor, dotimes, and dolist, will be
supported.
We can also write our own functions in Lisp.
3
Access to powerful and easy-to-integrate macros;
The language itself is programmable to meet nearly any need;
Operates on most platforms; and.
Many find programming in Lisp to be faster with smaller code footprints.
BENEFITS OF LISP
Modularity: since a LISP program consists of a set of function definitions, and it is very
easy to have one function call another, it is also easy to fit a hierarchy of modules
(functions) to the natural structure of a problem or problem- solving procedure.
Speed of Development: LISP requires a few declarations of data or data types. There is
time saved in not having to declare data and in not having to debug consistencies between
data declarations and data usages. On the other hand, LISP programmer is not prevented
from writing procedures that test data for proper format. The fact that LISP is usually
interpreted means that the programmer can easily and frequently make changes and try them
without having to wait for a compiler to translate his entire program each time.
Functional Programming: it is relatively easy in LISP to write programs whose
correctness is relatively easy to prove mathematically. A style of programming in which no
assignments or global properties are manipulated is called functional programming.
LISP WITH ARTIFICIAL INTELLIGENCE (AI)
Lisp is used for AI because it supports the implementation of software that computes with
symbols very well. Symbols, symbolic expressions and computing with those is at the core of
Lisp. Common Lisp (CL) is a dialect of the Lisp programming language. Common Lisp is great
for symbolic AI. In Lisp, all computation is expressed as a function of at least one object.
Objects can be other functions, data items -- such as constants or variables -- or data structures.
Lisp's ability to compute with symbolic expressions rather than numbers makes it convenient for
artificial intelligence (AI) applications.
Common Lisp is great for symbolic Artificial Intelligence because:
It has very good implementations (e.g. SBCL, which compiles to machine code every
expression given to the REPL).
It has a Read-Eval-Print Loop to ease interactive programming.
4
It provides a very powerful macro machinery (essentially, you define your own domain
specific sublanguage for your problem), much more powerful than in other languages like
C.
It mandates a garbage collector (even code can be garbage collected).
It provides many container abstract data types, and can easily handle symbols.
You can code both high-level (dynamically typed) and low-level (more or less statically
typed) code, through appropriate annotations.
Lisp is used for AI because it supports the implementation of software that computes with
symbols very well. Symbols, symbolic expressions and computing with those is at the core
of Lisp. Common Lisp (CL) is a dialect of the Lisp programming language. Common Lisp
is great for symbolic AI
S – EXPRESSIONS
All data and program statements in LISP are represented in terms of S-expressions (Symbolic
Expressions). S-expressions often appear as lists of items enclosed in parentheses, but they are
actually more general. An S-expression is either an atom, a list of S-expressions or a dotted pair.
Example:
A A literal atom
Sample A literal atom
4 A literal numeral
(A B C D) A list of S-expressions
(A.B) A list of dotted expressions
LISTS AND CONSES
Every list has two forms: a printed representation and an internal one. The printed representation
is most convenient for people to use, because it’s compact and easy to type on a computer
keyboard. The internal representation is the way the list actually exists in the computer’s
memory. In its printed form, a list is a bunch of items enclosed in parentheses. These items are
called the elements of the list. Here are some examples of lists written in parenthesis notation:
5
(RED GREEN BLUE)
(2 3 5 7 11 13 17)
What each cons cell actually is, internally, is a small piece of memory, split in two, big enough to
hold two addresses (pointers) to other places in memory where the actual data is stored. On most
computers pointers are four bytes long, so each cons cell is eight bytes. The term ‘‘CONS’’ is
short for CONStruct.
The internal representation of lists does not involve parentheses. Inside the computer’s memory,
lists are organized as chains of cons cells, which are drawn as boxes. The cons cells are linked
together by pointers, which are drawn as arrows. Each cons cell has two pointers. One of them
always points to an element of the list, while the other points to the next cons cell in the chain.
When we say ‘‘lists may include symbols or numbers as elements,’’ what we are really saying is
that cons cells may contain pointers to symbols or numbers, as well as pointers to other cons
cells. The computer’s internal representation of the list (RED GREEN BLUE) is drawn this way:
Looking at the rightmost cell, you’ll note that the cons cell chain ends in NIL. This is a
convention in Lisp. It may be violated in some circumstances, but most of the time lists will end
in NIL. When the list is written in parenthesis notation, the NIL at the end of the chain is
omitted, again by convention.
EMPTY LIST
A list of zero elements is called an empty list. It has no cons cells. It is written as an empty pair
of parentheses: (). Inside the computer the empty list is represented by the symbol NIL. This is a
tricky point: the symbol NIL is the empty list; that’s why it is used to mark the end of a cons cell
chain. Since NIL and the empty list are identical, we are always free to write NIL instead of (),
and vice versa. Thus (A NIL B) can also be written (A () B). It makes no difference which
printed form is used; inside the computer the two are the same.
6
CAR and CDR
By now you know that each half of a cons cell points to something. The two halves have obscure
names. The left half is called the CAR, and the right half is called the CDR (pronounced ‘‘cou-
der,’’ rhymes with ‘‘good-er’’). These names are relics from the early days of computing, when
Lisp first ran on a machine called the IBM 704. The 704 was so primitive it didn’t even have
transistors, it used vacuum tubes. Each of its ‘‘registers’’ was divided into several components,
two of which were the address portion and the decrement portion. Back then, the name CAR
stood for Contents of Address portion of Register, and CDR stood for Contents of Decrement
portion of Register. Even though these terms don’t apply to modern computer hardware,
Common Lisp still uses the acronyms CAR and CDR when referring to cons cells. Besides
naming the two halves of a cons cell, CAR and CDR are also the names of built-in Lisp
functions that return whatever pointer is in the CAR or CDR half of the cell, respectively.
Consider again the list (THE BIG BOPPER). When this list is used as input to a function such as
CAR, what the function actually receives is not the list itself, but rather a pointer to the first cons
cell:
7
CDR follows the pointer to get to the cons cell, and extracts the pointer sitting in the CDR half,
which it returns. So, the result of CDR is a pointer to the list (BIG BOPPER). From this example
you can see that CAR is the same as FIRST, and CDR is the same as REST. Lisp programmers
usually prefer to express it the other way around: FIRST returns the CAR of a list, and REST
returns the CDR.
FUNCTIONS
The bulk of Lisp itself consists of functions. All the built-in data types are defined purely in
terms of what functions operate on them. Even Lisp's powerful object system is built upon a
conceptual extension to functions, generic functions.
MACROS
A macro is defined much like a function, but instead of telling how to compute a value, it tells
how to compute another Lisp expression which will in turn compute the value. Macros can do
this because they operate on the unevaluated expressions for the arguments, not on the argument
values as functions do. They can therefore construct an expansion containing these argument
expressions or parts of them. A Lisp macro-object is a list whose CAR is macro, and whose CDR
8
is a function. Suppose you want a version of SETQ that sets two variables to the same value. So,
if you write
(setq2 x y (+ z 3))
when z=8 then both x and y are set to 11.
IN-BUILT FUNCTIONS
EVAL and APPLY Functions
The EVAL function is the heart of Lisp. EVAL’s job is to evaluate Lisp expressions to compute
their result. Most expressions consist of a function followed by a set of inputs. If we give EVAL
the expression (+ 2 3), for example, it will invoke the built-in function + on the inputs 2 and 3,
and + will return 5. We therefore say the expression (+ 2 3) evaluates to 5.
Part of Eval’s job is actually handled by the built-in function APPLY. The function APPLY
takes two arguments (and in some implementations, a third), and the form of a call is (APPLY
function arglist). When this is evaluated, the function which is the value of function is
applied to the arguments that are the elements of the list arglist. For example, the following form
produces the value 5.
(APPLY ’DIFFERENCE ’(12 7))
Here are some more examples of expressions in EVAL notation:
(+ 1 6) ⇒ 7
(oddp (+ 1 6)) ⇒ t
(* 3 (+ 1 6)) ⇒ 21
(/ (* 2 11) (+ 1 6)) ⇒ 22/7
9
Boolean Functions or Logical Predicates
Boolean functions are functions whose inputs and outputs are truth values, meaning T
(representing true) or NIL (representing false). Defining a two-input LOGICAL-AND function:
(defun logical-and (x y)
(and x y t))
ITERATIVE APPLICATION OF FUNCTIONS (MAPCAR)
Sometimes one wants to apply a single function to each of the several elements in a list. LISP
provides a special form “MAPCAR” to accomplish this. For example, the form (ADD1 5)
which returns 6 can be expanded upon with MAPCAR to yield a form such as
(MAPCAR (FUNCTION ADD1) ’(5, 10 7 -2 100))
Which returns as its value the list (6 11 8 -1 101).
> (mapcar ’evenp ’(0 1 2 3 4 5 6 7 8 9))
(t nil t nil t nil t nil t nil)
>(mapcar ’times ’(1 2 3 4) ’(5 6 7 8))
(5 12 21 32)
> (mapcar ’listp ’(1 2 3 (1 2) a (a b) () nil ))
(nil nil nil t nil t t t)
STRINGS
Most of Lisp is an s-expression, but a string is a different data type surrounded by doublequotes.
> (setf sentence ’’Most of Lisp is an s-expression, but a string
is a different data type surrounded by double-quotes.’’)
> sentence
10
CONTROL STRUCTURES
Lisp originally had very few control structures, but many more were added during the language's
evolution. (Lisp's original conditional operator, cond, is the precursor to later if-then-else
structures).
Lisp syntax lends itself naturally to recursion. Mathematical problems such as the enumeration of
recursively defined sets are simple to express in this notation. For example, to evaluate a
number's factorial:
(defun factorial (n)
(if (zerop n) 1
(* n (factorial (1- n)))))
Conditionals (COND)
A cond form (or “Conditional form”) takes one or more arguments, representing the various
options. Each argument should be of the form:
(<test for true> <action-1> … <action-N>)
When cond finds a test that evaluates to something which is non-nil, it takes the actions after it
and then leaves the cond. Test and actions are of course just s-expressions. It is often useful to
make the last test of cond an trivially true default. cond returns the value of the last action it took,
otherwise NIL.
(defun agegroup (age)
(cond ((numberp age)
(cond ((< age 0) ’unborn)
((< age 3) ’baby)
((< age 14) ’child)
((<age 110) ’adult)
( t ’Probably_deceased)))
( t ’Error_in_usage))
)
11
agegroup
>(agegroup 12)
child
>
QUOTES and SETQ
A special functional form is QUOTE. The QUOTE of something evaluates to itself. Another
special form is SETQ, used for saving a value by associating it with an atom. QUOTE is used to
suppress the evaluation of an S-expression in a place in which it would otherwise be evaluated.
(PLUS 1 2) produces 3
(QUOTE (plus 1 2)) produces (plus 1 2)
(QUOTE A) produces A
(CAR (cons 1 2)) produces 1
(CAR (quote(cons 1 2))) produces cons
’(PLUS(1 2)) produces (plus(1 2))
For the example (PLUS 1 2), if it is desired to have the system remember the value, a form
such as the following could be used.
SETQ X (PLUS 1 2) or SET ’X (PLUS 1 2)
This form first evaluates the PLUS expression and then assigns it to the value to the atom X.
PREDICATES
Predicates are the functions that return values normally interpreted as Boolean truth values.
Examples:
(LESSP 3 5) returns T
(LESSP 3 3) returns NIL
(GREATERP 3 5) returns NIL
(LISTP ’(a b c)) returns T
12
(numberp 6) returns T
There are also other predicates like zerop, oddp, evenp.
Primitives LENGTH, REVERSE, and LAST
The following primitive are all self-explanatory.
>(length ’(Bitter Stout)) ; length’s argument must be list
2
>(length ())
0
>(reverse ’(SHOT A DUCK!))
(DUCK! A SHOT)
>(reverse ’((a b) (l e)))
((l e) (a b))
>(last ’( (apple) carrot banana (rice pudding)))
((rice pudding)); makes a list containing last element
PROG1, PROGN
These allow the sequential execution of several expressions, and are handy in certain
circumstances.
>(prog1 (setf a ’x) (setf b ’y) (setf a ’z)) ; returns
x ; value of 1st
>(progn (setf a ’x) (setf b ’y) (setf a ’z)) ; returns
z ; value of last
READ and PRINT
A function may accept data directly from the user or from a data channel connected through the
operating system. In order to obtain data this way, the function READ is used. READ takes no
arguments. When it is called, evaluation is suspended until the user types in a syntactically valid
13
S-expression at the console. When the input operation is complete, the value typed in is returned
as the value of READ. For example, we type:
(CONS (READ) ’(TWO THREE))
ONE
And get the result (ONE TWO THREE)
The results of a computation are commonly printed on the screen because LISP automatically
prints the result of any top-level evaluation. However, any available S-expression can be printed
at almost any point within a function using PRINT. PRINT takes one argument and prints the
value of the argument on a new line on the screen or printer. For example:
(MAPCAR (FUNCTION PRINT) ’(A B (C.D)))
Which produces the output:
A
B
C.D
And then the value of print is returned (it returns T or NIL).
An important variation of PRINT is PRIN1 which is like PRINT but does not go to a new line.
USER-DEFINED FUNCTION
Define Function (DEFUN)
DEFUN is a special kind of function, called a macro function, that does not evaluate its
arguments. Therefore, they do not have to be quoted. DEFUN is used to define other functions.
The first input to DEFUN is the name of the function being defined. The second input is the
argument list. It specifies the names the function will use to refer to its arguments. The remaining
inputs to DEFUN define the body of the function: what goes on ‘‘inside the box.’’ By the way,
DEFUN stands for define function.
Normally functions are defined using the DEFUN macro. The basic skeleton of a DEFUN looks
like this:
14
(DEFUN name (parameter*)
"Optional documentation string."
body-form*)
Example 1:
(DEFUN SQUARE(x)
(TIMES x x))
Example 2:
The average function is defined in EVAL notation this way:
(defun average (x y)
(/ (+ x y) 2.0))
Example 3:
(defun distance (x1 y1 x2 y2)
(sqrt ; Start of the body
(+ (* (- x1 x2) (- x1 x2))
(* (- y1 y2) (- y1 y2))
)
)
distance
>(distance 1 1 2 2 )
1.414213562373095
RECURSIVE FUNCTIONS
Any recursive procedure should do a little ‘work’, including checking whether it should
terminate,
then apply to procedure to something simpler. A classic recursive routine is to compute
factorials:
15
(defun factorial (m)
(cond
((zerop m) 1)
( t (* m (factorial (- m 1))))))
DEBUGGING
Lisp systems usually provides two kinds of debugging aids:
Trace capability for viewing function calls. Trace is a special form which takes any number
of function names as (unevaluated) arguments. It causes those functions to be marked for
automatic tracing whenever they are called. For example, (TRACE LENGTH) marks the
LENGTH function so that each time it is called, the name of the function and the particular
values of the arguments in the call will be displayed.
Break capability for examining the values of variables at selected breakpoints during the
execution.
GARBAGE COLLECTOR
Cells are continually taken off the free-list to make the list structure to represent results of
function evaluations. Many of these results are temporary and of no use after a brief moment. All
LISP systems have a garbage collector facility which causes the cells no longer in active use to
be identified and linked back into the free-list for reallocation.
CONCLUSION
LISP is one of the most ancient programming languages that is still widely used. It was created
to make manipulating data strings simple. Lisp is used for problems with rapid prototyping
needs. As one of the earliest programming languages, Lisp pioneered many ideas in computer
science, including tree data structures, automatic storage management, dynamic typing, and the
self-hosting compiler. Lisp is not only a widely used programming language but is also a family
of languages.
16
REFERENCES
Berkeley, E.C. and Bobrow, D.G. (1964). The Programming language LISP: Its Operation and
Applications (eds). Cambridge, MA: Information International, Inc.
Reilly, D. (2003). Milestones in computer science and information technology. Greenwood
Publishing Group. pp. 156–157. ISBN 978-1-57356-521-9.
Steele, G. L. (1990). Common Lisp: the language (2nd ed.). Bedford, MA: Digital Press. ISBN
1-55558-041-6. OCLC 20631879.
Steele, G. L. and Sussman, G. J. (1978). The Art of the Interpreter, or the Modularity Complex
(Parts Zero, One, and Two), Part Zero, P. 4. MIT Libraries. hdl:1721.1/6094. Retrieved
2020-08-01.
Robin, J.,Clive, M. and Stewart, I. (2012). The Art of Lisp Programming. Springer Science &
Business Media. p. 2. ISBN 9781447117193.
17