Sunteți pe pagina 1din 139

Intro to Lisp

 LISP = LISt Processing


 Originated w/ McCarthy (1959)
 Implemented Church's lambda-calculus (recursive func-
tion theory)
 Manipulation of symbolic information
 The initial Lisp was impractical (no iteration)
 Many versions: Franz, Mac, Inter, Common (now the
standard)
 Guy Steele (1990): VERY useful reference.

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 de ning 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

 Interactive (somewhat super cially), Recursive,


Very simple syntax (does automatic memory
mgmt), variables not explicitly typed, may be
interpreted or compiled.

 Oriented toward: (1) non-numeric symbol ma-


nipulation; (2) structure building and modi -
cation.

 Programs and data have same form

 Lisp is the defacto standard language in AI

14
Some di erences between Lisp and
Conventional Languages
 Symbolic Processing

 Structure Building and Modi cation


| Not only assigns values to symbols, but also permits dynamic
construction and dissection of data structures
 Programs and data have same form
| Can construct pgms on the y and even use them
Additional di erences (noted in Norvig)
 Many langs distinguish between stmts and expressions.
| Statements have e ects (x = 2 + 2).
| Expressions return values (2 + 2).
In Lisp, ALL every expression returns a value; Some of
those expressions also have side e ects.
 Lexical rules are very simple.
| Fewer punctuation chars, only parens and white space.
x=2+2 is one single symbol; need to insert spaces: (x = 2 + 2)
to separate the symbols.
 No need for semicolons as a delimiter.
| Expressions delimited by parentheses. (Semi colons are for com-
ments.)

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 ); :::

Examples: (+ 1 2), (A (B C))


| Note: case insensitive
THAT'S ALL THERE IS TO THE SYNTAX!!! (except for
dot notation and special characters | we'll get to special
chars later).
S-expression

atom list dotted pair


(a b 3) (3 . a)
symbol, (a . (b . (3 . nil)))
number,
string,
character,
array

Note: A list is a special case of a dotted pair where the last cell = NIL

So, actually: an S-expression is either an atom or a dotted pair.

 So (A) is an abbrev for (A . NIL).


 Lists more commonly used than dotted pairs (conve-
nient).
 Note: (nil . nil) = (() . ()) = (() . nil) = (()) 6= 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

The Lisp evaluation rule:


To evaluate an atom:
If it is a numeric atom, the value is the number
Else it must be a variable symbol;
so the value is the value of the variable

To evaluate a list: Assume the first item of the list is a


function, and apply that function to the values of the remaining
elements of the list.

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 in x a+b+ +n
::: :::

| In Lisp: put f inside left paren: (f a b n):::

 By convention, we assume that when given a list to


evaluate, the rst atom has a value associated with it
which is the body of a function.
([name of fn] [args])
| (+ 1 2) ! 3
| First elt of list is special: applied to rest of elts.
 Evaluation of list: (1) eval each elt of list ( rst elt
should return fcn body, rest return args); (2) apply fcn
from 1st atom to rest.
 Important Note: use ' to indicate \the S-expr stated"
rather than the value of that S-expr. (e.g., 'X returns
X, whereas X returns the value stored in X).
 We'll run through what a sample session looks like in a
minute.
19
Quote symbol
Examples:
| '2 ! 2
| 2!2
| 'John ! John
| John ! ERROR: not a bound variable
 Quote: use ' to indicate \the S-expression stated" rather
than the value of that S-expression.
 Serves to BLOCK evaluation of following expression,
returning it literally.
 Example: '(Pat Kim) ! (PAT KIM)
If we just had (Pat Kim), it would consider Pat as a
function and apply it to the value of the expression Kim.

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

x ; Can abort out of this (abort current


=> Error: Unbound variable X ; computation and return to read-eval-print loop)

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

