You think you know when you learn, are more sure when you can write, even more when you can teach, but certain when you can program.
-Alan Perlis Yale University computer scientist
This book covers a number of typical AI problems, showing how each problem can be broken down into manageable pieces, and also how each piece can be described in the programming language Common Lisp. Ideally, readers will learn enough through studying these examples to attack new AI problems with style, grace, and success.
Install
sudo apt install clisp
Basic
Lisp types > to indicate it is ready to accept the next computation. So we are faced with a screen that looks like this:
>
> (+ 2 2) 4 >
Symbolic Computation
Here’s an example of a computation on lists:
> (append '(Pat Kim) '(Robin Sandy)) => (PAT KIM ROBIN SANDY)
> 'John => JOHN
> '(John Q Public) => (JOHN Q PUBLIC)
> '2 => 2
> 2 => 2
> '(+ 22) => (+22)
> (+22) 4
> John => *Error: JOHN is not a bound variable*
> (John Q Public) => *Error: JOHN is not a function*
Above, there are four important points to make about symbols:
Lisp does not attach any external significance to the objects it manipulates.
Common Lisp provides over 700 built-in functions, like append, length, + …
Common Lisp are not case sensitive.
A wide variety of characters are allowed in symbols: numbers, letters, and other punctuation marks like + or !
Variable
One way to give a value to a variable is with setf:
> (setf p '(John Q Public)) => (JOHN Q PUBLIC) > p => (JOHN Q PUBLIC) > (setf x 10) => 10 > (+ x x) => 20 > (+ x (length p)) => 13
Special Forms
setf is called a special form because it does something special: if it did not exist, it would be impossible to write a function that assigns a value to a variable. The philosophy of Lisp is to provide a small number of special forms to do the things that could not otherwise be done, and then to expect the user to write everthing else as functions.
(defun first-name (name) "Select the first name from a name represented as a list." (first name))
> p => (JOHN Q PUBLIC)`
> (first-name p) => JOHN`
> (first-name '(Wilma Flintstone)) => WILMA`
> (setf names '((John Q Public) (Malcolm X) (Admiral Grace Murray Hopper) (Spot) (Aristotle) (A A Milne) (Z Z Top) (Sir Larry Olivier) (Miss Scarlet))) =>
((JOHN Q PUBLIC) (MALCOLM X) (ADMIRAL GRACE MURRAY HOPPER) (SPOT) (ARISTOTLE) (A A MILNE) (Z Z TOP) (SIR LARRY OLIVIER) (MISS SCARLET))
> (first-name (first names)) => JOHN
Using Functions
mapcar :
> (mapcar #'last-name names) (PUBLIC X HOPPER SPOT ARISTOTLE MILNE TOP OLIVIER SCARLET)
> (mapcar #'first-name names) (JOHN MALCOLM ADMIRAL SPOT ARISTOTLE A Z SIR MISS)
> (mapcar #'- '(1234)) => (-1-2-3-4)
> (mapcar #'+ '(1234) '(10203040)) => (11223344)
member :
(defparameter *titles* '(Mr Mrs Miss Ms Sir Madam Dr Admiral Major General) "A list of titles that can appear at the start of a name.")
(defun first-name (name) "Select the first name from a name represented as a list." (if (member (first name) *titles*) (first-name (rest name)) (first name)))
> (mapcar #'first-name names) (JOHN MALCOLM GRACE SPOT ARISTOTLE A Z LARRY SCARLET)
> (first-name '(Madam Major General Paula Jones)) PAULA
We can see how this works by tracing the execution of first-name, and seeing the values passed to and returned from the function. The special forms trace and untrace are used for this purpose.
> (trace first-name) (FIRST-NAME)
> (first-name '(John Q Public)) (1 ENTER FIRST-NAME: (JOHN Q PUBLIC)) (1 EXIT FIRST-NAME: JOHN) JOHN
> (first-name '(Madam Major General Paula Jones)) => (1 ENTER FIRST-NAME: (MADAM MAJOR GENERAL PAULA JONES)) (2 ENTER FIRST-NAME: (MAJOR GENERAL PAULA JONES)) (3 ENTER FIRST-NAME: (GENERAL PAULA JONES)) (4 ENTER FIRST-NAME: (PAULA JONES)) (4 EXIT FIRST-NAME: PAULA) (3 EXIT FIRST-NAME: PAULA) (2 EXIT FIRST-NAME: PAULA) (1 EXIT FIRST-NAME: PAULA) PAULA
> (untrace first-name) => (FIRST-NAME)
> (first-name '(Mr Blue Jeans)) => BLUE
Higher-Order Functions
A function that takes another function as an argument is called a higher-order function. mapcar is an example.
We will define a new function called mappend:
(defun mappend (fn the-list) "Apply fn to each element of list and append the results." (apply #'append (mapcar fn the-list)))
> (apply #'+ '(1234)) => 10
> (apply #'append '((123) (a b c))) => (123 A B C)
Now we define a new function, self-and-double, and apply it to a variety of arguments:
funcall is similar to apply; it too takes a function as its first argument and applies the function to a list of arguments, but in the case of funcall, the arguments are listed separately:
(defun mappend (fn the-list) "Apply fn to each element of list and append the results." (if (null the-list) nil (append (funcall fn (first the-list)) (mappend fn (rest the-list)))))
> (funcall #'+ 23) => 5
> (apply #' + '(23)) => 5
> (funcall #' + '(23)) => *Error: (23) is not a number.*
lambda :
In general, the form of a lambda expression is:
(lambda (*parameters...*) *body...*)
> ((lambda (x) (+ x 2)) 4) => 6
> (funcall #'(lambda (x) (+ x 2)) 4) => 6
> append => *Error: APPEND is not a bound variable*
> (lambda (x) (+ x 2)) => *Error: LAMBDA is not a function*
>(mapcar #'(lambda (x) (+ x x)) '(12345)) => (246810)
> (mappend #'(lambda (l) (list l (reverse l))) ((123) (a b c))) => ((123) (321) (A B C) (C B A))
Other Data Types
So far we have seen just four kinds of Lisp objects: numbers, symbols, lists, and functions. Lisp actually defines about 25 different types of objects: vectors, arrays, structures, characters, streams, hash tables, and others. At this point we will introduce one more, the string. As you can see in the following, strings, like numbers, evaluate to themselves. Strings are used mainly for printing out messages, while symbols are used for their relationships to other objects, and to name variables. The printed representation of a string has a double quote mark "" at each end.
> "a string" => "a string"
> (length"a string") => 8
> (length"") => 0
Summary: The Lisp Evaluation Rule
We can now summarize the evaluation rule for Lisp.
Every expression is either a list or an atom.
Every list to be evaluated is either a special form expression or a function application.
A special form expression is defined to be a list whose first element is a special form operator.
A function application is evaluated by first evaluating the arguments (the rest of the list) and then finding the function named by the first element of the list and applying it to the list of evaluated arguments.
Every atom is either a symbol or a nonsymbol.
A symbol evaluates to the most recent value that has been assigned to the variable named by that symbol.
A nonsymbol atom evaluates to itself.
What Makes Lisp Different?
What is it that sets Lisp apart from other languages? Why is it a good language for AI applications? There are at least eight important factors: