Documente Academic
Documente Profesional
Documente Cultură
12
Why Lisp?
First, Lisp is much more
exible than most languages. Users
have such total control over what goes on that if they do
not like the syntax of the language, they may change it to
suit themselves. Suppose that you do not like the method
for dening programs in, say, Fortran, can you do anything
about it? Short of switching to another language the answer
is clearly no. Indeed the very idea seems absurd. But not to
Lisp users. (Spector, 1995, PC.)
...The second reason for choosing lisp is the way in which
Lisp in oriented toward the manipulation of symbols as op-
posed to, say, numbers.Norvig, (PoAIP, p. 25):
What is it that sets Lisp apart from other languages? Why
is it a good language for AI applications? There are at least
seven important factors (from Norvig):
Built-in Support for Lists
Automatic Storage Management
Dynamic Typing
First-Class Functions
Uniform Syntax
Interactive Environment
Extensibility
13
Characteristics of Lisp
14
Some dierences between Lisp and
Conventional Languages
Symbolic Processing
15
Syntax of Lisp
Lisp is based on symbolic expressions or S-expressions:
| An atom is an S-expression: (a) symbol (b) non-symbol (num-
ber, string, array);
Examples: X, JOHN, SETQ, 7.2, "John"
| A list is an S-expression: (S-expr S-expr ); :::
Note: A list is a special case of a dotted pair where the last cell = NIL
16
Basic Idea of Using Lisp
Sit in front of terminal
Enter Lisp (interactive)
Now you're in a read-eval-print loop
EVAL = give me an expression to evaluate; returns a
value for an s-expr. (analogy: words in sentences vs.
meaning)
Lisp reads the S-expr, evaluates the S-expr, then prints
the result of the evaluation.
17
Atoms and Lists
Atoms:
atom
turkey
1492
3turkeys
*abc
Lists:
(atom)
(atom turkey)
(atom 1492 ocean blue)
(an-atom (a list inside) and more atoms)
(a list containing 5 atoms)
(a list containing 8 atoms and a list (that contains 4 atoms))
(((a list containing a list containing a list of 11 atoms)))
()
(a list that ends with the null list ())
(not a list
(not) a list
)not again
18
Result of Evaluation: What is the value?
Number ! number itself (e.g., 7 ! 7)
String ! string itself (e.g., "hi" ! "hi")
Symbol ! value of symbol (e.g., X ! 5, if X has that
value)
| Symbols like variables in most langs, have a value associated
with them. Special: T, NIL
List ! normally function evaluation
| Normally write fcn as f(a,b, ,n) or inx a+b+ +n
::: :::
20
Sample Session
Fire up Lisp ...
(+ 1 1)
=> 2
(* 23 13)
=> 299
(+ 1 (* 23 13))
=> 300
(+ (* 2 2) (- 10 9))
=> 5
(* 2 2 2 2)
=> 16
(sqrt (* 2 2 2 2))
=> 4
(/ 25 5)
=> 5
(/ 38 4)
=> 19/2
"Hello, world!"
=> "Hello, world!"
'x
=> X
21
Sample Session (continued)
(setf x 5) ; Setf gives a value to a variable. It has a side
=> 5 ; effect and ALSO returns a value
(length l)
=> 5 ; Note that the length of l has not changed.
;;; Note: "list" puts elts into a list; doesn't merge them like "append" does.
;;; Note: Parens MUST match --> source of bizarre errors if not.
(car l) ; (first l)
=> A
(cdr l) ; (rest l)
=> (B C D E)
22
Understanding LISP: Lisp Data Structures
Best way to understand Lisp: Develop an intuitive un-
derstanding of its data structures (remember: pgms and
data stored the same way).
First step: forget conventional pgming langs w/ which
you are familiar (else confusion).
Three notations for Lisp data structures: (1) paren/list
(printed) (2) dot (printed only if req; list notation is
abbrev. for this); (3) pictorial or graphic or box/cell
notation (never printed; shows what's going on inside).
We will return to relate these three.
Note: parenthesized notation = surface appearance (typ-
ing in/out at terminal) whereas pictorial notation =
what's happening below the surface (i.e., how the com-
puter is really doing it).
Need to be able to switch back and forth between these
two easily.
24
Pictorial Notation: used to describe what goes on in
Memory
CONS Cell: A cell in memory (w/ left and right halves) Note: combinations of
CAR/CDR (up to 4), e.g.,
CADR = (CAR (CDR ..))
nil nil
A B A B A B
(CONS ’A ’B) (LIST ’A ’B) (APPEND ’(A) ’(B))
Adds one mem cell Adds one mem cell All args must be lists!
for each elt Last ptr of each elt
(A . B) (A B) points to next elt
Note: can have Note: can have
(A B)
(CONS ’A NIL)-> (A) (LIST ’A) -> (A)
same
Suppose:
X = (A)
nil nil
Y = (B)
What is (CONS X Y)?
B
What is (LIST X Y)?
25
Quote (continued from last time)
The quote function inhibits the evaluation of its argument.
It returns its argument literally. If the variable barney has
the value 22, then:
Evaluating
barney
yields
22
Evaluating
(quote barney)
yields
barney
Evaluating
(car (quote (betty bambam)))
yields
betty
2
Taking lists apart (continued from last time)
The rst element of a list (be it an atom or another list) is
the car of the list.
(car '(alphabet soup))
=> alphabet
(car '((pickles beer) alphabet (soup)))
=>(pickles beer)
(car '(((miro (braque picasso))) leger))
=>((miro (braque picasso)))
3
Taking lists apart (continued)
rst is a synonym for car
second is a synonym for cadr
third is a synonym for caddr .......
rest is a synonym for cdr
(setf x '(eenie meenie minie moe))
=> (EENIE MEENIE MINIE MOE)
(first x)
=> EENIE
(third x)
=> MINIE
(rest x)
=> (MEENIE MINIE MOE)
4
Putting lists together
cons builds lists.
(cons 'woof '(bow wow))
=> (woof bow wow)
(cons '(howl) '(at the moon))
=> ((howl) at the moon)
5
Atoms and Dotted Pairs (continued from last time)
You get errors if you try to take the car or cdr of a non-nil
atom
(car 'foo)
=>
> Error: Can't take CAR of FOO.
(cdr 'foo)
=>
> Error: Can't take CDR of FOO.
You get \funny dots" if you try to cons things onto non-lists
(cons 'a 'b)
=> (A . B)
6
Generalized assignment (setf)
Earlier dialects: not available (used something called
setq)
setq assigns a value to a symbol
(setq my-favorite-list '(fe fi fo fum))
=> (fe fi fo fum)
my-favorite-list
=> (fe fi fo fum)
3 nil
A 3 "A has the value 3"
A A
7
More about setting variables
Lisp doesn't need type declaration because everything
is a pointer (i.e., the values of symbols have types as-
sociated w/ them; atoms, lists, integers, etc.)
Typical memory org:
Lisp system
Cons nodes
Strings
Integers
Symbols
Package
8
Eval
EVAL = explicit evaluation.
| (+ 3 5) ! evaluated w/o user explicitly asking for eval'n
| EVAL is applied by the system (LISP = read-eval-print loop)
| Explicit eval = extra evaluation
| Sort of the opposite of quote.
Examples: Suppose (setf x 'y), (setf y 'z)
|x!y
| (eval x) ! z
| (eval 'x) ! y
What is eval useful for?
{ retrieving value of symbol whose name is not known until run-
time:
(setf a 3)
(eval 'a) ! 3
NOT REALLY A GOOD IDEA: Can use symbol-value; no rea-
son to use eval.
{ evaluating form that is constructed at runtime:
(eval (list <fn> args ...))
(eval (list 'setf arg1 arg2)) (where arg1='x and arg2=3)
evaluates (setf x 3)
Note: basically building a (trivial) function and running it on
the
y; in the \old" days, this was considered the \key" to
Lisp's
exibility
NOT REALLY A GOOD IDEA: Use symbol-value again:
(setf (symbol-value 'arg1) (symbol-value 'arg2))
But might be cases where don't have enough info; if the list '(setf
x 3) is passed into a fn to be evaluated, then probably best thing
to do is eval: (1) can't use funcall/apply cause setf is macro; (2)
don't know enough about the args w/o extensive testing to be able
to use (or in this case not use) symbol-value.
But in general, we no longer need eval, really. Some people (e.g.,
Norvig) say you're doing something wrong if you use it. But you
should know it is there, and that sometimes there's no choice.
9
Comments
Common Lisp provides 2 ways to comment your code:
#| this
could
be a
really big comment |#
(car '(a b c)) ; this is a comment
=> A
10
Commonlisp Forms
Function: (+ 3 5)
Can test with (functionp x)
Note: Arguments to a function can themselves be a
function (nesting): (+ (* 3 5) 5)
Macro: (setf x 1)
Can test with (macro-function x)
Note: violates eval'n rule: don't eval x, only eval 1.
Special form: (if test a b)
Can test with (special-form x)
Two others we'll learn about later: let, let*
We'll initially concentrate on this rst type of form (the
function).
We'll talk about macros in detail later.
11
Predicates
12
Predicates (continued)
ical condition.
3. (atom x), (null x), (not x), (listp x), (consp x),
(numberp x), (stringp x), (arrayp x), (vectorp
x), (characterp x), (member x y :test <test>)
Note: All conses are lists, but converse not true: (consp nil) ! nil
Note: Member doesn't just return t or nil!
13
Sample Session: Predicates
(null 'fig-tree)
=> NIL
(null '(bismark))
=> NIL
(null '())
=> T
(null ())
=> T
(null nil)
=> T
(atom 'eggplant)
=> T
(atom 99)
=> T
(atom 3.141592)
=> T
(atom '(floating point number))
=> NIL
(atom nil)
=> T
(atom ())
=> T
(listp 'apple)
=> NIL
(listp '(fish and telephones))
=> T
(listp 33)
=> NIL
(listp nil)
=> T
(setq lucky-number 23)
=> 23
(evenp lucky-number)
=> NIL
(not (evenp lucky-number))
=> T
14
Sample Session: Predicates Taking More Than One
Argument
(> 22 11)
=> T
(> 11 22)
=> NIL
(<= 23 23)
=> T
(>= 23 23)
=> T
(>= 100 1)
=> T
(>= 1 100)
=> NIL
(< 1 2 3 4 5 6 7 8 9)
=> T
(< 1 2 3 4 5 6 7 8 7)
=> NIL
15
Equality
Common lisp has a variety of Equality Predicates, of which
equalp is the most general (see CLtL2 p. 103).
And, or, and not allow the combination of predicates into
complex predicates.
EQ and EQUAL { two most commonly used.
EQL: EQ or =
nil nil
A B A B
EQ
A
17
Sample Session: Equality
(equalp 'foot 'foot)
=> T
(equalp 'nose 'ear)
=> NIL
(equalp (+ 22 33 44) (* 33 3))
=> T
18
Conditionals
IF: special form (if (= x 21) (print 'blackjack))
WHEN: macro; has same form: (when (= x 21) (print 'blackjack))
Dierence: IF has else part; should use if only when there is an
else part ! (if z 'not-empty 'empty) [z is a boolean]
| IF: most useful for 2-way fork where then/else are each 1 expr
| WHEN: used for 1-way fork (then may be more than 1 expr)
What if more than 2-way fork? Use COND!
(cond (C1 ( E11 E21...))
(if C E1 E2) (C2 (E21 E22 ...)) ...
(Cn (En1 En2 ...)))
C Y E1
C1 Y E11 E12...
N
E2 N
C Y E1 E2...
Cn Y En1 En2...
19
Conditionals: Examples
(cond (x 'b) (y 'c) (t 'd))
What if x = 'a? (then returns b)
What if x = nil, y = t? (then returns c)
What if x = nil y = nil? (then returns d)
(cond (x (setf x 1) (+ x 2))
(y (setf y 2) (+ y 2))
(t (setf x 0) (setf y 0)))
What if x = t? (then returns 3) What is x? (x = 1)
What if x = nil, y = t? (then returns 4) What are x/y? (nil/2)
What if x = nil y = nil? (then returns 0) What are x/y? (0/0
Note: could also do the following:
(cond (x (setf x 1) (+ x 2)) ((setf y 2)))
If x is nil, then it'll execute the last statement and return it.
Other conditionals (chapter 3):
(case (expression (match1 result11 result12 ...)
(match2 result21 result22 ...) ...
(matchn resultn1 resultn2 ...)))
Note: use \otherwise" as last match expression.
Can also use OR and AND as conditional control con-
structs (as we talked about earlier): (and nil t t t), (or
t nil nil nil)
20
IF vs. COND
The IF special form is a special case of COND: IF testform
thenform [elseform]
Evaluates testform. If the result is true, evaluates thenform
and returns the result; if the result is nil, evaluates elseform
and returns the result.
(if (> 100 23) 'sure-is 'sure-aint)
=> SURE-IS
(if (member 'bog '(blig blog bog bumper))
(* 99 99)
(sqrt -1))
=> 9801
(if (member 'fog '(blig blog bog bumper))
(* 99 99)
(sqrt -1))
=> #c(0 1)
Note that the thenform and the elseform are both restricted
to being single forms. In contrast, you can specify any num-
ber of forms in a cond clause:
(setq temperature 8)
=> 8
(cond ((< temperature 32)
(print '(yowch -- it is cold!))
(print '(i will play god and
change that!))
(setq temperature 78))
(t
(print '(well i guess it is
not so bad))
(print '(where do you think
we are? hawaii?))))
(YOWCH -- IT IS COLD!)
(I WILL PLAY GOD AND CHANGE THAT!)
=> 78
21
PROGN
If you want multiple forms in an IF you can use a PROGN:
Evaluates each form in order, left to right. The values of all
forms but the last are discarded; the value of the last form
is returned.
(if (< temperature 32)
(progn (print '(yowch -- it is cold!))
(print '(i will play god and
change that!))
(setq temperature 78))
(progn (print '(well i guess it is
not so bad))
(print '(where do you think
we are? hawaii?))))
(WELL I GUESS IT IS NOT SO BAD)
(WHERE DO YOU THINK WE ARE? HAWAII?)
=> (WHERE DO YOU THINK WE ARE? HAWAII?)
22
Sample Session: COND
(setq x 23)
=> 23
(setq x 2)
=> 2
23
Dening Functions
Dene Function = \Defun"
(defun function-name (parameter ...) function-body)
Function name = symbol; parameters usually symbols.
(defun rst-name (name) (rst name))
(rst-name '(john q public)) ! JOHN
24
Sample Session: Defun
Here's a function that takes one argument and returns the
argument plus 20:
(defun add-20 (n)
"returns n + 20"
(+ n 20))
=> ADD-20
(add-20 15)
=> 35
(describe 'add-20)
=> ADD-20 is a symbol. Its home package is USER.
Its global function definition is #<Interpreted-Function ADD-20 FB0C26> ...
Its source code is (NAMED-LAMBDA ADD-20 (N) (BLOCK ADD-20 (+ N 20))) ...
It has this function documentation: "returns n + 20" ...
25
Sample Session: Defun (continued)
Here's a constant function that takes no arguments
(defun dumb-function ()
'(you might as well use a variable for something like this))
=> DUMB-FUNCTION
(dumb-function)
=> (YOU MIGHT AS WELL USE A VARIABLE FOR SOMETHING LIKE THIS)
(even-or-odd? 24)
=> EVEN
(even-or-odd? 25)
=> ODD
(even-or-odd? '(swahili))
=> NEITHER
26
Sample Session: Defun (continued)
Here's a function that takes two arguments and returns their
sum:
(defun my-sum (n1 n2)
"silly substitute for +"
(+ n1 n2))
=> MY-SUM
(my-sum 99 100)
=> 199
27
Sample Session: Defun (continued)
Here's a cleaner version of funny-arg-lister using the LIST
function:
(defun funny-arg-lister (arg1 arg2 arg3)
(list (list 'arg1 arg1)
(list 'arg2 arg2)
(list 'arg3 arg3)))
=> FUNNY-ARG-LISTER
(funny-arg-lister 'a 'b '(x y z))
=> ((ARG1 A) (ARG2 B) (ARG3 (X Y Z)))
(double-cons 1 nil)
=> (1 1)
(run shoes)
(I THINK I WILL TAKE A TROT IN MY NIKES)
(I THINK I WILL TAKE A TROT IN MY OLD BEAT-UP NIKES)
=> (I THINK I WILL TAKE A TROT IN MY OLD BEAT-UP NIKES)
(print 'foo)
=> FOO
(run shoes)
(I THINK I WILL TAKE A TROT IN MY NIKES)
(I THINK I WILL TAKE A TROT IN MY OLD BEAT-UP NIKES)
=> (I RAN)
shoes
=> NIKES
29
Sample Session: Operations on Lists
The NTH function returns an element of a list by number
(setq animals '(giraffe dog dinosaur bug big-bug big-hairy-bug))
=> (GIRAFFE DOG DINOSAUR BUG BIG-BUG BIG-HAIRY-BUG)
(nth 0 animals)
=> GIRAFFE
(nth 1 animals)
=> DOG
(nth 2 animals)
=> DINOSAUR
(nth 3 animals)
=> BUG
(nth 4 animals)
=> BIG-BUG
(nth 5 animals)
=> BIG-HAIRY-BUG
(nth 6 animals)
=> NIL
30
Sample Session: Operations on Lists (continued)
Here's a function to return a random element of a list
(defun random-elt (choices)
"returns one element of the list of choices at random"
(nth (random (length choices)) choices))
=> RANDOM-ELT
(random-elt animals)
=> BUG
(random-elt animals)
=> BIG-BUG
31
Programming Example
(Taken from Chapter 2 of Norvig, (PoAIP))
A Grammar for a (miniscule) Subset of English:
Sentence => Noun-Phrase + Verb-Phrase
Noun-Phrase => Article + Noun
Verb-Phrase => Verb + Noun-Phrase
Article => the, a, ...
Noun => man, ball, woman, table ...
Verb => hit, took, saw, liked ...
(defun noun-phrase ()
"generates and returns a noun-phrase as a list of atoms"
(append (article) (noun)))
=> NOUN-PHRASE
32
Programming Example (continued)
(defun verb-phrase ()
"generates and returns a verb-phrase as a list of atoms"
(append (verb) (noun-phrase)))
=> VERB-PHRASE
(defun article ()
"generates and returns an article as an atom in a list"
(one-of '(the a)))
=> ARTICLE
(defun noun ()
"generates and returns a noun as an atom in a list"
(one-of '(man ball woman table)))
=> NOUN
(defun verb ()
"generates and returns a noun as an atom in a list"
(one-of '(hit took saw liked)))
=> VERB
33
Programming Example (continued)
(sentence)
=> (A MAN TOOK THE WOMAN)
(sentence)
=> (THE MAN SAW A MAN)
(sentence)
=> (THE WOMAN TOOK A BALL)
(noun-phrase)
=> (THE WOMAN)
(verb-phrase)
=> (TOOK THE BALL)
(noun)
=> (BALL)
(verb)
=> (LIKED)
34
Using the Trace Facility
(trace sentence noun-phrase verb-phrase
article noun verb)
=> NIL
(sentence)
Calling (SENTENCE)
Calling (NOUN-PHRASE)
Calling (ARTICLE)
ARTICLE returned (THE)
Calling (NOUN)
NOUN returned (MAN)
NOUN-PHRASE returned (THE MAN)
Calling (VERB-PHRASE)
Calling (VERB)
VERB returned (HIT)
Calling (NOUN-PHRASE)
Calling (ARTICLE)
ARTICLE returned (THE)
Calling (NOUN)
NOUN returned (WOMAN)
NOUN-PHRASE returned (THE WOMAN)
VERB-PHRASE returned (HIT THE WOMAN)
SENTENCE returned (THE MAN HIT THE WOMAN)
=> (THE MAN HIT THE WOMAN)
(untrace)
=> (SENTENCE NOUN-PHRASE VERB-PHRASE ARTICLE NOUN VERB)
35
Recursion
Dene length in terms of itself: the empty list has length
0 and any other list has a length which is one more than
the length of the rest of the list.
(defun length (list)
(cond ((null list) 0) (BASE)
(t (1+ (length (cdr list)))))) (RECURSION: leap of faith)
Evaluation of recursive call:
| Evaluate the argument (cdr list)
| Bind it to list after old value is saved on stack.
| Upon return, add 1 to result.
Spiral of recursion: unwind when list = NIL
(length (a b c))
(length (b c))
(length (c))
(length ())
0
36
Tail Recursion
Note: compiler has to allocate memory for each recur-
sive call. But not true for all recursive calls!
In rst def of length: add 1 after returning from recur-
sive call.
More ecient to write a \tail-recursive" function: re-
cursive call appears as the last thing the function does
(the tail)
(defun length (list)
(length-aux list 0))
Auxiliary function: uses LEN-SO-FAR as an \accumu-
lator"
(defun length-aux (sublist len-so-far)
(cond ((null sublist) len-so-far)
(t (length-aux (rest sublist) (1+ len-so-far)))))
37
Sample Session: Recursion
Here's a recursive function called lat?, that takes one argu-
ment and returns t if that argument is a list of atoms.
(defun lat? (l)
"Returns t if the argument is a list of atoms, nil otherwise"
(cond ((null l) t)
((atom (car l)) (lat? (cdr l)))
(t nil)))
=> LAT?
(lat? 12)
=> NIL
38
Sample Session: Recursion (continued)
Here's a generalization of double-cons and quadruple-cons
we dened previously:
(defun multi-cons (one-thing another-thing n)
"Returns the result of consing one-thing onto another-thing n times"
(cond ((equalp n 0) another-thing)
(t (cons one-thing
(multi-cons one-thing
another-thing
(- n 1))))))
=> MULTI-CONS
(multi-cons 'hip '(hooray) 5)
=> (HIP HIP HIP HIP HIP HOORAY)
39
MEMBER
Recall that Common Lisp includes a function called MEM-
BER that does a similar thing. Note, however, that it returns
the matching cdr rather than t:
(member 'hampshire five-colleges)
=> (HAMPSHIRE SMITH MOUNT-HOLYOKE)
(member 'oberlin five-colleges)
=> NIL
40
A closer look at recursive programming:
Wilensky (Common Lispcraft) p. 89: ...we have the follow-
ing components to recursive programming:
Breaking down the task at hand into a form that in-
volves simpler versions of the same task.
Specifying a way to combine the simpler versions of the
task to solve the original problem.
Identifying the "grounding" situations in which the task
can be accomplished directly.
Specifying checks for these grounding cases that will be
examined before recursive steps are taken.
(defun factorial (n)
"returns the factorial of n"
(cond ((<= n 1) 1)
(t (* n (factorial (- n 1))))))
=> FACTORIAL
(factorial 5)
=> 120
(trace factorial)
=> NIL
(factorial 5)
Calling (FACTORIAL 5)
Calling (FACTORIAL 4)
Calling (FACTORIAL 3)
Calling (FACTORIAL 2)
Calling (FACTORIAL 1)
FACTORIAL returned 1
FACTORIAL returned 2
FACTORIAL returned 6
FACTORIAL returned 24
FACTORIAL returned 120
=> 120
41
Factorial and Length Revisited
Another version of Factorial:
(defun factorial (n)
"returns the factorial of n"
(cond ((zerop n) 1)
(t (* n (factorial (- n 1))))))
=> FACTORIAL
(factorial 5)
=> 120
42
Iteration: DOTIMES
Sometimes iteration seems more natural. Common Lisp pro-
vides many iteration constructs.
DOTIMES (var countform [resultform]) {declaration}* {tag | statement}*
[Macro]
43
Iteration: DOTIMES (continued)
Here was our recursive function multicons:
(defun multi-cons
(one-thing another-thing n)
"Returns the result of consing one-thing onto another-thing n times"
(cond ((equalp n 0) another-thing)
(t (cons one-thing
(multi-cons
one-thing
another-thing
(- n 1))))))
=> MULTI-CONS
(multi-cons 'hip '(hooray) 5)
=> (HIP HIP HIP HIP HIP HOORAY)
44
Iteration: DOLIST
DOLIST (var listform [resultform]) {declaration}* {tag | statement}*
[Macro]
Evaluates listform, which produces a list, and executes the body once
for every element in the list. On each iteration, var is bound to
successive elements of the list. Upon completion, resultform is
evaluated, and the value is returned. If resultform is omitted, the
result is nil.
45
#'
Funny #' notation (sharp-quote): maps name of func-
tion to function itself. Equivalent to FUNCTION.
Always use this when using mapcar, apply, funcall, lambda
expressions keywords (e.g., :TEST).
Note: only functions may be quoted (not macros or
special forms).
More ecient; means something to compiler: go to
compiled code, not through symbol to nd function
defn.
Analogous to quote. Use any time a function is speci-
ed.
Mapcar: Expects an n-ary fn as 2st arg, followed by
n lists. It applies the fn to the arg list obtained by
collecting the rst elt of each list.
| (mapcar #'1+ '(5 10 7)) ! (6 11 8)
| (mapcar (function 1+) '(5 10 7)) ! (6 11 8)
| (mapcar #'cons '(a b c) '(1 2 3)) ! ((A . 1) (B .
2) (C . 3))
| (mapcar #'print '(a b c)) ! (A B C)
Note: last case also has side eect of printing A, B, and
C.
Avoid consing up a whole new list by using Mapc:
| (mapc #'print '(a b c)) ! (A B C)
[This prints A, B, and C, but then returns the second
arg, NOT a new list.]
1
Apply, Funcall, Eval
Following forms equivalent:
| (+ 1 2 3 4)
| (funcall #'+ 1 2 3 4)
| (apply #'+ '(1 2 3 4))
| (apply #'+ 1 2 '(3 4))
| (eval '(+ 1 2 3 4))
Funcall: great to use if we don't know function name
in advance
| (funcall <fn> arg1 arg2 ...)
| Applies to arguments, not in a list.
| But what if we don't know the no. of args in advance?
Apply: same idea, but don't need to know no. of args
| (apply <fn> arg1 arg2 ... arglist)
| Applies to arguments; last arg MUST be a list
Eval: in general we can use funcall and apply.
2
Lambda expressions: apply, funcall
Lambda expressions specify a function without a name.
(Ah hah! so now we see how apply/funcall will be used.)
Lambda is most primitive way to specify fn; has its roots
in lambda calculus of Church
(lambda (parameters ...) body ...)
A lambda expr is a nonatomic name for a fn, just as
append is an atomic name for a built-in.
Use the #' notation.
Example:
(funcall #'(lambda (x) (+ x x)) 2) ! 4
(apply #'(lambda (x) (+ x x)) '(2)) ! 4 (wasteful)
(mapcar #'(lambda (x) (+ x x)) '(1 2 3 4 5)) ! '(2 4 6 8 10)
*** Programmers who are used to other langs some-
times fail to see the point of lambda expressions. Why
are they useful?
(1) It's a pain to think up names and to clutter up a
program with lots of functions that are only used very
locally;
(2) MORE IMPORTANT: can create new functions at
run time.
Note: cannot give a lambda expr to lisp to eval; \lambda"
not a function! But can use it as car of a list: ((lambda
(x) (+ x x)) 2) ! 4. (I don't use this though.)
Note: Defun is a macro that expands out to lambda;
so there's really only one way to specify a fn, not two!
(symbol-function <fn>) returns lambda expression.
3
IF vs. COND (slide 21 of previous lecture)
The IF special form is a special case of COND: IF testform
thenform [elseform]
Evaluates testform. If the result is true, evaluates thenform
and returns the result; if the result is nil, evaluates elseform
and returns the result.
(if (> 100 23) 'sure-is 'sure-aint)
=> SURE-IS
(if (member 'bog '(blig blog bog bumper))
(* 99 99)
(sqrt -1))
=> 9801
(if (member 'fog '(blig blog bog bumper))
(* 99 99)
(sqrt -1))
=> #c(0 1)
Note that the thenform and the elseform are both restricted
to being single forms. In contrast, you can specify any num-
ber of forms in a cond clause:
(setq temperature 8)
=> 8
(cond ((< temperature 32)
(print '(yowch -- it is cold!))
(print '(i will play god and
change that!))
(setq temperature 78))
(t
(print '(well i guess it is
not so bad))
(print '(where do you think
we are? hawaii?))))
(YOWCH -- IT IS COLD!)
(I WILL PLAY GOD AND CHANGE THAT!)
=> 78
2
PROGN
If you want multiple forms in an IF you can use a PROGN:
Evaluates each form in order, left to right. The values of all
forms but the last are discarded; the value of the last form
is returned.
(if (< temperature 32)
(progn (print '(yowch -- it is cold!))
(print '(i will play god and
change that!))
(setq temperature 78))
(progn (print '(well i guess it is
not so bad))
(print '(where do you think
we are? hawaii?))))
(WELL I GUESS IT IS NOT SO BAD)
(WHERE DO YOU THINK WE ARE? HAWAII?)
=> (WHERE DO YOU THINK WE ARE? HAWAII?)
3
Sample Session: COND
(setq x 23)
=> 23
(setq x 2)
=> 2
4
Sample Session: Operations on Lists (slide 30 of
previous lecture)
The NTH function returns an element of a list by number
(setq animals '(giraffe dog dinosaur bug big-bug big-hairy-bug))
=> (GIRAFFE DOG DINOSAUR BUG BIG-BUG BIG-HAIRY-BUG)
(nth 0 animals)
=> GIRAFFE
(nth 1 animals)
=> DOG
(nth 2 animals)
=> DINOSAUR
(nth 3 animals)
=> BUG
(nth 4 animals)
=> BIG-BUG
(nth 5 animals)
=> BIG-HAIRY-BUG
(nth 6 animals)
=> NIL
5
Sample Session: Operations on Lists (continued)
Here's a function to return a random element of a list
(defun random-elt (choices)
"returns one element of the list of choices at random"
(nth (random (length choices)) choices))
=> RANDOM-ELT
(random-elt animals)
=> BUG
(random-elt animals)
=> BIG-BUG
6
Sample Session: Recursion Revisited (slide 39 of
previous lecture)
Here's a recursive function called lat?, that takes one argu-
ment and returns t if that argument is a list of atoms.
(defun lat? (l)
"Returns t if the argument is a list of atoms, nil otherwise"
(cond ((null l) t)
((atom (car l)) (lat? (cdr l)))
(t nil)))
=> LAT?
(lat? 12)
=> NIL
7
Sample Session: Recursion (continued)
Recall the double-cons and quadruple-cons functions (de-
ned in previous lecture):
(defun double-cons (one-thing another-thing)
"Returns the result of consing one-thing onto another-thing twice"
(cons one-thing (cons one-thing another-thing)))
=> DOUBLE-CONS
8
MEMBER
The following member? function checks to see if its rst
argument occurs in its second:
(defun member? (a lat)
"Returns t if a occurs in lat, nil otherwise"
(cond ((null lat) nil)
(t (or (equalp (car lat) a)
(member? a (cdr lat))))))
=> MEMBER?
(setq five-colleges
'(amherst umass hampshire smith mount-holyoke))
=> (AMHERST UMASS HAMPSHIRE SMITH MOUNT-HOLYOKE)
(member? 'hampshire five-colleges)
=> T
(member? 'oberlin five-colleges)
=> NIL
9
A closer look at recursive programming (slide 41 of
previous lecture)
Wilensky (Common Lispcraft) p. 89: ...we have the follow-
ing components to recursive programming:
Breaking down the task at hand into a form that in-
volves simpler versions of the same task.
Specifying a way to combine the simpler versions of the
task to solve the original problem.
Identifying the "grounding" situations in which the task
can be accomplished directly.
Specifying checks for these grounding cases that will be
examined before recursive steps are taken.
(defun factorial (n)
"returns the factorial of n"
(cond ((<= n 1) 1)
(t (* n (factorial (- n 1))))))
=> FACTORIAL
(factorial 5)
=> 120
(trace factorial)
=> NIL
(factorial 5)
Calling (FACTORIAL 5)
Calling (FACTORIAL 4)
Calling (FACTORIAL 3)
Calling (FACTORIAL 2)
Calling (FACTORIAL 1)
FACTORIAL returned 1
FACTORIAL returned 2
FACTORIAL returned 6
FACTORIAL returned 24
FACTORIAL returned 120
=> 120
10
Factorial and Length Revisited (slide 42 of previous
lecture)
Another version of Factorial:
(defun factorial (n)
"returns the factorial of n"
(cond ((zerop n) 1)
(t (* n (factorial (- n 1))))))
=> FACTORIAL
(factorial 5)
=> 120
11
Iteration: DOTIMES (slide 43 of previous lecture)
Sometimes iteration seems more natural. Common Lisp pro-
vides many iteration constructs. We talked about DOLIST
last time. Another is DOTIMES.
DOTIMES (var countform [resultform]) {declaration}* {tag | statement}*
[Macro]
12
Iteration: DOTIMES (continued)
Here was our recursive function multicons:
(defun multi-cons
(one-thing another-thing n)
"Returns the result of consing one-thing onto another-thing n times"
(cond ((equalp n 0) another-thing)
(t (cons one-thing
(multi-cons
one-thing
another-thing
(- n 1))))))
=> MULTI-CONS
(multi-cons 'hip '(hooray) 5)
=> (HIP HIP HIP HIP HIP HOORAY)
13
Keyword arguments: remove
So what are all these keywords? Let's look at remove:
(remove 1 '(1 2 3 2 1 0 -1))
=> (2 3 2 0 -1)
(remove 1 '(1 2 3 2 1 0 -1) :key #'abs)
=> (2 3 2 0)
(remove 1 '(1 2 3 2 1 0 -1) :test #'<)
=> (1 1 0 -1)
(remove 1 '(1 2 3 2 1 0 -1) :start 4)
=> (1 2 3 2 0 -1)
14
Global Variables
Global, dynamically scoped variables can be created with
defvar or defparameter
DEFVAR variable-name &optional initial-value documentation
[Macro]
(defparameter *depth-limit* 10
"The maximum depth to which we will search")
16
Local Variables
The LET special form is used to create local variables.
(let ((foo 2) (bar 3))
(+ foo bar))
=> 5
17
Local Variables (continued)
Recall CMSC 330: \call-bys", activation recs, stacks.
Lisp uses lexical binding. (Used to be dynamic.) So
cannot access vars not declared locally (unless special
global), even if dened inside of calling fn. (In dynamic
scoping, value of var determined by value inside local
env OR that of most recent caller containing the var.)
Let is the most common way of introducing vars that
are not parameters of fns; resist temptation to use a
var w/o introducing it (can actually do this in Lisp).
Let introduces a new local variable and binds it to a
value. General form: (let ((var value) ...) body-containing-vars)
Equiv to: ((lambda (var ...) body-containing-vars) value)
Let used to initialize LOCALS; Setf used to initialize
GLOBALS (or defparameter, defconstant, defvar).
You need to know that symbols (name for var or fn)
are NOT vars (depends on context). The symbol value
does NOT change when fn is called w/ a particular
symbol as a local var:
(symbol-value 'x) returns global val of x
x returns global val of x
(symbol-value cannot access the value of a lexical variable)
Current activation record (containing lexical vars) pushed
on stack; x bound to particular value, but symbol-value
looks at global value, not value at top of the stack.
IMPORTANT: Try not to overload programs w/ glob-
als; use locals where possible.
18
Local Variables (continued)
Note: Let (and let*) take up memory; if don't need to
allocate at top of fn, then don't:
(defun foo (x)
(cond ((zerop x) 0)
(t (let ((y (1+ x))) (* y x)))))
(setf x 1)
=> 1
(let* ((x 5) (y (1+ x))) (print (+ x y)))
=> 11
11
(print x)
=> 1
1
19
Lexical Closures
What does it mean to create a new function?
Every time #' is evaluated, a fn is returned.
But in previous examples, it is always the same fn that
is returned (e.g., the case given above for doubling ar-
guments).
Lexical closures are fns in which free variables have val-
ues from environment in which closure is PERFORMED,
not APPLIED.
| Evaluating a #' form PERFORMS a closure.
| Using the result of this evaluation APPLIES the clo-
sure.
A lexical closure is a box we can pick up and carry around
with us; has its own local environment. A lexical closure
is a function and its environment.
Any function (or lambda form) dened in a non-null
lexical environment|that is, not at the top level|is
actually a closure.
What does \free variable" mean? Example: variable n
in (lambda (x) ... n ...)
Normally: by lexical scoping rules, refers to value of
global symbol taken at time the lambda expression is
APPLIED: the value of n will be whatever the value of
n is when this lambda expr is applied.
Contrast: lexical closure; refers to value of that symbol
taken at time the function is created (i.e., when the clo-
sure is PERFORMED). The value PERSISTS between
fn calls.
20
Lexical Closures
The existence of variables that are local to a fn but which
persist between calls is useful for creating generators. A
generator is a function that produces a sequence of related
values.
(defun make-even (seed) #'(lambda nil (setf seed (+ seed 2))))
=> MAKE-EVEN
What is the free var above? SEED. It initially gets its value
when the closure is performed:
(setf even (make-even 0))
=> #<Interpreted Closure #xd275a6>
(funcall even)
=> 2
(funcall even)
=> 4 ;; remembers!
21
Lexical Closures (continued)
Closures can be used in some powerful ways. The following
gives us many of the advantages of global variables without
the disadvantages.
(let ((counter 0))
(defun new-id () (incf counter))
(defun reset-id () (setq counter 0)))
=> RESET-ID
(new-id)
=> 1
(new-id)
=> 2
(new-id)
=> 3
(reset-id)
=> 0
(new-id)
=> 1
(new-id)
=> 2
22
Lexical Closures (continued)
Note that (adder n): PERFORMS the closure. What does
(adder 3) return?
(adder 3)
=> #<Interpreted Closure #xd275a6>
23
Characters, Arrays, Strings
Recall: s-expressions are Atoms, lists, dotted pairs
What can an atom be?
Symbol, number
What else?
Character, array, string: atoms because not conses; but
not really atomic.
(Made up of components)
Character = #\a
| code (integer identier)
| bits (
ags for printing)
| font (integer; implementation-dependent)
(if bits and font = 0, then standard char; can be stored
in string)
Array: collection of integers, chars, symbols, s-expressions.
String: collection of chars (one-dimensional array or
vector)
Tests: characterp, arrayp, stringp
Note: arrayp returns T on strings!
24
Characters, Arrays, Strings
Characters:
Can do things like:
(characterp #\a)
=> T
(char< #\a #\b)
=> T ;; alphanumeric ordering
Equality:
char=, char<=, char>, char>=, char/= (not equal),
char-equal (upper/lower allowed),
char= (distinguishes between them)
25
Characters, Arrays, Strings (continued)
Arrays: collection of int, char, sym, any s-exp [Not strongly
typed; can be mixed]
Dimensions:
0 1 2 3 <--- start w/ 0
0 ............ rows
1 .
2 .
columns
26
Characters, Arrays, Strings (continued)
AREF:
(aref a 1)
=> X
(aref b 1 2)
=> 3
SETF:
(setf (aref a 1) 2)
=> 2 ;; Returns 2; Array a becomes: '#(x 2 x x x)
;; For efficiency, can specify type:
(setf a (make-array '(1) :element-type 'integer))
=> #<Simple-Vector T 1 FB0B9E>
(setf a (make-array '(1) :element-type 'character))
Some compilers can store the array more eciently and cre-
ate ecient code for accessing if it is typed.
27
Strings:
Combines arrays and chars (a string is a 1-d array (vector)
of chars)
X = | #\a | #\b | #\c | ! "abc" (shorthand)
{ (char x 0) = #\a (most appropriate)
{ (aref x 0) = #\a
{ (elt x 0) = #\a (least ecient: works on lists too)
How do we create a string?
(make-string 7 :initial-element #\z)
=> "zzzzzzz"
(string #\z)
=> "z"
(string 'z)
=> "z"
(string "z")
=> "z"
(format nil "~s" 'z) ;; stream is nil (just return value)
=> "z"
28
Sequences
A sequence is:
{ A 1-d array (vector): #(a b c d)
{ A list: (a b c d)
{ A string (special case vector): #(#\a #\b #\c #\d) = "abcd"
Can perform certain types of ops on all sequences:
(remove 'a '(a b c a) :test #'eq)
=> (B C)
(remove #\a "abc" :test #'char=)
=> "bc"
(reverse '(a b c)
=> (C B A)
(reverse "abc")
=> "cba"
(elt '(a b c a) 1)
=> B
Nth works on lists only (not strings, vectors). Elt used for
all sequences. If type is known, use:
{ nth for lists
{ aref for vectors/arrays
{ char for strings
29
Sequences (continued)
Other general sequence functions:
(concatenate 'string "a" "b")
=> "ab"
(concatenate 'list '(a) '(b))
=> (A B) ;; Inefficient: Use append
(concatenate 'array #(a b c d) #(e f))
=> #(a b c d e f)
(length "abc")
=> 3
(length '(a b c))
=> 3
(length #(a b c))
=> 3
(subseq "abc" 2 3)
=> "c"
(subseq '(a b c) 2 3)
=> (C)
(subseq #(a b c) 2 3)
=> #(c)
30
More Sequence Functions
Suppose x = "abcd"
FIND: (find item sequence :test <test>)
(subseq x 1)
=> "bcd"
;; Often used with subseq
(subseq x (position #\b x :test #'char=))
=> "bcd"
31
Input in Lisp: READ, READ-LINE,
READ-FROM-STRING
The input function is READ:
(with-open-file
(mystream "x.lisp" :direction :input) ; dflt is input
(setf x (read stream)) ; READS one s-expr
(format t "~s" x))
32
Input in Lisp (continued)
Here's the full format for read-from-string:
(read-from-string string eof-error-p eof-value <keywords>)
;; eof-error-p, eof-value are optional
;; eof-error-p: if t, then error at eof; else, no error at eof
;; eof-value: expression returned if eof-error-p is nil
(read-from-string x nil nil :start 5)
=> IS
=> 8
(read-from-string x nil nil :start 8)
=> A
=> 10
(read-from-string x nil nil :start 10)
=> LINE
=> 14
(read-from-string x nil nil :start 14)
=> NIL
=> 14
(defun noun-phrase ()
"generates and returns a noun-phrase as a list of atoms"
(append (article) (noun)))
=> NOUN-PHRASE
34
Programming Example (continued)
(defun verb-phrase ()
"generates and returns a verb-phrase as a list of atoms"
(append (verb) (noun-phrase)))
=> VERB-PHRASE
(defun article ()
"generates and returns an article as an atom in a list"
(one-of '(the a)))
=> ARTICLE
(defun noun ()
"generates and returns a noun as an atom in a list"
(one-of '(man ball woman table)))
=> NOUN
(defun verb ()
"generates and returns a noun as an atom in a list"
(one-of '(hit took saw liked)))
=> VERB
35
Programming Example (continued)
(sentence)
=> (A MAN TOOK THE WOMAN)
(sentence)
=> (THE MAN SAW A MAN)
(sentence)
=> (THE WOMAN TOOK A BALL)
(noun-phrase)
=> (THE WOMAN)
(verb-phrase)
=> (TOOK THE BALL)
(noun)
=> (BALL)
(verb)
=> (LIKED)
36
Using the Trace Facility (slide 35 of previous lecture)
(trace sentence noun-phrase verb-phrase
article noun verb)
=> NIL
(sentence)
Calling (SENTENCE)
Calling (NOUN-PHRASE)
Calling (ARTICLE)
ARTICLE returned (THE)
Calling (NOUN)
NOUN returned (MAN)
NOUN-PHRASE returned (THE MAN)
Calling (VERB-PHRASE)
Calling (VERB)
VERB returned (HIT)
Calling (NOUN-PHRASE)
Calling (ARTICLE)
ARTICLE returned (THE)
Calling (NOUN)
NOUN returned (WOMAN)
NOUN-PHRASE returned (THE WOMAN)
VERB-PHRASE returned (HIT THE WOMAN)
SENTENCE returned (THE MAN HIT THE WOMAN)
=> (THE MAN HIT THE WOMAN)
(untrace)
=> (SENTENCE NOUN-PHRASE VERB-PHRASE ARTICLE NOUN VERB)
37
Multiple Values
So far we've spoken of \the value returned by a func-
tion." Historically: Lisp designed so that every fn re-
turns a val. But sometimes we want more than one
piece of info (e.g., read-from-string).
Rather than creating a list for multiple types of info,
can return more than one value.
Consider read-from-string. Suppose we want to \catch"
the two values:
x <- "this is a string"
(read-from-string x nil nil :start 0) -> THIS 5
Note that this looks like a LET. Can use it to start out
a function; close it o at the end. Now we can pluck
out the tokens in the string:
x <- "this is a string"
n <- 0
(loop
(multiple-value-bind (atom ptr)
(read-from-string x nil nil :start n)
(print atom)
(setf n ptr)
(when (null atom) (return nil))))
2
Output in Lisp: Format
Output: We've been using PRINT from time to time:
(print '(a b (c)))
(A B (C)) ; side effect
=> (A B (C)) ; value
3
Output in Lisp: Format (continued)
Side eect: specify a stream name:
(format t "~%Here are 3 s-expressions: ~s, ~s, and ~s." 'a '(b c) "de")
Here are 3 s-expressions: A, (B C), and "de".
=> NIL
~{ ~} is an iteration construct:
(format t "~{~a ~}" '(mary bill))
=> MARY BILL ;; space at end.
4
Output in Lisp: Format (continued)
Format is used for output to a le (use with-open-le; im-
plicit progn):
(with-open-file (mystream "x.lisp" :direction :output)
(format stream "Hi."))
5
Output in Lisp: Examples
(format t "Howdy there in TV land!") ;; Simple cases (no directives):
Howdy there in TV land!
=> NIL
(format t "I had a little froggy.")
I had a little froggy.
=> NIL
turn around.
=> NIL
6
Output in Lisp: Examples (continued)
There must be enough arguments in the call to format; ex-
tras will be ignored:
(format t "~%this is ~a, so ~a ~a ~a!"
'(a list) 4 "ever")
> Error: Missing argument
7
Output in Lisp: Examples (continued)
(format t "This year:~@R." 1995) ;; roman
This year:MCMXCV.
=> NIL
(format t "Here's n:~R." n) ;; english
Here's n:fifty-four thousand three hundred twenty-one.
=> NIL
(format t "Here's n:~10D." n)
Here's n: 54321.
=> NIL
(format t "Here's PI:~F." PI)
Here's PI:3.141592653589793.
=> NIL
(format t "Here's PI:~15,5F." PI)
Here's PI: 3.14159.
=> NIL
(format t "Here's PI:~15,5,,,'*F." PI)
Here's PI:********3.14159.
=> NIL
(format t "Here's PI as money:$~$." PI)
Here's PI as money:$3.14.
=> NIL
8
Format: More Variations
See CLTL2 for many more variations. The power of FOR-
MAT goes well beyond numeric formatting. Consider this
example from Wilensky:
(setq n 1)
=> 1
(format t "~D boy~:P left" n)
1 boy left
=> NIL
(setq n 2)
=> 2
(format t "~D boy~:P left" n)
2 boys left
=> NIL
9
Function Parameters: &optional
10
Function Parameters: (continued)
&optional
11
Function Parameters: (continued)
&optional
12
Function Parameters: &rest
13
Function Parameters: &keyword
14
Function Parameters: (continued)
&keyword
(make-poem 'spring)
SPRING, by Lulu Lispy
--------------------------
'twas a stupendously beautiful FLOWER
upon my SNOW-covered PORCH
such a lovely lovely FLOWER
I blasted it with my TORCH.
=> NIL
(make-poem 'spring
:location 'veranda
:blaster 'power-sander)
SPRING, by Lulu Lispy
--------------------------
'twas a stupendously beautiful FLOWER
upon my SNOW-covered VERANDA
such a lovely lovely FLOWER
I blasted it with my POWER-SANDER.
=> NIL
15
Function Parameters: &keyword (continued)
(make-poem 'spring
:location 'veranda
:blaster 'power-sander
:subject 'pumpkin)
SPRING, by Lulu Lispy
--------------------------
'twas a stupendously beautiful PUMPKIN
upon my SNOW-covered VERANDA
such a lovely lovely PUMPKIN
I blasted it with my POWER-SANDER.
=> NIL
(make-poem 'spring
:subject 'space-alien
:location 'veranda
:blaster 'power-sander
:covering 'glitter)
SPRING, by Lulu Lispy
--------------------------
'twas a stupendously beautiful SPACE-ALIEN
upon my GLITTER-covered VERANDA
such a lovely lovely SPACE-ALIEN
I blasted it with my POWER-SANDER.
=> NIL
16
Function Parameters: (continued)
&keyword
17
Function Parameters: (continued)
&keyword
18
Looping Constructs
Looping in Tanimoto is very simple (dotimes, dolist, loop).
(dolist (variable list optional-result) body ...)
(defun length1 (list)
(let ((len 0)) ; start with LEN=0
(dolist (element list) ; and on each iteration
(incf len)) ; increment LEN by 1
len)) ; and return LEN
Most commonly used do macro. Note: we'll talk about LET next
time.
(dotimes (variable number optional-result) body ...)
(dotimes (i n) (print (aref a i))) prints elts of array.
(do/do* ((variable initial next) ...) (exit-test result)
body ...)
(defun length3 (list)
(do ((len 0 (+ len 1)) ; start with LEN=0, increment
(l list (rest l))) ; ... on each iteration
((null l) len))) ; (until the end of the list)
19
Loop
There are more complicated looping constructs than those
described in Tanimoto, i.e., \loop".
Note: You should not be using loop in lieu of sequence
operations. (e.g., nding an elt in a list should be done with
nd or nd-if, not loop or do).
Note from last time: (return x); will return out of a loop
of any kind (do, dolist, etc.) with the value x. (We'll
look at this in a minute.)
Steele 84: (loop body ...)
This is the old \loop forever" loop. (Tanimoto)
Steele 90: (loop do body ...)
This is the more sophisticated loop macro that allows
counting, summing, etc. Many di types of \loop key-
words." Example (from page 60):
(defun length6 (list)
(loop with len = 0 ; start with LEN=0
until (null list) ; and (until end of list)
for element = (pop list) ; on each iteration
do (incf len) ; increment LEN by 1
finally (return len))) ; and return LEN
20
Defmacro
(defmacro macro-name (parameter ...) body ...)
Compare: (defun fn-name (parameter ...) body ...)
Similar structure, but a macro is dierent: the compiler
expands macros into some other code.
When do we use macros? When we want some \nice"
syntax that expands into uglier code that we don't care
about looking at.
(1) Decide what the \nice" syntax is. (2) Decide what
the \ugly" code is.
Why used?
(1) Eciency: expanded in-line at compile time |More
ecient than calling a fn at RT
(2) Eval'n of args can be suppressed
|Example: setf does not eval rst arg.
(3) CT: can base expansion of macro on known arg vals
|(setf (aref a 1) 0):
(let* ((#:g332 a) (#:g331 0)) (excl::.inv-s-aref #:g331 #:g332 0))
| (setf a 1): (setq a 1)
(4) Shorthand: allows you to say things more concisely
Evaln of macro: extra evaln step before standard form
evaln takes place.
Use macroexpand to see what a macro expands out to.
Every time you dene a macro, you are actually dening
a new language and writing a compiler for it! So don't
take macros lightly.
21
Defmacro
Defmacro:
(1) args bound to formals
(2) macro's body eval'd (produce new form)
(3) eval the new form at RT to produce nal value
(while test body ...)
(loop (unless test (return nil)) body)
Note: unless has intuitive meaning.
What does the macro look like?
(defmacro while (test &rest body)
(list* 'loop (list 'unless test '(return nil)) body))
Expands to:
(loop (unless (< i 10) (return nil)) (print (* i i)) (setf i (+ i 1)))
22
DEFMACRO: More details
DEFMACRO symbol lambda-list {declaration | doc-string}* {form}*
[Macro]
(get-age 'tina)
=> 56
(get-age tina)
> Error: Unbound variable: TINA
(age tina)
=> 56
24
Backquote/Comma
Hardest part about dening macro is building code that
is the expansion of the macro. Have to use list, cons,
append, etc. when writing a macro.
Use backquote (`): Gives more immediate way of build-
ing code.
Handy in writing macros: acts just like quote except
that any expression preceded by a comma is considered
to be unquoted:
(setf name 'fred)
`(I gave ,name about ,(* 25 8) dollars) becomes
(i gave fred about 200 dollars)
Other examples:
'(spring forward fall back)
=> (SPRING FORWARD FALL BACK)
`(spring forward fall back)
=> (SPRING FORWARD FALL BACK)
`(spring forward fall back ,(+ 100 265) times)
=> (SPRING FORWARD FALL BACK 365 TIMES)
'(spring forward fall back ,(+ 100 265) times)
> Error: Comma not inside backquote
(list 'spring 'forward 'fall 'back (+ 100 265) 'times)
=> (SPRING FORWARD FALL BACK 365 TIMES)
25
Commas, Comma-atsign
Commas can be nested deep within list structure:
(setq name 'barney)
=> BARNEY
(setq occupation 'bozo)
=> BOZO
(append '(hello) `(mister ,name (the ,occupation)))
=> (HELLO MISTER BARNEY (THE BOZO))
(setq fudds-law
'(if you push something hard enough it will fall over))
=> (IF YOU PUSH SOMETHING HARD ENOUGH IT WILL FALL OVER)
`(in my youth i thought that ,@fudds-law -- but now i know better)
=> (IN MY YOUTH I THOUGHT THAT IF YOU PUSH SOMETHING HARD ENOUGH
IT WILL FALL OVER -- BUT NOW I KNOW BETTER)
26
Cleaner Macros
Using the backquote, how would we rewrite WHILE without
using LIST and LIST*? (i.e., using ` and , and @)?
(defmacro while (test &rest body)
`(do () ((not ,test)) ,@body))
PUSH is a macro:
(defmacro push (elt list) `(setq ,list (cons ,elt ,list)))
So: (push 'a x) ! (setq x (cons 'a x))
Note: you can assume this macro is available in your homeworks.
This is the most obvious way of inserting an elt at the front of a
list.
INCF is another macro you can assume: expands to (setq
x (+ x 1))
28
More Macros
Here's another macro-based language extension:
(defmacro arithmetic-if (test neg-form zero-form pos-form)
`(let ((x ,test))
(cond ((< x 0) ,neg-form)
((= x 0) ,zero-form)
(t ,pos-form))))
=> ARITHMETIC-IF
(defvar binky)
=> BINKY
(defvar bonko)
=> BONKO
(setq bonko 99)
=> 99
(setq binky 44)
=> 44
(macroexpand
'(arithmetic-if (- binky bonko)
(* binky 2)
(* binky 100)
(* binky bonko)))
=> (LET ((X (- BINKY BONKO)))
(COND ((< X 0) (* BINKY 2))
((= X 0) (* BINKY 100))
(T (* BINKY BONKO))))
(arithmetic-if (- binky bonko)
(* binky 2)
(* binky 100)
(* binky bonko))
=> 88
29
Using Gensym in Macros
Unfortunately, we have problems if we use the variable x:
(defvar x)
=> X
(setq x 10)
=> 10
(arithmetic-if (- x 3)
(* x 2)
(* x 100)
(* x x))
=> 49
(macroexpand
'(arithmetic-if (- x 3)
(* x 2)
(* x 100)
(* x x)))
=> (LET ((X (- X 3))) ;; We lose global value of X
(COND ((< X 0) (* X 2))
((= X 0) (* X 100))
(T (* X X))))
30
Using Gensym in Macros (continued)
(gensym "foo")
=> #:FOO288
(gensym "FOO")
=> #:FOO289
(defmacro arithmetic-if (test neg-form zero-form pos-form)
(let ((var (gensym)))
`(let ((,var ,test))
(cond ((< ,var 0) ,neg-form)
((= ,var 0) ,zero-form)
(t ,pos-form)))))
=> ARITHMETIC-IF
(macroexpand
'(arithmetic-if (- x 3)
(* x 2)
(* x 100)
(* x x)))
=> (LET ((#:G293 (- X 3)))
(COND ((< #:G293 0) (* X 2))
((= #:G293 0) (* X 100))
(T (* X X))))
(arithmetic-if (- x 3)
(* x 2)
(* x 100)
(* x x))
=> 100
31
More Macros
(defmacro fortran (operator &rest args)
(case operator
(if (cons 'arithmetic-if args))
(do (append '(do) (new-line-num) args))
(go (cons 'go args))))
=> FORTRAN
(fortran if (- x 3) (* x 2) (* x 100) (* x x))
=> 100
(MACROEXPAND-1
'(fortran if (- x 3) (* x 2) (* x 100) (* x x)))
=> (ARITHMETIC-IF (- X 3) (* X 2) (* X 100) (* X X))
(MACROEXPAND
'(fortran if (- x 3) (* x 2) (* x 100) (* x x)))
=> (LET ((#:G304 (- X 3)))
(COND ((< #:G304 0) (* X 2))
((= #:G304 0) (* X 100))
(T (* X X))))
32
More Macros (continued)
We can write a recursive version of macroexpand that ex-
pands all macro calls:
(defun macroexpand* (form)
(if (atom form)
form
(let ((expansion (macroexpand form)))
(if (consp expansion)
(cons (macroexpand* (car expansion))
(macroexpand* (cdr expansion)))
expansion))))
=> MACROEXPAND*
(macroexpand*
'(cond ((> x y) 'x-higher)
((< x y) 'x-lower)
(t 'same)))
=> (IF (> X Y)
(PROGN 'X-HIGHER)
(IF (< X Y)
(PROGN 'X-LOWER)
(IF T (PROGN 'SAME) NIL)))
(macroexpand
'(cond ((> x y) 'x-higher)
((< x y) 'x-lower)
(t 'same)))
=> (IF (> X Y)
(PROGN 'X-HIGHER)
(COND ((< X Y) 'X-LOWER) (T 'SAME)))
33
More Macros (continued)
Macros can be used to extend the language in radical ways:
(defmacro deftwinkie (name arg-list body)
`(defun ,name ,arg-list
(format t "~% I like twinkies yes I do,")
(format t "~% I like twinkies how 'bout you?")
,body))
=> DEFTWINKIE
(macroexpand-1 '(deftwinkie doubler (n) (* n 2)))
=> (DEFUN DOUBLER (N)
(FORMAT T "~% I like twinkies yes I do,")
(FORMAT T "~% I like twinkies how 'bout you?")
(* N 2))
(deftwinkie doubler (n)
(* n 2))
=> DOUBLER
(doubler 23)
I like twinkies yes I do,
I like twinkies how 'bout you?
=> 46
34
More Macros (continued)
Note also that macros cannot be passed to MAPCAR, FUN-
CALL, etc:
(mapcar #'age '(tina john wendy))
> Error: Undefined function: AGE
(mapcar #'get-age '(tina john wendy))
=> (56 21 11)
35
When should you use macros?
[This section borrows from Paul Graham's ON LISP, Pren-
tice Hall, 1994|THE book to read about lisp macros]
Here are some of the nifty things you can do with macros:
Argument transformation|e.g., the SETF macro, which picks
apart its arguments before evaluation.
Conditional evaluation of arguments|like IF, WHEN, COND, etc.
Multiple evaluation of arguments|like DO, WHILE, etc.
Use of the calling environment|a macro expansion replaces the
macro call in the lexical scope of the call|hence it can use and
change lexical bindings in ways that functions can't. For example,
the behavior of the macro:
(defmacro foo (x)
`(+ ,x y))
36
The downside of macros:
Functions are data|they can be passed as arguments,
returned from other functions, etc. None of this is pos-
sible with macros.
Clarity of source code|macro denitions can get hairy.
Clarity at runtime|macros can be harder to debug, you
can't trace them (because they're gone by runtime) and
stack backtraces are less informative when you use lots
of macros.
Recursion|an expansion function may be recursive, but
the expansion itself may not be.
37
Alists, Assoc
The matching functions in chapter 3 use an \association
list" (ALIST) for representing the associations of values
with variables.
ALIST: A list of lists in which each list is called an entry
and the car of each entry is the key. In the book, each
list is a dotted pair:
((VAR1 . VAL1) (VAR2 . VAL2) ...)
26
Alists, Assoc (continued)
Can set keys and values:
(setf (second (assoc 'one words)) 'un) ! un
What is WORDS?
(setf words '((one un) (two dos) (three tres) (four cuatro) (ve
cinco)))
Can also use RASSOC to index by value rather than key,
but the a-list must be stored as cons cells, not lists:
(setf words '((one . un) (two . dos) (three . tres) (four . cuatro)
(ve . cinco)))
(rassoc 'tres words) ! (three . tres)
27
Defstruct (continued)
Constructor, accessor, assignment automatically dened
when data structure is dened.
DEFSTRUCT name-and-options [doc-string]
{slot-description}*
[Macro]
3
Defstruct vs. Other Data Structures (continued)
You COULD build complex data structures out of lists:
I'll represent a PERSON as a list of (name age shoe-size
parents children) where:
name is a list of atoms
age is an integer
shoe-size is an integer
parents is a list of names
children is a list of names
For example:
(setq person-1
'((betty byte)
22
4
((boopsie byte)(barney byte))
((bobby byte)(belinda byte))))
=> ((BETTY BYTE) 22 4 ((BOOPSIE BYTE) (BARNEY BYTE))
((BOBBY BYTE) (BELINDA BYTE)))
4
Defstruct vs. Other Data Structures (continued)
We could also write a function with keyword arguments to
make the construction of new PERSONs easier:
(defun make-person (&key (name '(ordinary jo))
(age 20)
(shoe-size 5)
(parents nil)
(children nil))
"Returns a PERSON with the given attributes"
(list name age shoe-size parents children))
=> MAKE-PERSON
(make-person :name '(consina cadarowitz))
=> ((CONSINA CADAROWITZ) 20 5 NIL NIL)
(make-person :name '(consina cadarowitz)
:age 15
:parents '((candy car)(charlie cdr)))
=> ((CONSINA CADAROWITZ) 15 5 ((CANDY CAR) (CHARLIE CDR)) NIL)
5
Defstruct (continued)
DEFSTRUCT makes life much easier:
(defstruct person
(name '(ordinary jo))
(age 20)
(shoe-size 5)
(parents nil)
(children nil))
=> PERSON
6
Defstruct (continued)
(setq person-2 (make-person :name '(sally silicon)))
=> #S(PERSON :NAME (SALLY SILICON) :AGE 20
:SHOE-SIZE 5 :PARENTS NIL :CHILDREN NIL)
person-2
=> #S(PERSON :NAME (SALLY SILICON) :AGE 20 :SHOE-SIZE 5
:PARENTS NIL :CHILDREN NIL)
(person-name person-2)
=> (SALLY SILICON)
(person-shoe-size person-2)
=> 5
(person-p person-2)
=> T
(person-p nil)
=> NIL
(person-p person-1)
=> NIL
person-2
=> #S(PERSON :NAME (SALLY SILICON) :AGE 20
:SHOE-SIZE 5 :PARENTS NIL :CHILDREN NIL)
(setf (person-children person-2)
'((sam silicon)))
=> ((SAM SILICON))
person-2
=> #S(PERSON :NAME (SALLY SILICON) :AGE 20
:SHOE-SIZE 5 :PARENTS NIL :CHILDREN ((SAM SILICON)))