(setf y (reverse '(+ a b))) ; Reverse reverses a list.


=> (B A +)

(setf l '(a b c d e)) ; l's value will now be a list.


=> (A B C D E)

(length l) ; length --> gives length of a list


=> 5

(append l '(f)) ; append --> merges to lists together


=> (A B C D E F) ; (Note use of quote; l not quoted)

(length l)
=> 5 ; Note that the length of l has not changed.

(length (append '(pat Kim) (list '(John Q Public) 'Sandy)))


=> 4 ; Symbolic computations can be nested and
; mixed with numeric computations.

;;; Note: "list" puts elts into a list; doesn't merge them like "append" does.

;;; Note: Order of evaluation critical. (Eval args first.)

;;; Note: Parens MUST match --> source of bizarre errors if not.

(car l) ; (first l)
=> A

(cdr l) ; (rest l)
=> (B C D E)

(first (rest l))


=> B

(cons 'q l) ; MAIN CONSTRUCTOR used in Lisp.


=> (Q A 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 ..))

A B Also: FIRST, SECOND, ...


A B TENTH
(car,cadr,caddr, ...)
A B
(CONS ’A ’B) CAR = A CDR = B
These are off in memory
Note: No SIDE EFFECT (important later)

Difference Between: CONS, LIST, APPEND

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)?

A nil A nil B nil


(CONS X Y) = ((A) B) (LIST X Y) = ((A) (B))

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

The single quote mark (') (it is actually an apostrophe) can


be used as an abbreviation for (quote ...).

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)))

Everything except the rst element of a list is the cdr of the


list.
(cdr '(alphabet soup))
=>(soup)
(cdr '((pickles beer) alphabet (soup)))
=> (alphabet (soup))
(cdr '(((miro (braque picasso))) leger))
=>(leger)

Given a complex S-expression, you can obtain any of its


component S-expressions through some combination of car
and cdr. cadr is the car of the cdr:
(cadr '(old mcdonald had a farm))
=>MCDONALD

cdar is the cdr of the car:


=> (cdar '((do you)(really think so?)))
(YOU)

There are more variations, at least up to 4 a's or d's:


(caddar
'((deep
(in the (nested) structure)
lies
truth)))
=> LIES

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)

The null list:


(cdr '(just-one-item))
=> NIL
'()
=> NIL

4
Putting lists together
cons builds lists.
(cons 'woof '(bow wow))
=> (woof bow wow)
(cons '(howl) '(at the moon))
=> ((howl) at the moon)

You can create any list with cons.


(cons 'naked-atom '())
=> (NAKED-ATOM)
(cons 'naked-atom nil)
=>(NAKED-ATOM)

car, cdr, and cons are all non-destructive:


(setf x '(the rain in spain))
=>(THE RAIN IN SPAIN)
(car x)
=>THE
(cdr x)
=>(RAIN IN SPAIN)
(cons 'darn x)
=>(DARN THE RAIN IN SPAIN)
x
=>(THE RAIN IN SPAIN)

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)

Although \dotted pairs" have their uses, we mostly won't


use them beyond the rst problem set. That means that
you're probably doing something wrong if you get dots in
your answers.

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)

 setq is an exception to the normal evaluation rule; the


rst argument is not evaluated. (q = quote)
 But setq cannot be used for generalized vars ! rplaced
with setf.
 A generalized var refers to any place a ptr may be stored.
A "A has the value (3)"

3 nil
A 3 "A has the value 3"

 Other places a ptr can be stored?


 car/cdr of cons cell
 Asst ! replacing one ptr with another
(setf A ’(4)) (setf (car A) 4)

A A

"A has the value (4)"


3 nil 3 nil
4
Can use an accessor
such as CAR, CDR,
4 nil FIRST, etc.

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

 Symbol: \indivisible atoms" actually have parts.


Typically each atom is a table:
X Name "X" (string -- not same as symbol)
Value (if nothing here, atom is unbound)
Function (function definition)
Prop List

Package

 What does setf do? (setf x 7)


Changes value slot of X's table to point to 7
 Can access slots:
(symbol-name 'reverse) ! "reverse"
(symbol-function 'reverse) ! #<compiled-fn reverse>

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

(cdr '(here is #| a list with a comment |# huh?))


=> (IS HUH?)

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

 A Predicate is a function that is called to an-


swer a yes-or-no question.

 A predicate returns boolean truth values (T =


true / NIL = false)

 T/NIL special symbols; prede ned to have self


as value. (The atom t means true, and evalu-
ates to itself. The atom NIL means false, and
evaluates to itself.)

 Predicates may return t for true, or they may


return any other non-nil value.

 Predicates in Common Lisp take various forms,


but often end with p.

12
Predicates (continued)

1. <, > (numeric functions)

2. macros may serve as predicates: (AND t nil),


(OR t nil)
Note: implementation of these allows them to be control constructs
(and nil t t t) | stop at rst nil, (or t nil nil nil) | stop at rst t.
Note: Generally considered poor style to use and/or for side e ect:
(or (setf x (<fn> ...)) (setf x 'fail)) | should use to test log-

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.

 EQ tests whether args eval to exact same Lisp obj

 EQUAL compares any two s-expressions (lists, strings,


etc.)
 = used for numbers (must be same number)

 EQL: EQ or =

 EQUALP | same as EQUAL but disregards case (for


strings).
 Others: tree-equal, char-equal, string-equal, string=,
char=
Note: these are all a special case of equal; Tree-equal tests whether
two trees are equal except that the leaf nodes are expected to be
symbol atoms (not, eg., strings)
Note: can specify :TEST such as string=.
x y = eq eql equal equalp
'x 'x err T T T T
'0 '0 T ? T T T
'(x) '(x) err nil nil T T
'"xy" '"xy" err nil nil T T
'"Xy" '"xY" err nil nil nil T
'0 '0.0 nil nil nil nil T
'0 '1 nil nil nil nil nil
16
Equality: Need to think about what's happening in
memory
So far: S-exprs drawn so that
symbol referenced more than once What’s really going on?
drawn multiple times in memory

nil nil

A B A B
EQ
A

 In reality, both pointers to A point to the SAME symbol


(i.e., the same exact mem location)
 EQ: tests for memory location exactly | therefore,
atoms only!
 LISP automatically makes sure that all refs to some
symbol actually point to the UNIQUE point in mem
that the symbol happens to be stored physically. This
way, any info stored w/ it (e.g., its value) is accessible
from every ref.
 Anytime LISP sees new symbol, it adds the symbol to
its storehouse of known atoms.

17
Sample Session: Equality
(equalp 'foot 'foot)
=> T
(equalp 'nose 'ear)
=> NIL
(equalp (+ 22 33 44) (* 33 3))
=> T

(setq long-list '(1 2 3 4 can I show you out the door?))


=> (1 2 3 4 CAN I SHOW YOU OUT THE DOOR?)

(setq lucky-number 23)


=> 23

(or (equalp lucky-number (car long-list))


(equalp (* (car long-list) 2)
(car (cdr long-list))))
=> T

(and (equalp lucky-number (car long-list))


(equalp (* (car long-list) 2)
(car (cdr long-list))))
=> NIL

18
Conditionals
 IF: special form (if (= x 21) (print 'blackjack))
 WHEN: macro; has same form: (when (= x 21) (print 'blackjack))
 Di erence: 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

(when C E1 E2) C1 Y E11 E12...

C Y E1 E2...

Cn Y En1 En2...

 COND: Powerful multiway branching construct; Arbitrary number


of args (called clauses) Note: general form ! can have more than
one action.
 Each clause is a list of s-exprs Example: (Ci Ei1 Ei2 ... Eim).
 Value returned : Eim, where Ci is rst non-nil condition
(No more Ci or Ei's are evaluated.)
 Convention for our class: last Ci is the constant T (guaranteed to
be non-nil).

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

(cond ((equalp x 1) '(it was one))


((equalp x 2) '(it was two))
(t '(it was not one or two)))
=> (IT WAS NOT ONE OR TWO)

(setq x 2)
=> 2

(cond ((equalp x 1) '(it was one))


((equalp x 2) '(it was two))
(t '(it was not one or two)))
=> (IT WAS TWO)

23
De ning Functions
 De ne 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

Note the use of a documentation string in the function


above. The string is not obligatory and does not a ect
the execution of the function, but it provides information
about the function which can be accessed through the built-
in functions \documentation" and \describe".
(documentation 'add-20 'function)
=> returns n + 20

(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" ...

The semicolon comment follows a convention:


(1) Preface description of function with three semicolons;
(2) Preface lines inside of a function with two semicolons;
(3) Preface minor comments at end of line with two semi-
colons.
;;; The function TEST runs a demonstration.
(defun test ()
"Runs a test of SOLVE-PROBLEM."
(setf x 'test-data) ; Assign X some test data.
;; Call the main function.
(solve-problem x))

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)

Here's a NON-constant function that takes no arguments


(defun rand8 ()
(random 8))
=> RAND8
(rand8)
=> 0
(rand8)
=> 6
(rand8)
=> 4

Note: \random" is a pseudo random number generator that


takes a number and returns a value between zero (inclusive)
and the number (exclusive).
Here's a function using COND that returns an atom indicat-
ing whether its argument is even or odd (or not a number).
(defun even-or-odd? (n)
"returns 'even if n is even, 'odd if n is odd, and 'neither if n is not an in
(cond ((not (integerp n)) 'neither)
((evenp n) 'even)
((oddp n) 'odd)))
=> EVEN-OR-ODD?

(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

Here's a version of + that also sets the global variable *last-


sum*:
(defun +store (n1 n2)
"Returns the sum of n1 and n2, and also sets *last-sum* to the
(setq *last-sum* (+ n1 n2)))
=> +STORE
(+store 99 100)
=> 199
*last-sum*
=> 199

Here's a function that takes 3 arguments and returns a de-


scriptive list:
(defun funny-arg-lister (arg1 arg2 arg3)
(cons (cons 'arg1 (cons arg1 nil))
(cons (cons 'arg2 (cons arg2 nil))
(cons (cons 'arg3 (cons arg3 nil))
nil))))
=> FUNNY-ARG-LISTER

(funny-arg-lister 'a 'b '(x y z))


=> ((ARG1 A) (ARG2 B) (ARG3 (X Y Z)))

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)))

Here's another simple function


(defun double-cons (one-thing another-thing)
"Returns the result of consing one-thing onto another-thing twic
(cons one-thing (cons one-thing another-thing)))
=> DOUBLE-CONS

(double-cons 'hip '(hooray))


=> (HIP HIP HOORAY)

(double-cons 1 nil)
=> (1 1)

(double-cons 1 (double-cons 1 nil))


=> (1 1 1 1)

(defun quadruple-cons (one-thing another-thing)


"Returns the result of consing one-thing onto another-thing 4x"
(double-cons one-thing
(double-cons one-thing another-thing)))
=> QUADRUPLE-CONS

(quadruple-cons 'um '(huh?))


=> (UM UM UM UM HUH?)
28
Sample Session: Defun (continued)
Function arguments are LOCAL variables
(setq shoes 'nikes)
=> NIKES

(defun run (shoes)


(print (append '(i think i will take a trot in my)
(list shoes)))
(setq shoes (append '(old beat-up) (list shoes)))
(print (append '(i think i will take a trot in my) shoes)))
=> RUN

(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

(defun run (shoes) ;; this version returns '(I RAN)


(print (append '(i think i will take a trot in my)
(list shoes)))
(setq shoes (append '(old beat-up) (list shoes)))
(print (append '(i think i will take a trot in my) shoes))
'(i ran))
=> RUN

(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

The LENGTH function returns the number of elements in a


list
(length animals)
=> 6
(length '(the (deeper (you (go (the (nuller (you
(get)))))))))
=> 2

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

This returns a random element as a singleton list


(defun one-of (set)
"picks one element of the set and returns it in a list"
(list (random-elt set)))
=> ONE-OF

(one-of '(how are you doing today?))


=> (YOU)

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 ...

;; The grammar can be used to generate sentences:


To get a Sentence, append a Noun-Phrase and a Verb-Phrase
To get a Noun-Phrase, append an Article and a Noun
Choose "the" for the Article
Choose "man" for the Noun
The resulting Noun-Phrase is "the man"
To get a Verb-Phrase, append a Verb and a Noun-Phrase
Choose "hit" for the Verb
[etc.]

;; The grammar can be used as the basis of the following


;; Lisp functions:
(defun sentence ()
"generates and returns a sentence as a list of atoms"
(append (noun-phrase) (verb-phrase)))
=> SENTENCE

(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
 De ne 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? '(remember the alamo))


=> T
(lat? '(did you remember (to floss?)))
=> NIL
(lat? long-list)
=> T
(lat? 12)
=>
> Error: Can't take CAR of 12.
> While executing: LAT?
> Type Command-. to abort.
See the Restarts menu item for further choices.
1 >

(defun lat? (l)


"Returns t if the argument is a list of atoms, nil otherwise"
(cond ((not (listp l)) nil)
((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 de ned 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)

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

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

Note that MEMBER compares using EQ, not EQUALP. For


example:
(member '(nest) '(a hornet in a hornet (nest) is to be avoided))
=> NIL
(member? '(nest) '(a hornet in a hornet (nest) is to be avoided))
=> T

We can get MEMBER to match using equalp by providing


a keyword argument (details on this another day...)
(member '(nest)
'(a hornet in a hornet (nest) is to be avoided)
:test #'equalp)
=> ((NEST) IS TO BE AVOIDED)

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

Using IF we can write a simpler version of factorial:


(defun factorial (n)
"returns the factorial of n"
(if (zerop n)
1
(* n (factorial (- n 1)))))
=> FACTORIAL
(factorial 5)
=> 120

Here's a recursive length function:


(defun my-length (list)
"returns the length of list"
(if (null list)
0
(+ 1 (my-length (cdr list)))))
=> MY-LENGTH
(my-length
'(let it snow let it snow let it AAAARRRRRGGGGGHHHH!!!!))
=> 9

42
Iteration: DOTIMES
Sometimes iteration seems more natural. Common Lisp pro-
vides many iteration constructs.
DOTIMES (var countform [resultform]) {declaration}* {tag | statement}*
[Macro]

Executes forms countform times. On successive executions, var is bound


to the integers between zero and countform. Upon completion,
resultform is evaluated, and the value is returned. If resultform is
omitted, the result is nil.

(dotimes (n 20 'spumoni) (print n))


0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
=> SPUMONI

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)

Here's a version using dotimes:


(defun multi-cons
(one-thing another-thing n)
"Returns the result of consing one-thing onto another-thing
n times"
(dotimes (count n)
(setq another-thing
(cons one-thing another-thing)))
another-thing)
=> 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.

(defun greet (people)


"prints greetings for all of the people listed."
(dolist (person people)
(print (append '(so nice to see you)
(list person)))))
=> GREET
(setq guests '(sally booboo mr-pajamas ms-potato-head))
=> (SALLY BOOBOO MR-PAJAMAS MS-POTATO-HEAD)
(greet guests)
(SO NICE TO SEE YOU SALLY)
(SO NICE TO SEE YOU BOOBOO)
(SO NICE TO SEE YOU MR-PAJAMAS)
(SO NICE TO SEE YOU MS-POTATO-HEAD)
=> 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 e ect 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

(cond ((equalp x 1) '(it was one))


((equalp x 2) '(it was two))
(t '(it was not one or two)))
=> (IT WAS NOT ONE OR TWO)

(setq x 2)
=> 2

(cond ((equalp x 1) '(it was one))


((equalp x 2) '(it was two))
(t '(it was not one or two)))
=> (IT WAS TWO)

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

The LENGTH function returns the number of elements in a


list
(length animals)
=> 6
(length '(the (deeper (you (go (the (nuller (you
(get)))))))))
=> 2

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

This returns a random element as a singleton list


(defun one-of (set)
"picks one element of the set and returns it in a list"
(list (random-elt set)))
=> ONE-OF
(one-of '(how are you doing today?))
=> (YOU)

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? '(remember the alamo))


=> T
(lat? '(did you remember (to floss?)))
=> NIL
(lat? long-list)
=> T
(lat? 12)
=>
> Error: Can't take CAR of 12.
> While executing: LAT?
> Type Command-. to abort.
See the Restarts menu item for further choices.
1 >

(defun lat? (l)


"Returns t if the argument is a list of atoms, nil otherwise"
(cond ((not (listp l)) nil)
((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

(double-cons 'hip '(hooray))


=> (HIP HIP HOORAY)

(defun quadruple-cons (one-thing another-thing)


"Returns the result of consing one-thing onto another-thing 4x"
(double-cons one-thing
(double-cons one-thing another-thing)))
=> QUADRUPLE-CONS

(quadruple-cons 'um '(huh?))


=> (UM UM UM UM HUH?)

Here's a generalization of double-cons and quadruple-cons


we de ned 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)

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

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

Note that MEMBER compares using EQ, not EQUALP. For


example:
(member '(nest) '(a hornet in a hornet (nest) is to be avoided))
=> NIL
(member? '(nest) '(a hornet in a hornet (nest) is to be avoided))
=> T

We can get MEMBER to match using equalp by providing


a keyword argument:
(member '(nest)
'(a hornet in a hornet (nest) is to be avoided)
:test #'equalp)
=> ((NEST) IS TO BE AVOIDED)

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

Using IF we can write a simpler version of factorial:


(defun factorial (n)
"returns the factorial of n"
(if (zerop n)
1
(* n (factorial (- n 1)))))
=> FACTORIAL
(factorial 5)
=> 120

Here's a recursive length function:


(defun my-length (list)
"returns the length of list"
(if (null list)
0
(+ 1 (my-length (cdr list)))))
=> MY-LENGTH
(my-length
'(let it snow let it snow let it AAAARRRRRGGGGGHHHH!!!!))
=> 9

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]

Executes forms countform times. On successive executions, var is bound


to the integers between zero and countform. Upon completion,
resultform is evaluated, and the value is returned. If resultform is
omitted, the result is nil.

(dotimes (n 20 'spumoni) (print n))


0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
=> SPUMONI

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)

Here's a version using dotimes:


(defun multi-cons
(one-thing another-thing n)
"Returns the result of consing one-thing onto another-thing
n times"
(dotimes (count n)
(setq another-thing
(cons one-thing another-thing)))
another-thing)
=> 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]

DEFVAR proclaims variable-name to be a special variable,


optionally sets it to the value of initial-value, and returns
variable-name. If initial-value is given, variable-name is ini-
tialized to the result of evaluating it unless variable-name
already has a value. If initial-name is not used, it is not
evaluated. The macro defvar only has an e ect the rst
time it is called on a symbol. Documentation may be pro-
vided as a string.
DEFPARAMETER variable-name initial-value &optional documentation
[Macro]

DEFPARAMETER proclaims variable-name to be a special


variable, sets it to the value of evaluating initial-value, a
form, and returns variable-name. Documentation may be
provided as a string.
Globals are conventionally written with surrounding * char-
acters:
(defvar *world-state*
'((clear block-a) (on block-a table)))

(defparameter *depth-limit* 10
"The maximum depth to which we will search")

Note: Subsequent defvars for a previously defvar'd variable


are ignored. Subsequent defparameters act as setq's.
15
Defparameter, defvar, defconstant
 (DEF var-name init-val)
 DEFPARAMETER: de nes param, i.e., a var that does not change
over course of computation, but that might change when we think
of new things to add. A change to a param is a change TO the
pgm.

 DEFVAR: routinely changed during the course of running the pro-


gram. A change to a param is a change BY the pgm.
 DEFCONSTANT: declare a symbol that will ALWAYS stand for
a particular value. (Compiler optimizes; might expand out in a
macro, for example, and an error will result at RT if a val is as-
signed.)
Important note: There is a di erence between DEFPARAMETER and
DEFVAR:
 If global initialized using DEFPARAMETER in a le, then each
time le is loaded, the global will be reset to the initial value.
 If global initialized using DEFVAR in a le, then ONLY THE FIRST
TIME the le is loaded will the global set to the initial value (not
on subsequent times).
 Why is this the case? DEFVAR doesn't reinitialize a var because
it assumes this val is in ux and may change many times over; it
takes the initial val as the TRUE initial val and won't reinitialize
w/o an explicit SETF.
 DEFPARAMETER is di erent; it assumes that if you're giving it
an initial value that will not be changed by the program. So it
WILL reinitialize each time the le is loaded.

16
Local Variables
The LET special form is used to create local variables.
(let ((foo 2) (bar 3))
(+ foo bar))
=> 5

LET ({variable | (variable value) }*) {declaration}* {form}*


[Special Form]

LET creates a binding for each variable (in parallel) and


evaluates forms in the resulting environment. Returns the
value of the last form.
(setq laadeedaa 'hihohiho)
=> HIHOHIHO
(let ((laadeedaa 'feefifofum))
(list laadeedaa laadeedaa laadeedaa))
=> (FEEFIFOFUM FEEFIFOFUM FEEFIFOFUM)
laadeedaa
=> HIHOHIHO

Note that the initializations in a LET are performed \in par-


allel". You may not rely on the earlier ones being performed
before the later ones.
If you want sequential initialization behavior, use LET*:
(let* ((foo 2) (bar (+ foo 3)))
(+ foo bar))
=> 7

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 de ned 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)))))

 Let*: is the same as Let except scope begins immedi-


ately.
More formally: let* = (let (let ...))
 (setf x 1)
=> 1
(let ((x 5) (y (1+ x))) (print (+ x y)))
=> 7 ;; side effect
7 ;; value
(print x)
=> 1
1

 (setf x 1)
=> 1
(let* ((x 5) (y (1+ x))) (print (+ x y)))
=> 11
11
(print x)
=> 1
1

 Note: use Let whenever possible.


 (let* ((x 6) (y (* x x))) (+ x y))
(let ((x 6)) (let ((y (* x x))) (+ x y)))
((lambda (x) ((lambda (y) (+ x y)) (* x x))) 6)

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) de ned 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!

An even neater thing to do:


(setf (symbol-function 'even) (make-even 0))
=> #<Interpreted Closure #xd275a6>
(even)
=> 2 ;; Now use as function
(even)
=> 4

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

We can also return closures from functions and then call


them with FUNCALL or MAPCAR:
(defun make-adder (n)
#'(lambda (x) (+ x n)))
=> MAKE-ADDER
(funcall (make-adder 2) 5)
=> 7
(funcall (make-adder 12) 500)
=> 512
(mapcar (make-adder 3) '(1 3 10))
=> (4 6 13)

22
Lexical Closures (continued)
Note that (adder n): PERFORMS the closure. What does
(adder 3) return?
(adder 3)
=> #<Interpreted Closure #xd275a6>

This is a closure that REMEMBERS the value 3!!! (n is set


to 3 internally). Suppose Y is set to this result. We can
now APPLY the closure:
(funcall y 2)
=> 12
(apply y '(2))
=> 12
(mapcar y '(1 3 10))
=> (11 13 20)

It is also possible to make closures that can be asked to


change their state
(defun make-adderb (n)
;; allows optional argument (discussed next lecture)
#'(lambda (x &optional change)
(if change
(setq n x)
(+ x n))))
=> MAKE-ADDERB
(setq addx (make-adderb 1))
=> #<INTERPRETED-LEXICAL-CLOSURE #x97AFE6>
(funcall addx 3)
=> 4
(funcall addx 100 t)
=> 100
(funcall addx 3)
=> 103

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 identi er)
| 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)

Some other tests:


(lower-case-p #\a)
= T
(upper-case-p #\A)
=> T
(char-downcase #\A)
=> #\a

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

Make-array: Can initialize array in two di erent ways:


(setf b (make-array '(2 3) :initial-contents '((a b c) (1 2 3))))
rows cols [optional]

(setf b '#2a((a b c) (1 2 3)))


||
|+--- stands for array
+---2 dimensions

#2a is a reader macro that signals 2d array; automatically


counts columns.
(array-rank b) ;; number of dimensions
=> 2
(array-dimensions b) ;; actual dimensions
=> (2 3)
(array-dimension b 0)
=> 3
(setf a '#(x x x x x))
|
+---use for one dimension (i.e., a vector)

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

Note: Nth more ecient than Elt:


(nth '(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>)

(find #\a x :test #'char=)


=> #\a
(find #\b x :test #'char<) ;; Useful with #'char<
=> #\a

 POSITION: (position item sequence :test <test>)

(position #\a x :test #'char=)


=> 0

 SUBSEQ: (subseq sequence start &optional end)

(subseq x 1)
=> "bcd"
;; Often used with subseq
(subseq x (position #\b x :test #'char=))
=> "bcd"

 REMOVE-IF: (remove-if test sequence ...)

(remove-if #'oddp '(1 2 3 2 1 0 -1))


=> (2 2 0)
(remove-if-not #'oddp '(1 2 3 2 1 0 -1))
=> (1 3 1 -1)

 FIND-IF: (find-if test sequence ...)

(find-if #'evenp '(1 2 3 2 1 0 -1))


=> 2

 POSITION-IF: (position-if test sequence ...)

(position-if #'evenp '(1 2 3 2 1 0 -1))


=> 1

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))

File is closed when "with" is exited.


 Note: read used w/o arg means d t stream is *standard-
input* (the screen usually). So if you say (read), it'll
sit there until user types in.
 (read-line stream) is a variant of read: reads a whole
line and returns a string. Can then use read-from-string.
(setf n 0)
(setf x (read-line stream)) "this is a line"
=> "this is a line"
(read-from-string x :start 5)
=> THIS
=> 5

Note: read-from-string returns two values (atom and


pointer of next char); we'll learn how to catch these
values shortly.
Note: This is NOT really correct! Need to specify two
optional parameters ...

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

 Other types of input: load a le:


(load "x.lisp") ; source
(load "x.fasl") ; compiled
(compile-file "x") ; turns .lisp into binary file
(load "x") ; can say this and it'll look for binary
; if none then it'll look for .lisp

 Note: If you have two modules, A, and B, where B


depends on A, need to be careful about order of com-
pilation and loading:
A B
macro uses
defns macros
compile then
and load compile
this first and load
this

B must know about A before it is compiled and loaded.


33
Programming Example (slide 32 of previous lecture)
(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 ...

;; The grammar can be used to generate sentences:


To get a Sentence, append a Noun-Phrase and a Verb-Phrase
To get a Noun-Phrase, append an Article and a Noun
Choose "the" for the Article
Choose "man" for the Noun
The resulting Noun-Phrase is "the man"
To get a Verb-Phrase, append a Verb and a Noun-Phrase
Choose "hit" for the Verb
[etc.]

;; The grammar can be used as the basis of the following


;; Lisp functions:
(defun sentence ()
"generates and returns a sentence as a list of atoms"
(append (noun-phrase) (verb-phrase)))
=> SENTENCE

(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

 We need to catch these two values by using multiple-


value-bind:
(multiple-value-bind (atom ptr)
(read-from-string x nil nil :start n))

 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))))

 How do we RETURN more than one value? Uses values:


<code that creates values x and y>
(values x y)

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

Better to use FORMAT:


(format stream format-string E1 ... En)
The FORMAT function produces formatted output to char-
acter streams. It is very exible and has many options. The
rst argument to FORMAT should be a stream. Use t for
output to the listener. The second argument to FORMAT
should be a string, which may or may not contain \direc-
tives".
Format is generally used either for side e ect or for value,
but not both. (If used for side e ect, the value is NIL.)

3
Output in Lisp: Format (continued)
Side e ect: 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

If stream = t, then usually *standard-output* (the screen ).


Can also specify another type of stream, like a le name.
Directive: The \tilde" character (~) is used to signal a di-
rective.
 ~s prints s-expr (as is)

 ~a prints s-expr w/o quotes/slashes

 ~% is a directive that starts a new line (unconditionally)

 ~& is a directive that starts a new line unless already on


a new line.

 ~{ ~} is an iteration construct:
(format t "~{~a ~}" '(mary bill))
=> MARY BILL ;; space at end.

 ~{~a~^ ~} is up and out (used w/ iteration construct):


(format t "~{~a~^ ~}." '(mary bill))
=> MARY BILL ;; no 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."))

File is saved out when \with" is exited.


Value: specify nil for stream name (returns a string; no side
e ect):
(format nil
"~%Here are 3 s-expressions: ~s, ~s, and ~s." 'a '(b c) "de")
=> "Here are 3 s-expressions: A, (B C), and "de"."

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

One of the simplest and most useful directives is ~%, which


means to output a newline character.
(format t "~%Bop bop~% shoo~% bob~% bop bop bam")
Bop bop
shoo
bob
bop bop bam
=> NIL

Even ~% has more options { for example, a number between


the ~ and the % will cause that many newlines to be printed:
(format t "jump down ~5% turn around.")
jump down

turn around.
=> NIL

The second handiest format directive is ~a. This means \go


get the next lisp object from the argument list and print it
here."
(format t "~%this is ~a, so ~a ~a ~a!"
'swell 'stomp 'and 'yell)
this is SWELL, so STOMP AND YELL!
=> NIL
(format t "~%this is ~a, so ~a ~a ~a!"
'(a list) 4 "ever" 'tell)
this is (A LIST), so 4 ever TELL!
=> 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

(format t "~%this is ~a, so ~a ~a ~a!"


'(a list) 4 "ever" 'tell 'somebody)
this is (A LIST), so 4 ever TELL!
=> NIL

Format directives can be used to print numbers in a wide


range of formats.
(setq n 54321)
=> 54321
(format t "Here's n:~A." n)
Here's n:54321.
=> NIL
(format t "Here's n:~O." n)
Here's n:152061.
=> NIL
(format t "Here's n:~X." n)
Here's n:D431.
=> NIL
(format t "Here's n:~E." n)
Here's n:5.4321E+4.
=> NIL
(format t "Here's n:~B." n)
Here's n:1101010000110001.
=> NIL
(format t "Here's n:~5R." n) ;; base 5
Here's n:3214241.
=> NIL

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

The ~{ and ~} directives can be used to perform iteration


within a format string:
(setq folks '(candy sandy wendy henry ben))
=> (CANDY SANDY WENDY HENRY BEN)
(format t "It was a mess. ~{~%~A fell down,~}OUCH!"
folks)
It was a mess.
CANDY fell down,
SANDY fell down,
WENDY fell down,
HENRY fell down,
BEN fell down,OUCH!
=> NIL

There are format directives for case-conversion, tables, in-


direction, etc... See CLTL2 for details.
Providing NIL as the rst argument to FORMAT causes the
string to be RETURNED rather than printed.
(progn (setq my-string (format nil "wowie zowie!"))
'hmmm)
=> HMMM
my-string
=> "wowie zowie!"

9
Function Parameters: &optional

Ordinary function de nitions specify some number of RE-


QUIRED parameters; calls to these functions must pass ex-
actly the correct number of arguments:
(defun gimme-two (the-first the-second)
(list 'here-they-are the-first the-second))
=> GIMME-TWO
(gimme-two 1 2)
=> (HERE-THEY-ARE 1 2)
(gimme-two 1)
> Error: Too few arguments.
(gimme-two 1 2 3)
> Error: Too many arguments.

The rst enhancement that we'll look at is optional param-


eters. A function is speci ed to take optional parameters
by putting &optional, followed by parameter names, in the
parameter list.
(defun shout (stuff &optional more-stuff)
(list 'hey 'you! '--- stuff more-stuff '!))
=> SHOUT
(shout 'look 'out)
=> (HEY YOU! --- LOOK OUT !)
(shout 'look)
=> (HEY YOU! --- LOOK NIL !)
(defun shout (stuff &optional more-stuff)
(if more-stuff
(list 'hey 'you! '--- stuff more-stuff '!)
(list 'hey 'you! '--- stuff '!)))
=> SHOUT
(shout 'look 'out)
=> (HEY YOU! --- LOOK OUT !)
(shout 'look)
=> (HEY YOU! --- LOOK !)

10
Function Parameters: (continued)
&optional

If you don't want your optional parameter to default to nil,


you can provide another default value:
(defun shout (stuff &optional (more-stuff 'around))
(list 'hey 'you! '--- stuff more-stuff '!))
=> SHOUT
(shout 'look 'out)
=> (HEY YOU! --- LOOK OUT !)
(shout 'look)
=> (HEY YOU! --- LOOK AROUND !)

You can specify as many optional arguments as you want:


(defun fill-grocery-bag
(item &optional (item2 'milk) (item3 'eggs))
(list 'the 'bag 'contains item item2 'and item3))
=> FILL-GROCERY-BAG
(fill-grocery-bag 'spam)
=> (THE BAG CONTAINS SPAM MILK AND EGGS)
(fill-grocery-bag 'spam 'flan)
=> (THE BAG CONTAINS SPAM FLAN AND EGGS)

(fill-grocery-bag 'spam 'flan 'marjoram)


=> (THE BAG CONTAINS SPAM FLAN AND MARJORAM)
(fill-grocery-bag 'spam 'flan 'marjoram 'dan)
> Error: Too many arguments.

11
Function Parameters: (continued)
&optional

You can also specify SUPPLIED-P variables with your op-


tional parameters:
(defun eat (what &optional
(when 'supper when-supplied))
(if (and (eq when 'supper)
when-supplied)
(print '(supper is the default -- you could
have left it out)))
(list 'you 'ate what 'at when))
=> EAT
(eat 'gefilte-fish 'lunch)
=> (YOU ATE GEFILTE-FISH AT LUNCH)
(eat 'gefilte-fish)
=> (YOU ATE GEFILTE-FISH AT SUPPER)
(eat 'gefilte-fish 'supper)
(SUPPER IS THE DEFAULT -- YOU COULD HAVE LEFT IT OUT)
=> (YOU ATE GEFILTE-FISH AT SUPPER)

12
Function Parameters: &rest

We can write functions that are even more exible by using


&rest parameters. An &rest parameter gets bound to ALL
remaining arguments in the function call.
(defun eat (first-food &rest more-foods)
(format t "~%you started with ~A" first-food)
(dolist (next-food more-foods)
(format t "~%and then you had ~A" next-food))
'burp)
=> EAT
(eat 'gefilte-fish)
you started with GEFILTE-FISH
=> BURP
(eat 'gefilte-fish 'horseradish)
you started with GEFILTE-FISH
and then you had HORSERADISH
=> BURP
(eat 'gefilte-fish 'horseradish 'pickles)
you started with GEFILTE-FISH
and then you had HORSERADISH
and then you had PICKLES
=> BURP
(eat 'gefilte-fish 'horseradish 'pickles 'olives)
you started with GEFILTE-FISH
and then you had HORSERADISH
and then you had PICKLES
and then you had OLIVES
=> BURP

13
Function Parameters: &keyword

The &keywords facility allows us to specify NAMED optional


parameters.
(defun make-poem (title &key (subject 'flower))
(format t "~%~A, by Lulu Lispy" title)
(format t "~%--------------------------")
(format t "~%'twas a stupendously beautiful ~A"
subject)
(format t "~%upon my snow-covered porch")
(format t "~%such a lovely lovely ~A" subject)
(format t "~%I blasted it with my torch."))
=> MAKE-POEM
(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 :subject 'cat)
SPRING, by Lulu Lispy
--------------------------
'twas a stupendously beautiful CAT
upon my snow-covered porch
such a lovely lovely CAT
I blasted it with my torch.
=> NIL

Note that the keyword must be provided. This causes an


error:
(make-poem 'spring 'cat)
> Error: Incorrect keyword arguments in (CAT) .

14
Function Parameters: (continued)
&keyword

The nifty thing about keyword parameters is that you can


specify any subset of a function's arguments, in any order:
(defun make-poem (title &key
(subject 'flower)
(location 'porch)
(blaster 'torch)
(covering 'snow))
(format t "~%~A, by Lulu Lispy" title)
(format t "~%--------------------------")
(format t "~%'twas a stupendously beautiful ~A"
subject)
(format t "~%upon my ~A-covered ~A"
covering location)
(format t "~%such a lovely lovely ~A" subject)
(format t "~%I blasted it with my ~A." blaster))
=> MAKE-POEM

(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

&keyword parameters can also be given SUPPLIED-P vari-


ables, and they may be combined with &optional and &rest
parameters.
Note that many built-in functions use &keyword parameters
for optional features. For example, MEMBER has 3 key-
word parameters: TEST, TEST-NOT, and KEY. (It sounds
funny to have a &keyword parameter called KEY, but that's
just a coincidence.) Here are some examples:
(member 'prune '(raisin prune cheese))
=> (PRUNE CHEESE)
(defun young-version (food)
(case food
(raisin 'grape)
(prune 'plum)
(cheese 'milk)))
=> YOUNG-VERSION
(young-version 'prune)
=> PLUM
(member 'plum '(raisin prune cheese))
=> NIL
(member 'plum '(raisin prune cheese)
:key #'young-version)
=> (PRUNE CHEESE)
(member 'green '((blue sea)(green grass)
(yellow teeth)))
=> NIL
(member 'green '((blue sea)(green grass)
(yellow teeth))
:key #'car)
=> ((GREEN GRASS) (YELLOW TEETH))

17
Function Parameters: (continued)
&keyword

NOTE: KEY defaults to the function IDENTITY.


(member '(the jetsons)
'((the flintstones)(the jetsons)
(the honeymooners)))
=> NIL
(member '(the jetsons)
'((the flintstones)(the jetsons)
(the honeymooners))
:test #'equalp)
=> ((THE JETSONS) (THE HONEYMOONERS))

NOTE: TEST defaults to the function EQL.


TEST-NOT is not as obvious, but it is sometimes handy:
(member 4 '(4 4 4 4 4 4 23 5 5 5 5 5 5))
=> (4 4 4 4 4 4 23 5 5 5 5 5 5)
(member 4 '(4 4 4 4 4 4 23 5 5 5 5 5 5)
:test-not #'eq)
=> (23 5 5 5 5 5 5)

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)

Note: do/do* anal. to let/let*.

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

 Can read Steele 90 for more details.


 Can also use COLLECT:
(loop for i in '(bird 3 (l) horse) when (symbolp i) collect i)
=> (bird horse)

20
Defmacro
 (defmacro macro-name (parameter ...) body ...)
 Compare: (defun fn-name (parameter ...) body ...)
 Similar structure, but a macro is di erent: 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 de ne a macro, you are actually de ning
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))

Note: list* appends last arg to end of list of other args.

 (macroexpand '(while (< i 10) (print (* i i)) (setf i (+ i 1))))

 Expands to:
(loop (unless (< i 10) (return nil)) (print (* i i)) (setf i (+ i 1)))

 How do we know when something is a macro?


(1) Are args quoted? (If not then probably macro, but
not very reliable.)
(2) Macro-function
(3) Macroexpand: if returns same thing, then not macro:
(macroexpand '(cons 'a 'b)): (cons 'a 'b)

22
DEFMACRO: More details
DEFMACRO symbol lambda-list {declaration | doc-string}* {form}*
[Macro]

DEFMACRO constructs a global macro de nition, binds it


to symbol, marks symbol as a macro, and returns symbol.
Defmacro is the macro equivalent of defun.
(defvar *people-ages*
'((sally 22) (john 21) (tina 56)
(eugene 43) (wendy 11) (sam 1)))
=> *PEOPLE-AGES*

(defun get-age (name &optional (people-ages *people-ages*))


(cond ((null people-ages) nil)
((eq name (car (car people-ages)))
(car (cdr (car people-ages))))
(t (get-age name (cdr people-ages)))))
=> GET-AGE

(get-age 'tina)
=> 56

(get-age tina)
> Error: Unbound variable: TINA

(defmacro age (name)


(list 'get-age
(list 'quote name)))
=> AGE

(age tina)
=> 56

(macroexpand '(age tina))


=> (GET-AGE 'TINA)
23
DEFMACRO: More details (continued)
Macros have more interesting applications. They are often
used to extend the syntax of lisp.
(defmacro while (test &rest body)
(append (list 'do nil (list (list 'not test)))
body))
=> WHILE

(let ((n 1))


(while (< n 10)
(print n)
(setq n (+ n 1))))
1
2
3
4
5
6
7
8
9
=> NIL

(macroexpand-1 '(while (< n 10)


(print n)
(setq n (+ n 1))))
=> (DO () ((NOT (< N 10))) (PRINT N) (SETQ N (+ N 1)))

24
Backquote/Comma
 Hardest part about de ning 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)

 Backquote useful for other things (not always used in


macro):
(list (list x)) where x = (b c d) can be speci ed as:
`((,x)) ! (((b c d))).

 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)

 (defmacro mysetq (a e) `(set (quote ,a) ,e))


(mysetq x 3) ! (set (quote x) 3) equiv to (setq x 3)

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))

 COMMA-ATSIGN (,@) within a backquote \splices" its


list-valued result into the larger list: `(a ,@x) where x is (b
c d) ! (a b c d)

(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))

The backquote syntax allows us to write a much cleaner


WHILE macro.
(let ((n 1))
(while (< n 10)
(print n)
(setq n (+ n 1))))
1
2
3
4
5
6
7
8
9
=> NIL
(macroexpand-1 '(while (< n 10)
(print n)
(setq n (+ n 1))))
=> (DO () ((NOT (< N 10))) (PRINT N) (SETQ N (+ N 1)))

The function MACROEXPAND-1 can be used to expand


only one level of macro de nitions:
MACROEXPAND-1 form &optional environment
[Function]

MACROEXPAND-1 returns the result of expanding form


once within environment. (In e ect, macroexpand simply
calls macroexpand-1 repeatedly until no more expansion is
possible.)
27
Macro Tips
 Note: have to be careful not to evaluate an expression
more than once:
(defmacro foo (element) `(unless (null ,element) (print ,element)))

 Suppose element = (cons 'a '(b))? Don't want to eval-


uate this twice. Better to use LET:
(defmacro foo (element)
`(let ((elt ,element))
(unless (null elt) (print elt))))

 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))))

The solution is gensym:


GENSYM &optional string-or-number
[Function]

GENSYM creates and returns a unique uninterned symbol.


If string-or-number is given, it will be used in the name of
the new symbol.

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

But overuse of macros can cause problems:


(defun age-twice (name)
(list (age name) (age name)))
=> AGE-TWICE
(age-twice tina)
> Error: Unbound variable: TINA
(age-twice 'tina)
=> (NIL NIL)
(defun age-twice (name)
(list (get-age name) (get-age name)))
=> AGE-TWICE
(age-twice 'tina)
=> (56 56)

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))

depends on the binding of y where foo is called. Graham notes


that \This kind of lexical intercourse is usually viewed more as a
source of contagion than a source of pleasure."
 Reduction of function call overheads|there is no overhead asso-
ciated with macro calls. By runtime, the macro call has been
replaced by its expansion.
 Computation at compile time|you can sometimes move a *lot* of
computation to compile-time, reducing the runtime computation
to a minimum.
 Integration with Lisp|sometimes you can write macros that trans-
form problems, in a higher-level language of your own design, into
simple Lisp.

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 de nitions 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) ...)

 Dotted pair form of ALIST can be built with ACONS:


(acons 'z 3 '((y . 2) ( x . 1))) =>
((z . 3) (y . 2) ( x . 1))

 Can use to build a table (with or without dot):


(setf words '((one uno) (two dos) (three tres) (four
cuatro) ( ve cinco)))
 Use assoc to access:
(assoc 'one words :test #'eq) ! (one uno)
Allows you to index by key. (Note, use :TEST.)
 (defun translate (x) (second (assoc x words :test #'eq)))
(translate 'one)
What is the result?
! uno

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)

 You might nd cons cells easier to work with (as in the


book).

27
Defstruct (continued)
 Constructor, accessor, assignment automatically de ned
when data structure is de ned.
 DEFSTRUCT name-and-options [doc-string]
{slot-description}*
[Macro]

DEFSTRUCT de nes a new structure, according to name-and-


options, with slots described by the slot-descriptions.

 (defstruct elephant name (color 'grey) (nose 'trunk) (isa 'mammal))


!
ELEPHANT

 Constructor: (setf x (make-elephant :name 'clyde :color 'white))


#s(ELEPHANT :NAME CLYDE :COLOR WHITE :NOSE TRUNK :ISA MAMMAL)

 Accessor: (elephant-color x) ! WHITE

 Assignment: (setf (elephant-color x) 'grey)


Modi es color slot.
 (typep x 'elephant) ! T
 (elephant-p x) ! T

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)))

We could then write some functions to access elements of


a PERSON:
(defun person-name (p) (first p))
=> PERSON-NAME
(defun person-age (p) (second p))
=> PERSON-AGE
(defun person-shoe-size (p) (third p))
=> PERSON-SHOE-SIZE
(defun person-parents (p) (fourth p))
=> PERSON-PARENTS
(defun person-children (p) (fifth p))
=> PERSON-CHILDREN
(person-name person-1)
=> (BETTY BYTE)
(person-shoe-size person-1)
=> 4
(person-children person-1)
=> ((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)

We could also write functions to DESTRUCTIVELY modify


PERSONs. This requires the use of SETF:
(defun set-person-name (p new-name)
(setf (car p) new-name))
=> SET-PERSON-NAME
(set-person-name person-1 '(boxcar willie))
=> (BOXCAR WILLIE)
person-1
=> ((BOXCAR WILLIE) 22 4 ((BOOPSIE BYTE) (BARNEY BYTE))
((BOBBY BYTE) (BELINDA BYTE)))

5
Defstruct (continued)
DEFSTRUCT makes life much easier:
(defstruct person
(name '(ordinary jo))
(age 20)
(shoe-size 5)
(parents nil)
(children nil))
=> PERSON

This AUTOMATICALLY does the following:


 Creates a new data type called PERSON, which is a
STRUCTURE with 5 SLOTS.
 De nes 5 new functions PERSON-NAME, PERSON-
AGE, PERSON-SHOE-SIZE, PERSON-PARENTS, and
PERSON-CHILDREN, each of which takes a PERSON
as its argument and returns the associated slot of that
PERSON.
 De nes a predicate PERSON-P of one argument that
is true if its argument is a PERSON.
 De nes a function MAKE-PERSON with 5 keyword ar-
guments corresponding to the slots of PERSONs.
Note: (1) Structures are not lists; (2) Structures may
be destructively modi ed using SETF and the auto-
matically de ned access functions; (3) DEFSTRUCT
has many more features that we'll ignore for now.

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)))

S-ar putea să vă placă și