introedge.cs.drexel.edu/regli/classes/lisp_papers/lisp-slides.pdfintro to lisp lisp = list pro ......

139

Upload: vuongphuc

Post on 19-May-2018

252 views

Category:

Documents


1 download

TRANSCRIPT

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

� Guy Steele (1990): VERY useful reference.

12

Why Lisp?

First, Lisp is much more exible than most languages. Usershave such total control over what goes on that if they donot like the syntax of the language, they may change it tosuit themselves. Suppose that you do not like the methodfor de�ning programs in, say, Fortran, can you do anythingabout it? Short of switching to another language the answeris clearly no. Indeed the very idea seems absurd. But not toLisp users. (Spector, 1995, PC.)

...The second reason for choosing lisp is the way in whichLisp 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? Whyis it a good language for AI applications? There are at leastseven 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 ofthose 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 fordot notation and special characters | we'll get to specialchars later).

S-expression

atom list dotted pair

symbol,number,string,character,array

(a b 3) (3 . a)(a . (b . (3 . nil)))

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 avalue 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:

atomturkey14923turkeys*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 numberElse 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 afunction, and apply that function to the values of the remainingelements 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 thatvalue)| 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 toevaluate, the �rst atom has a value associated with itwhich 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 eltshould return fcn body, rest return args); (2) apply fcnfrom 1st atom to rest.

� Important Note: use ' to indicate \the S-expr stated"rather than the value of that S-expr. (e.g., 'X returnsX, whereas X returns the value stored in X).

� We'll run through what a sample session looks like in aminute.

19

Quote symbol

Examples:

| '2 ! 2| 2 ! 2| 'John ! John| John ! ERROR: not a bound variable

� Quote: use ' to indicate \the S-expression stated" ratherthan 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 afunction 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 anddata stored the same way).

� First step: forget conventional pgming langs w/ whichyou are familiar (else confusion).

� Three notations for Lisp data structures: (1) paren/list(printed) (2) dot (printed only if req; list notation isabbrev. for this); (3) pictorial or graphic or box/cellnotation (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 thesetwo easily.

24

Pictorial Notation: used to describe what goes on in

Memory

CONS Cell: A cell in memory (w/ left and right halves)

A B

BA(CONS ’A ’B)

A

CAR = A

B

CDR = B

Note: No SIDE EFFECT (important later)

Note: combinations ofCAR/CDR (up to 4), e.g.,CADR = (CAR (CDR ..))

Also: FIRST, SECOND, ... TENTH(car,cadr,caddr, ...)

These are off in memory

BA

(CONS ’A ’B)

A

(LIST ’A ’B) (APPEND ’(A) ’(B))

nil

B

Difference Between: CONS, LIST, APPEND

All args must be lists!Last ptr of each eltpoints to next elt(A . B) (A B)

Adds one mem cell Adds one mem cellfor each elt

A

nil

B

(A B)Note: can have(LIST ’A) -> (A)

Note: can have(CONS ’A NIL)-> (A)

same

Suppose:X = (A)Y = (B) What is (CONS X Y)? What is (LIST X Y)?

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

nil

B

A nil

nil

BA nil nil

25

Quote (continued from last time)

The quote function inhibits the evaluation of its argument.It returns its argument literally. If the variable barney hasthe value 22, then:

Evaluatingbarneyyields22

Evaluating(quote barney)yieldsbarney

Evaluating(car (quote (betty bambam)))yieldsbetty

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) isthe 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 thelist.

(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 itscomponent S-expressions through some combination of carand 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)liestruth)))

=> 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-nilatom

(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 calledsetq)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 ! rplacedwith setf.

� A generalized var refers to any place a ptr may be stored.

3 nil

A

3A

"A has the value (3)"

"A has the value 3"

� Other places a ptr can be stored?

� car/cdr of cons cell

� Asst ! replacing one ptr with another

3 nil

A

(setf A ’(4))

Can use an accessorsuch as CAR, CDR,FIRST, etc.

3 nil

A

4 nil

4

(setf (car A) 4)

"A has the value (4)"

7

More about setting variables

� Lisp doesn't need type declaration because everythingis a pointer (i.e., the values of symbols have types as-sociated w/ them; atoms, lists, integers, etc.)

� Typical memory org:

Symbols

Cons nodes

Strings

Integers

Lisp system

� Symbol: \indivisible atoms" actually have parts.Typically each atom is a table:

Package

Value

Function

Prop List

NameX "X" (string -- not same as symbol)

(if nothing here, atom is unbound)

(function definition)

� 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) ! 3NOT 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 onthe y; in the \old" days, this was considered the \key" toLisp's exibilityNOT 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 '(setfx 3) is passed into a fn to be evaluated, then probably best thingto 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 ableto 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 youshould know it is there, and that sometimes there's no choice.

9

Comments

Common Lisp provides 2 ways to comment your code:

#| thiscouldbe areally 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 afunction (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 (thefunction).

� 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 whichequalp is the most general (see CLtL2 p. 103).

And, or, and not allow the combination of predicates intocomplex 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 (forstrings).

� 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

A B A

nil

So far: S-exprs drawn so thatsymbol referenced more than oncedrawn multiple times in memory

A

B

nil

What’s really going on?

EQ

� 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 somesymbol actually point to the UNIQUE point in memthat the symbol happens to be stored physically. Thisway, any info stored w/ it (e.g., its value) is accessiblefrom every ref.

� Anytime LISP sees new symbol, it adds the symbol toits 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 anelse 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!

(if C E1 E2)

E1C Y

E2

N

(when C E1 E2)

C E1 E2...Y

(cond (C1 ( E11 E21...))

(C2 (E21 E22 ...)) ...

(Cn (En1 En2 ...)))

C1E11 E12...Y

C1 E11 E12...Y

CnEn1 En2...Y

N

� COND: Powerful multiway branching construct; Arbitrary numberof args (called clauses) Note: general form ! can have more thanone 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 tobe 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 result

n2 ...)))

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), (ort nil nil nil)

20

IF vs. COND

The IF special form is a special case of COND: IF testformthenform [elseform]

Evaluates testform. If the result is true, evaluates thenformand returns the result; if the result is nil, evaluates elseformand 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 restrictedto 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 allforms but the last are discarded; the value of the last formis returned.

(if (< temperature 32)(progn (print '(yowch -- it is cold!))

(print '(i will play god andchange 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 theargument 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 functionabove. The string is not obligatory and does not a�ectthe execution of the function, but it provides informationabout 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 thattakes 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 theirsum:

(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 LISTfunction:

(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-PhraseNoun-Phrase => Article + NounVerb-Phrase => Verb + Noun-PhraseArticle => 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-PhraseTo get a Noun-Phrase, append an Article and a NounChoose "the" for the ArticleChoose "man" for the Noun

The resulting Noun-Phrase is "the man"To get a Verb-Phrase, append a Verb and a Noun-PhraseChoose "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-phrasearticle 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 length0 and any other list has a length which is one more thanthe 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 ())

1

2

3

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 e�cient 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-conswe 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 �rstargument 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 returnsthe 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. Forexample:

(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 providinga 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 thetask to solve the original problem.

� Identifying the "grounding" situations in which the taskcan be accomplished directly.

� Specifying checks for these grounding cases that will beexamined 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 2FACTORIAL returned 6

FACTORIAL returned 24FACTORIAL 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 boundto the integers between zero and countform. Upon completion,resultform is evaluated, and the value is returned. If resultform isomitted, the result is nil.

(dotimes (n 20 'spumoni) (print n))012345678910111213141516171819=> 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-thinganother-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 oncefor every element in the list. On each iteration, var is bound tosuccessive elements of the list. Upon completion, resultform isevaluated, and the value is returned. If resultform is omitted, theresult 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, lambdaexpressions keywords (e.g., :TEST).

� Note: only functions may be quoted (not macros orspecial forms).

� More e�cient; means something to compiler: go tocompiled code, not through symbol to �nd functiondefn.

� Analogous to quote. Use any time a function is speci-�ed.

� Mapcar: Expects an n-ary fn as 2st arg, followed byn lists. It applies the fn to the arg list obtained bycollecting 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, andC.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 secondarg, 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 namein 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 rootsin lambda calculus of Church

� (lambda (parameters ...) body ...)

� A lambda expr is a nonatomic name for a fn, just asappend 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. Whyare they useful?(1) It's a pain to think up names and to clutter up aprogram with lots of functions that are only used verylocally;(2) MORE IMPORTANT: can create new functions atrun 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 testformthenform [elseform]

Evaluates testform. If the result is true, evaluates thenformand returns the result; if the result is nil, evaluates elseformand 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 restrictedto 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 allforms but the last are discarded; the value of the last formis returned.

(if (< temperature 32)(progn (print '(yowch -- it is cold!))

(print '(i will play god andchange 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 ofprevious 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 alist

(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 ofprevious 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-conswe 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 �rstargument 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 returnsthe 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. Forexample:

(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 providinga 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 ofprevious 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 thetask to solve the original problem.

� Identifying the "grounding" situations in which the taskcan be accomplished directly.

� Specifying checks for these grounding cases that will beexamined 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 2FACTORIAL returned 6

FACTORIAL returned 24FACTORIAL returned 120=> 120

10

Factorial and Length Revisited (slide 42 of previouslecture)

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 DOLISTlast time. Another is DOTIMES.

DOTIMES (var countform [resultform]) {declaration}* {tag | statement}*[Macro]

Executes forms countform times. On successive executions, var is boundto the integers between zero and countform. Upon completion,resultform is evaluated, and the value is returned. If resultform isomitted, the result is nil.

(dotimes (n 20 'spumoni) (print n))012345678910111213141516171819=> 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-thinganother-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 withdefvar 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 returnsvariable-name. If initial-value is given, variable-name is ini-tialized to the result of evaluating it unless variable-namealready has a value. If initial-name is not used, it is notevaluated. The macro defvar only has an e�ect the �rsttime 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 specialvariable, sets it to the value of evaluating initial-value, aform, and returns variable-name. Documentation may beprovided 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 andDEFVAR:

� If global initialized using DEFPARAMETER in a �le, then eachtime �le is loaded, the global will be reset to the initial value.

� If global initialized using DEFVAR in a �le, then ONLY THE FIRSTTIME the �le is loaded will the global set to the initial value (noton subsequent times).

� Why is this the case? DEFVAR doesn't reinitialize a var becauseit assumes this val is in ux and may change many times over; ittakes the initial val as the TRUE initial val and won't reinitializew/o an explicit SETF.

� DEFPARAMETER is di�erent; it assumes that if you're giving itan initial value that will not be changed by the program. So itWILL 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) andevaluates forms in the resulting environment. Returns thevalue 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 performedbefore 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.) Socannot access vars not declared locally (unless specialglobal), even if de�ned inside of calling fn. (In dynamicscoping, value of var determined by value inside localenv OR that of most recent caller containing the var.)

� Let is the most common way of introducing vars thatare not parameters of fns; resist temptation to use avar w/o introducing it (can actually do this in Lisp).

� Let introduces a new local variable and binds it to avalue. General form: (let ((var value) ...) body-containing-vars)

� Equiv to: ((lambda (var ...) body-containing-vars) value)

� Let used to initialize LOCALS; Setf used to initializeGLOBALS (or defparameter, defconstant, defvar).

� You need to know that symbols (name for var or fn)are NOT vars (depends on context). The symbol valuedoes NOT change when fn is called w/ a particularsymbol 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) pushedon stack; x bound to particular value, but symbol-valuelooks 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 toallocate 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)

=> 11

� 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 thatis 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 aroundwith us; has its own local environment. A lexical closureis a function and its environment.

� Any function (or lambda form) de�ned in a non-nulllexical environment|that is, not at the top level|isactually a closure.

� What does \free variable" mean? Example: variable nin (lambda (x) ... n ...)

� Normally: by lexical scoping rules, refers to value ofglobal symbol taken at time the lambda expression isAPPLIED: the value of n will be whatever the value ofn is when this lambda expr is applied.

� Contrast: lexical closure; refers to value of that symboltaken at time the function is created (i.e., when the clo-sure is PERFORMED). The value PERSISTS betweenfn calls.

20

Lexical Closures

The existence of variables that are local to a fn but whichpersist between calls is useful for creating generators. Agenerator is a function that produces a sequence of relatedvalues.

(defun make-even (seed) #'(lambda nil (setf seed (+ seed 2))))=> MAKE-EVEN

What is the free var above? SEED. It initially gets its valuewhen 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 followinggives us many of the advantages of global variables withoutthe 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 callthem 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 setto 3 internally). Suppose Y is set to this result. We cannow 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 tochange 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; butnot 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 storedin string)

� Array: collection of integers, chars, symbols, s-expressions.

� String: collection of chars (one-dimensional array orvector)

� Tests: characterp, arrayp, stringpNote: 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 stronglytyped; can be mixed]

Dimensions:

0 1 2 3 <--- start w/ 00 ............ 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; automaticallycounts 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 e�ciently and cre-

ate e�cient 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 e�cient: 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 e�cient 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'llsit there until user types in.

� (read-line stream) is a variant of read: reads a wholeline 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 andpointer of next char); we'll learn how to catch thesevalues shortly.

Note: This is NOT really correct! Need to specify twooptional 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 Bdepends on A, need to be careful about order of com-pilation and loading:

Amacrodefns

Buses

macros

compileand loadthis first

thencompileand loadthis

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-PhraseNoun-Phrase => Article + NounVerb-Phrase => Verb + Noun-PhraseArticle => 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-PhraseTo get a Noun-Phrase, append an Article and a NounChoose "the" for the ArticleChoose "man" for the Noun

The resulting Noun-Phrase is "the man"To get a Verb-Phrase, append a Verb and a Noun-PhraseChoose "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-phrasearticle 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 onepiece 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 outa function; close it o� at the end. Now we can pluckout 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 foroutput to the listener. The second argument to FORMATshould 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 ona 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 sidee�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 ~%, whichmeans to output a newline character.

(format t "~%Bop bop~% shoo~% bob~% bop bop bam")Bop bopshoobobbop bop bam

=> NIL

Even ~% has more options { for example, a number betweenthe ~ 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 \goget the next lisp object from the argument list and print ithere."

(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 widerange 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 5Here's n:3214241.=> NIL

7

Output in Lisp: Examples (continued)

(format t "This year:~@R." 1995) ;; romanThis year:MCMXCV.=> NIL(format t "Here's n:~R." n) ;; englishHere'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 thisexample 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 iterationwithin 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)

=> HMMMmy-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 parametersby putting &optional, followed by parameter names, in theparameter 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: &optional (continued)

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: &optional (continued)

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 couldhave 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 ALLremaining 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-FISHand then you had HORSERADISH=> BURP(eat 'gefilte-fish 'horseradish 'pickles)you started with GEFILTE-FISHand then you had HORSERADISHand then you had PICKLES=> BURP(eat 'gefilte-fish 'horseradish 'pickles 'olives)you started with GEFILTE-FISHand then you had HORSERADISHand then you had PICKLESand then you had OLIVES=> BURP

13

Function Parameters: &keyword

The &keywords facility allows us to specify NAMED optionalparameters.

(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 FLOWERupon my snow-covered porchsuch a lovely lovely FLOWERI blasted it with my torch.=> NIL(make-poem 'spring :subject 'cat)SPRING, by Lulu Lispy--------------------------'twas a stupendously beautiful CATupon my snow-covered porchsuch a lovely lovely CATI blasted it with my torch.=> NIL

Note that the keyword must be provided. This causes anerror:

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

14

Function Parameters: &keyword (continued)

The nifty thing about keyword parameters is that you canspecify 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 FLOWERupon my SNOW-covered PORCHsuch a lovely lovely FLOWERI blasted it with my TORCH.=> NIL

(make-poem 'spring:location 'veranda:blaster 'power-sander)

SPRING, by Lulu Lispy--------------------------'twas a stupendously beautiful FLOWERupon my SNOW-covered VERANDAsuch a lovely lovely FLOWERI 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 PUMPKINupon my SNOW-covered VERANDAsuch a lovely lovely PUMPKINI 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-ALIENupon my GLITTER-covered VERANDAsuch a lovely lovely SPACE-ALIENI blasted it with my POWER-SANDER.=> NIL

16

Function Parameters: &keyword (continued)

&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 parametersfor optional features. For example, MEMBER has 3 key-word parameters: TEST, TEST-NOT, and KEY. (It soundsfunny to have a &keyword parameter called KEY, but that'sjust 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: &keyword (continued)

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 thosedescribed in Tanimoto, i.e., \loop".

Note: You should not be using loop in lieu of sequenceoperations. (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 loopof any kind (do, dolist, etc.) with the value x. (We'lllook 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 allowscounting, 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 iterationdo (incf len) ; increment LEN by 1finally (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 compilerexpands 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 careabout looking at.

� (1) Decide what the \nice" syntax is. (2) Decide whatthe \ugly" code is.

� Why used?(1) E�ciency: expanded in-line at compile time |More

e�cient 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 formevaln takes place.

� Use macroexpand to see what a macro expands out to.

� Every time you de�ne a macro, you are actually de�ninga new language and writing a compiler for it! So don'ttake 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, butnot 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 oftenused 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

78

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 thatis 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 exceptthat any expression preceded by a comma is consideredto 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 inmacro):(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" itslist-valued result into the larger list: `(a ,@x) where x is (bc 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 ENOUGHIT WILL FALL OVER -- BUT NOW I KNOW BETTER)

26

Cleaner Macros

Using the backquote, how would we rewrite WHILE withoutusing 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 cleanerWHILE macro.

(let ((n 1))(while (< n 10)

(print n)(setq n (+ n 1))))

123

45

67

89=> 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 expandonly 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 expressionmore 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 picksapart 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 themacro call in the lexical scope of the call|hence it can use andchange 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 notesthat \This kind of lexical intercourse is usually viewed more as asource 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 beenreplaced by its expansion.

� Computation at compile time|you can sometimes move a *lot* ofcomputation to compile-time, reducing the runtime computationto a minimum.

� Integration with Lisp|sometimes you can write macros that trans-form problems, in a higher-level language of your own design, intosimple 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, youcan't trace them (because they're gone by runtime) andstack backtraces are less informative when you use lotsof macros.

� Recursion|an expansion function may be recursive, butthe expansion itself may not be.

37

Alists, Assoc

� The matching functions in chapter 3 use an \associationlist" (ALIST) for representing the associations of valueswith variables.

� ALIST: A list of lists in which each list is called an entryand the car of each entry is the key. In the book, eachlist 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) (fourcuatro) (�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) ! unWhat 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 thebook).

27

Defstruct (continued)

� Constructor, accessor, assignment automatically de�nedwhen 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-sizeparents children) where:

name is a list of atomsage is an integershoe-size is an integerparents is a list of nameschildren is a list of names

For example:

(setq person-1'((betty byte)

224((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 ofa 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 tomake 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 modifyPERSONs. 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 aSTRUCTURE with 5 SLOTS.

� De�nes 5 new functions PERSON-NAME, PERSON-AGE, PERSON-SHOE-SIZE, PERSON-PARENTS, andPERSON-CHILDREN, each of which takes a PERSONas its argument and returns the associated slot of thatPERSON.

� De�nes a predicate PERSON-P of one argument thatis 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 maybe destructively modi�ed using SETF and the auto-matically de�ned access functions; (3) DEFSTRUCThas 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)

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

7