The second reason for choosing lisp is the way in which. Lisp in oriented toward
the manipulation of symbols as op- ...... Programming Example continued.
Intro to Lisp
LISP = LISt Processing
Originated w/ McCarthy (1959)
Implemented Church's lambda-calculus (recursive function theory)
Manipulation of symbolic information
The initial Lisp was impractical (no iteration)
Many versions: Franz, Mac, Inter, Common (now the standard)
Guy Steele (1990): VERY useful reference.
12
Why Lisp?
First, Lisp is much more exible than most languages. Users have such total control over what goes on that if they do not like the syntax of the language, they may change it to suit themselves. Suppose that you do not like the method for de ning programs in, say, Fortran, can you do anything about it? Short of switching to another language the answer is clearly no. Indeed the very idea seems absurd. But not to Lisp users. (Spector, 1995, PC.) ...The second reason for choosing lisp is the way in which Lisp in oriented toward the manipulation of symbols as opposed to, say, numbers.Norvig, (PoAIP, p. 25): What is it that sets Lisp apart from other languages? Why is it a good language for AI applications? There are at least seven important factors (from Norvig):
Built-in Support for Lists
Automatic Storage Management
Dynamic Typing
First-Class Functions
Uniform Syntax
Interactive Environment
Extensibility 13
Characteristics of Lisp
Interactive (somewhat super cially), Recursive, Very simple syntax (does automatic memory mgmt), variables not explicitly typed, may be interpreted or compiled.
Oriented toward: (1) non-numeric symbol manipulation; (2) structure building and modi cation.
Programs and data have same form
Lisp is the defacto standard language in AI
14
Some dierences between Lisp and Conventional Languages
Symbolic Processing
Structure Building and Modi cation
Programs and data have same form
| Not only assigns values to symbols, but also permits dynamic construction and dissection of data structures | Can construct pgms on the y and even use them
Additional dierences (noted in Norvig)
Many langs distinguish between stmts and expressions. | Statements have eects (x = 2 + 2). | Expressions return values (2 + 2).
In Lisp, ALL every expression returns a value; Some of those expressions also have side eects.
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 comments.)
15
Syntax of Lisp
Lisp is based on symbolic expressions or S-expressions:
| An atom is an S-expression: (a) symbol (b) non-symbol (number, string, array); Examples: X, JOHN, SETQ, 7.2, "John" | A list is an S-expression: (S-expr S-expr ); Examples: (+ 1 2), (A (B C)) | Note: case insensitive :::
THAT'S ALL THERE IS TO THE SYNTAX!!! (except for dot notation and special characters | we'll get to special chars later). S-expression atom
list (a b 3)
symbol, number, string, character, array
dotted pair (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 (convenient).
Note: (nil . nil) = (() . ()) = (() . nil) = (()) 6= nil 16
Basic Idea of Using Lisp
Sit in front of terminal
Enter Lisp (interactive)
Now you're in a read-eval-print loop
EVAL = give me an expression to evaluate; returns a value for an s-expr. (analogy: words in sentences vs. meaning)
Lisp reads the S-expr, evaluates the S-expr, then prints the result of the evaluation.
17
Atoms:
Atoms and Lists
atom turkey 1492 3turkeys *abc
Lists: (atom) (atom turkey) (atom 1492 ocean blue) (an-atom (a list inside) and more atoms) (a list containing 5 atoms) (a list containing 8 atoms and a list (that contains 4 atoms)) (((a list containing a list containing a list of 11 atoms))) () (a list that ends with the null list ()) (not a list (not) a list )not again
The Lisp evaluation rule: To evaluate an atom: If it is a numeric atom, the value is the number Else it must be a variable symbol; so the value is the value of the variable To evaluate a list: Assume the first item of the list is a function, and apply that function to the values of the remaining elements of the list.
18
Result of Evaluation: What is the value?
Number ! number itself (e.g., 7 ! 7)
String ! string itself (e.g., "hi" ! "hi")
Symbol ! value of symbol (e.g., X ! 5, if X has that value)
| Symbols like variables in most langs, have a value associated with them. Special: T, NIL
List ! normally function evaluation
| Normally write fcn as f(a,b, ,n) or in x a+b+ +n | In Lisp: put f inside left paren: (f a b n) :::
:::
:::
By convention, we assume that when given a list to evaluate, the rst atom has a value associated with it which is the body of a function. ([name of fn] [args]) | (+ 1 2) ! 3 | First elt of list is special: applied to rest of elts.
Evaluation of list: (1) eval each elt of list ( rst elt should return fcn body, rest return args); (2) apply fcn from 1st atom to rest.
Important Note: use ' to indicate \the S-expr stated" rather than the value of that S-expr. (e.g., 'X returns X, whereas X returns the value stored in X).
We'll run through what a sample session looks like in a minute. 19
Quote symbol
Examples: | '2 ! 2 | 2!2 | 'John ! John | John ! ERROR: not a bound variable
Quote: use ' to indicate \the S-expression stated" rather than the value of that S-expression.
Serves to BLOCK evaluation of following expression, returning it literally.
Example: '(Pat Kim) ! (PAT KIM) If we just had (Pat Kim), it would consider Pat as a function and apply it to the value of the expression Kim.
20
Sample Session Fire up Lisp ... (+ 1 1) => 2 (* 23 13) => 299 (+ 1 (* 23 13)) => 300 (+ (* 2 2) (- 10 9)) => 5 (* 2 2 2 2) => 16 (sqrt (* 2 2 2 2)) => 4 (/ 25 5) => 5 (/ 38 4) => 19/2 "Hello, world!" => "Hello, world!" 'x => X x => Error: Unbound variable X
; Can abort out of this (abort current ; computation and return to read-eval-print loop)
21
Sample Session (continued) (setf x 5) => 5
; Setf gives a value to a variable. ; effect and ALSO returns a value
(setf y (reverse '(+ a b))) => (B A +)
; Reverse reverses a list.
(setf l '(a b c d e)) => (A B C D E)
; l's value will now be a list.
(length l) => 5
; length --> gives length of a list
(append l '(f)) => (A B C D E F) (length l) => 5
It has a side
; append --> merges to lists together ; (Note use of quote; l not quoted)
; 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) => A
; (first l)
(cdr l) => (B C D E)
; (rest l)
(first (rest l)) => B (cons 'q l) => (Q A B C D E)
; MAIN CONSTRUCTOR used in Lisp.
22
Understanding LISP: Lisp Data Structures
Best way to understand Lisp: Develop an intuitive understanding of its data structures (remember: pgms and data stored the same way).
First step: forget conventional pgming langs w/ which you are familiar (else confusion).
Three notations for Lisp data structures: (1) paren/list (printed) (2) dot (printed only if req; list notation is abbrev. for this); (3) pictorial or graphic or box/cell notation (never printed; shows what's going on inside).
We will return to relate these three.
Note: parenthesized notation = surface appearance (typing in/out at terminal) whereas pictorial notation = what's happening below the surface (i.e., how the computer is really doing it).
Need to be able to switch back and forth between these two easily.
24
Pictorial Notation: used to describe what goes on in Memory Note: combinations of CAR/CDR (up to 4), e.g., CADR = (CAR (CDR ..))
CONS Cell: A cell in memory (w/ left and right halves)
A
B
A B (CONS ’A ’B)
Also: FIRST, SECOND, ... TENTH (car,cadr,caddr, ...)
A
B
CAR = A
CDR = B These are off in memory
Note: No SIDE EFFECT (important later)
Difference Between: CONS, LIST, APPEND nil A
B
A
(LIST ’A ’B)
Adds one mem cell
Adds one mem cell for each elt
(A . B)
A B (APPEND ’(A) ’(B))
B
(CONS ’A ’B)
All args must be lists! Last ptr of each elt points to next elt
(A B)
Note: can have (CONS ’A NIL)-> (A)
nil
(A B)
Note: can have (LIST ’A) -> (A) same
Suppose: X = (A) Y = (B) What is (CONS X Y)? What is (LIST X Y)?
nil
nil
B A
nil
(CONS X Y) = ((A) B)
A
nil
B
nil
(LIST X Y) = ((A) (B))
25
Quote (continued from last time)
The quote function inhibits the evaluation of its argument. It returns its argument literally. If the variable barney has the value 22, then: Evaluating barney yields 22 Evaluating (quote barney) yields barney Evaluating (car (quote (betty bambam))) yields betty
The single quote mark (') (it is actually an apostrophe) can be used as an abbreviation for (quote ...).
2
Taking lists apart (continued from last time)
The rst element of a list (be it an atom or another list) is the car of the list. (car '(alphabet soup)) => alphabet (car '((pickles beer) alphabet (soup))) =>(pickles beer) (car '(((miro (braque picasso))) leger)) =>((miro (braque picasso)))
Everything except the rst element of a list is the cdr of the list. (cdr '(alphabet soup)) =>(soup) (cdr '((pickles beer) alphabet (soup))) => (alphabet (soup)) (cdr '(((miro (braque picasso))) leger)) =>(leger)
Given a complex S-expression, you can obtain any of its component S-expressions through some combination of car and cdr. cadr is the car of the cdr: (cadr '(old mcdonald had a farm)) =>MCDONALD
cdar is the cdr of the car: => (cdar '((do you)(really think so?))) (YOU)
There are more variations, at least up to 4 a's or d's: (caddar '((deep (in the (nested) structure) lies truth))) => LIES
3
Taking lists apart (continued)
rst is a synonym for car
second is a synonym for cadr
third is a synonym for caddr .......
rest is a synonym for cdr
(setf x '(eenie meenie minie moe)) => (EENIE MEENIE MINIE MOE) (first x) => EENIE (third x) => MINIE (rest x) => (MEENIE MINIE MOE)
The null list: (cdr '(just-one-item)) => NIL '() => NIL
4
Putting lists together
cons builds lists.
(cons 'woof '(bow wow)) => (woof bow wow) (cons '(howl) '(at the moon)) => ((howl) at the moon)
You can create any list with cons. (cons 'naked-atom '()) => (NAKED-ATOM) (cons 'naked-atom nil) =>(NAKED-ATOM)
car, cdr, and cons are all non-destructive: (setf x '(the rain in spain)) =>(THE RAIN IN SPAIN) (car x) =>THE (cdr x) =>(RAIN IN SPAIN) (cons 'darn x) =>(DARN THE RAIN IN SPAIN) x =>(THE RAIN IN SPAIN)
5
Atoms and Dotted Pairs (continued from last time)
You get errors if you try to take the car or cdr of a non-nil atom (car 'foo) => > Error: Can't take CAR of FOO. (cdr 'foo) => > Error: Can't take CDR of FOO.
You get \funny dots" if you try to cons things onto non-lists (cons 'a 'b) => (A . B)
Although \dotted pairs" have their uses, we mostly won't use them beyond the rst problem set. That means that you're probably doing something wrong if you get dots in your answers.
6
Generalized assignment (setf)
Earlier dialects: not available (used something called setq) setq assigns a value to a symbol
(setq my-favorite-list '(fe fi fo fum)) => (fe fi fo fum) my-favorite-list => (fe fi fo fum)
setq is an exception to the normal evaluation rule; the rst argument is not evaluated. (q = quote) But setq cannot be used for generalized vars ! rplaced with setf. A generalized var refers to any place a ptr may be stored. "A has the value (3)"
A 3 A
nil "A has the value 3"
3
Other places a ptr can be stored? car/cdr of cons cell Asst ! replacing one ptr with another (setf A ’(4))
(setf (car A) 4)
A
A 3
nil
3 nil
"A has the value (4)"
4
4
nil
Can use an accessor such as CAR, CDR, FIRST, etc.
7
More about setting variables
Lisp doesn't need type declaration because everything is a pointer (i.e., the values of symbols have types associated w/ them; atoms, lists, integers, etc.)
Typical memory org: Lisp system Cons nodes Strings Integers Symbols
Symbol: \indivisible atoms" actually have parts. Typically each atom is a table: X
Name Value Function
"X" (string -- not same as symbol) (if nothing here, atom is unbound) (function definition)
Prop List Package
What does setf do? (setf x 7) Changes value slot of X's table to point to 7
Can access slots: (symbol-name 'reverse) ! "reverse" (symbol-function 'reverse) ! # 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 runtime: (setf a 3) (eval 'a) ! 3 NOT REALLY A GOOD IDEA: Can use symbol-value; no reason to use eval. { evaluating form that is constructed at runtime:
(eval (list args ...)) (eval (list 'setf arg1 arg2)) (where arg1='x and arg2=3) evaluates (setf x 3) Note: basically building a (trivial) function and running it on the y; in the \old" days, this was considered the \key" to Lisp's exibility NOT REALLY A GOOD IDEA: Use symbol-value again: (setf (symbol-value 'arg1) (symbol-value 'arg2)) But might be cases where don't have enough info; if the list '(setf x 3) is passed into a fn to be evaluated, then probably best thing to do is eval: (1) can't use funcall/apply cause setf is macro; (2) don't know enough about the args w/o extensive testing to be able to use (or in this case not use) symbol-value. But in general, we no longer need eval, really. Some people (e.g., Norvig) say you're doing something wrong if you use it. But you should know it is there, and that sometimes there's no choice. 9
Comments
Common Lisp provides 2 ways to comment your code: #| this could be a really big comment |# (car '(a b c)) ; this is a comment => A (cdr '(here is #| a list with a comment |# huh?)) => (IS HUH?)
10
Commonlisp Forms
Function: (+ 3 5) Can test with (functionp x)
Note: Arguments to a function can themselves be a function (nesting): (+ (* 3 5) 5)
Macro: (setf x 1) Can test with (macro-function x)
Note: violates eval'n rule: don't eval x, only eval 1.
Special form: (if test a b) Can test with (special-form x)
Two others we'll learn about later: let, let*
We'll initially concentrate on this rst type of form (the function).
We'll talk about macros in detail later.
11
Predicates
A Predicate is a function that is called to answer 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 evaluates 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 eect: (or (setf x ( ...)) (setf x 'fail)) | should use to test logical 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 ) 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 ( T (>= 23 23) => T (>= 100 1) => T (>= 1 100) => NIL (< 1 2 3 4 5 6 7 8 9) => T (< 1 2 3 4 5 6 7 8 7) => NIL
15
Equality Common lisp has a variety of Equality Predicates, of which
equalp is the most general (see CLtL2 p. 103). And, or, and not allow the combination of predicates into complex predicates. EQ and EQUAL { two most commonly used. EQ tests whether args eval to exact same Lisp obj EQUAL compares any two s-expressions (lists, strings, etc.) = used for numbers (must be same number) EQL: EQ or = EQUALP | same as EQUAL but disregards case (for strings). Others: tree-equal, char-equal, string-equal, string=, char=
Note: these are all a special case of equal; Tree-equal tests whether two trees are equal except that the leaf nodes are expected to be symbol atoms (not, eg., strings) Note: can specify :TEST such as string=.
x 'x '0 '(x) '"xy" '"Xy" '0 '0
y 'x '0 '(x) '"xy" '"xY" '0.0 '1
= err T err err err nil nil
eq T ? nil nil nil nil nil
eql T T nil nil nil nil nil
equal T T T T nil nil nil
equalp T T T T T T nil 16
Equality: Need to think about what's happening in memory So far: S-exprs drawn so that symbol referenced more than once drawn multiple times in memory
What’s really going on?
nil
nil A
B
A
B
EQ A
In reality, both pointers to A point to the SAME symbol (i.e., the same exact mem location)
EQ: tests for memory location exactly | therefore, atoms only!
LISP automatically makes sure that all refs to some symbol actually point to the UNIQUE point in mem that the symbol happens to be stored physically. This way, any info stored w/ it (e.g., its value) is accessible from every ref.
Anytime LISP sees new symbol, it adds the symbol to its storehouse of known atoms.
17
Sample Session: Equality (equalp 'foot 'foot) => T (equalp 'nose 'ear) => NIL (equalp (+ 22 33 44) (* 33 3)) => T (setq long-list '(1 2 3 4 can I show you out the door?)) => (1 2 3 4 CAN I SHOW YOU OUT THE DOOR?) (setq lucky-number 23) => 23 (or (equalp lucky-number (car long-list)) (equalp (* (car long-list) 2) (car (cdr long-list)))) => T (and (equalp lucky-number (car long-list)) (equalp (* (car long-list) 2) (car (cdr long-list)))) => NIL
18
Conditionals
IF: special form (if (= x 21) (print 'blackjack)) WHEN: macro; has same form: (when (= x 21) (print 'blackjack)) Dierence: IF has else part; should use if only when there is an else part ! (if z 'not-empty 'empty) [z is a boolean] | IF: most useful for 2-way fork where then/else are each 1 expr | WHEN: used for 1-way fork (then may be more than 1 expr) What if more than 2-way fork? Use COND! (cond (C1 ( E11 E21...)) (C2 (E21 E22 ...)) ... (Cn (En1 En2 ...)))
(if C E1 E2) C
Y
E1
E2
E11 E12...
C1
Y
E11 E12...
Cn
Y
N
(when C E1 E2) C
Y
C1
N
Y
E1 E2... En1 En2...
COND: Powerful multiway branching construct; Arbitrary number of args (called clauses) Note: general form ! can have more than one action. Each clause is a list of s-exprs Example: (Ci Ei1 Ei2 ... Eim). Value returned : Eim, where Ci is rst non-nil condition (No more Ci or Ei's are evaluated.) Convention for our class: last Ci is the constant T (guaranteed to be non-nil). 19
Conditionals: Examples (cond (x 'b) (y 'c) (t 'd))
What if x = 'a? (then returns b) What if x = nil, y = t? (then returns c) What if x = nil y = nil? (then returns d)
(cond (x (setf x 1) (+ x 2)) (y (setf y 2) (+ y 2)) (t (setf x 0) (setf y 0))) What if x = t? (then returns 3) What is x? (x = 1) What if x = nil, y = t? (then returns 4) What are x/y? (nil/2) What if x = nil y = nil? (then returns 0) What are x/y? (0/0
Note: could also do the following:
Other conditionals (chapter 3):
(cond (x (setf x 1) (+ x 2)) ((setf y 2))) If x is nil, then it'll execute the last statement and return it. (case (expression (match1 result11 result12 ...) (match2 result21 result22 ...) ... (matchn resultn1 resultn2 ...)))
Note: use \otherwise" as last match expression.
Can also use OR and AND as conditional control constructs (as we talked about earlier): (and nil t t t), (or t nil nil nil)
20
IF vs. COND
The IF special form is a special case of COND: IF testform thenform [elseform] Evaluates testform. If the result is true, evaluates thenform and returns the result; if the result is nil, evaluates elseform and returns the result. (if (> 100 23) 'sure-is 'sure-aint) => SURE-IS (if (member 'bog '(blig blog bog bumper)) (* 99 99) (sqrt -1)) => 9801 (if (member 'fog '(blig blog bog bumper)) (* 99 99) (sqrt -1)) => #c(0 1)
Note that the thenform and the elseform are both restricted to being single forms. In contrast, you can specify any number of forms in a cond clause: (setq temperature 8) => 8 (cond ((< temperature 32) (print '(yowch -- it is cold!)) (print '(i will play god and change that!)) (setq temperature 78)) (t (print '(well i guess it is not so bad)) (print '(where do you think we are? hawaii?)))) (YOWCH -- IT IS COLD!) (I WILL PLAY GOD AND CHANGE THAT!) => 78
21
PROGN
If you want multiple forms in an IF you can use a PROGN: Evaluates each form in order, left to right. The values of all forms but the last are discarded; the value of the last form is returned. (if (< temperature 32) (progn (print '(yowch -- it is cold!)) (print '(i will play god and change that!)) (setq temperature 78)) (progn (print '(well i guess it is not so bad)) (print '(where do you think we are? hawaii?)))) (WELL I GUESS IT IS NOT SO BAD) (WHERE DO YOU THINK WE ARE? HAWAII?) => (WHERE DO YOU THINK WE ARE? HAWAII?)
22
Sample Session: COND (setq x 23) => 23 (cond ((equalp x 1) '(it was one)) ((equalp x 2) '(it was two)) (t '(it was not one or two))) => (IT WAS NOT ONE OR TWO) (setq x 2) => 2 (cond ((equalp x 1) '(it was one)) ((equalp x 2) '(it was two)) (t '(it was not one or two))) => (IT WAS TWO)
23
De ning Functions
De ne Function = \Defun"
(defun function-name (parameter ...) function-body)
Function name = symbol; parameters usually symbols.
(defun rst-name (name) ( rst name))
( rst-name '(john q public)) ! JOHN
24
Sample Session: Defun
Here's a function that takes one argument and returns the argument plus 20: (defun add-20 (n) "returns n + 20" (+ n 20)) => ADD-20 (add-20 15) => 35
Note the use of a documentation string in the function above. The string is not obligatory and does not aect the execution of the function, but it provides information about the function which can be accessed through the builtin 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 # ... 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 semicolons. ;;; The function TEST runs a demonstration. (defun test () "Runs a test of SOLVE-PROBLEM." (setf x 'test-data) ; Assign X some test data. ;; Call the main function. (solve-problem x))
25
Sample Session: Defun (continued)
Here's a constant function that takes no arguments (defun dumb-function () '(you might as well use a variable for something like this)) => DUMB-FUNCTION (dumb-function) => (YOU MIGHT AS WELL USE A VARIABLE FOR SOMETHING LIKE THIS)
Here's a NON-constant function that takes no arguments (defun rand8 () (random 8)) => RAND8 (rand8) => 0 (rand8) => 6 (rand8) => 4
Note: \random" is a pseudo random number generator that takes a number and returns a value between zero (inclusive) and the number (exclusive). Here's a function using COND that returns an atom indicating whether its argument is even or odd (or not a number).
(defun even-or-odd? (n) "returns 'even if n is even, 'odd if n is odd, and 'neither if n is not an in (cond ((not (integerp n)) 'neither) ((evenp n) 'even) ((oddp n) 'odd))) => EVEN-OR-ODD? (even-or-odd? 24) => EVEN (even-or-odd? 25) => ODD (even-or-odd? '(swahili)) => NEITHER
26
Sample Session: Defun (continued)
Here's a function that takes two arguments and returns their sum: (defun my-sum (n1 n2) "silly substitute for +" (+ n1 n2)) => MY-SUM (my-sum 99 100) => 199
Here's a version of + that also sets the global variable *lastsum*: (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 descriptive list: (defun funny-arg-lister (arg1 arg2 arg3) (cons (cons 'arg1 (cons arg1 nil)) (cons (cons 'arg2 (cons arg2 nil)) (cons (cons 'arg3 (cons arg3 nil)) nil)))) => FUNNY-ARG-LISTER (funny-arg-lister 'a 'b '(x y z)) => ((ARG1 A) (ARG2 B) (ARG3 (X Y Z)))
27
Sample Session: Defun (continued)
Here's a cleaner version of funny-arg-lister using the LIST function: (defun funny-arg-lister (arg1 arg2 arg3) (list (list 'arg1 arg1) (list 'arg2 arg2) (list 'arg3 arg3))) => FUNNY-ARG-LISTER (funny-arg-lister 'a 'b '(x y z)) => ((ARG1 A) (ARG2 B) (ARG3 (X Y Z)))
Here's another simple function
(defun double-cons (one-thing another-thing) "Returns the result of consing one-thing onto another-thing twic (cons one-thing (cons one-thing another-thing))) => DOUBLE-CONS (double-cons 'hip '(hooray)) => (HIP HIP HOORAY) (double-cons 1 nil) => (1 1) (double-cons 1 (double-cons 1 nil)) => (1 1 1 1) (defun quadruple-cons (one-thing another-thing) "Returns the result of consing one-thing onto another-thing 4x" (double-cons one-thing (double-cons one-thing another-thing))) => QUADRUPLE-CONS (quadruple-cons 'um '(huh?)) => (UM UM UM UM HUH?)
28
Sample Session: Defun (continued)
Function arguments are LOCAL variables (setq shoes 'nikes) => NIKES
(defun run (shoes) (print (append '(i think i will take a trot in my) (list shoes))) (setq shoes (append '(old beat-up) (list shoes))) (print (append '(i think i will take a trot in my) shoes))) => RUN (run shoes) (I THINK I WILL TAKE A TROT IN MY NIKES) (I THINK I WILL TAKE A TROT IN MY OLD BEAT-UP NIKES) => (I THINK I WILL TAKE A TROT IN MY OLD BEAT-UP NIKES) (print 'foo) => FOO (defun run (shoes) ;; this version returns '(I RAN) (print (append '(i think i will take a trot in my) (list shoes))) (setq shoes (append '(old beat-up) (list shoes))) (print (append '(i think i will take a trot in my) shoes)) '(i ran)) => RUN (run shoes) (I THINK I WILL TAKE A TROT IN MY NIKES) (I THINK I WILL TAKE A TROT IN MY OLD BEAT-UP NIKES) => (I RAN) shoes => NIKES
29
Sample Session: Operations on Lists
The NTH function returns an element of a list by number (setq animals '(giraffe dog dinosaur bug big-bug big-hairy-bug)) => (GIRAFFE DOG DINOSAUR BUG BIG-BUG BIG-HAIRY-BUG) (nth 0 animals) => GIRAFFE (nth 1 animals) => DOG (nth 2 animals) => DINOSAUR (nth 3 animals) => BUG (nth 4 animals) => BIG-BUG (nth 5 animals) => BIG-HAIRY-BUG (nth 6 animals) => NIL
The LENGTH function returns the number of elements in a list (length animals) => 6 (length '(the (deeper (you (go (the (nuller (you (get))))))))) => 2
30
Sample Session: Operations on Lists (continued)
Here's a function to return a random element of a list
(defun random-elt (choices) "returns one element of the list of choices at random" (nth (random (length choices)) choices)) => RANDOM-ELT (random-elt animals) => BUG (random-elt animals) => BIG-BUG
This returns a random element as a singleton list (defun one-of (set) "picks one element of the set and returns it in a list" (list (random-elt set))) => ONE-OF (one-of '(how are you doing today?)) => (YOU)
31
Programming Example
(Taken from Chapter 2 of Norvig, (PoAIP)) A Grammar for a (miniscule) Subset of English: Sentence => Noun-Phrase + Verb-Phrase Noun-Phrase => Article + Noun Verb-Phrase => Verb + Noun-Phrase Article => the, a, ... Noun => man, ball, woman, table ... Verb => hit, took, saw, liked ... ;; The grammar can be used to generate sentences: To get a Sentence, append a Noun-Phrase and a Verb-Phrase To get a Noun-Phrase, append an Article and a Noun Choose "the" for the Article Choose "man" for the Noun The resulting Noun-Phrase is "the man" To get a Verb-Phrase, append a Verb and a Noun-Phrase Choose "hit" for the Verb [etc.] ;; The grammar can be used as the basis of the following ;; Lisp functions: (defun sentence () "generates and returns a sentence as a list of atoms" (append (noun-phrase) (verb-phrase))) => SENTENCE (defun noun-phrase () "generates and returns a noun-phrase as a list of atoms" (append (article) (noun))) => NOUN-PHRASE
32
Programming Example (continued) (defun verb-phrase () "generates and returns a verb-phrase as a list of atoms" (append (verb) (noun-phrase))) => VERB-PHRASE (defun article () "generates and returns an article as an atom in a list" (one-of '(the a))) => ARTICLE (defun noun () "generates and returns a noun as an atom in a list" (one-of '(man ball woman table))) => NOUN (defun verb () "generates and returns a noun as an atom in a list" (one-of '(hit took saw liked))) => VERB
33
Programming Example (continued) (sentence) => (A MAN TOOK THE WOMAN) (sentence) => (THE MAN SAW A MAN) (sentence) => (THE WOMAN TOOK A BALL) (noun-phrase) => (THE WOMAN) (verb-phrase) => (TOOK THE BALL) (noun) => (BALL) (verb) => (LIKED)
34
Using the Trace Facility (trace sentence noun-phrase verb-phrase article noun verb) => NIL (sentence) Calling (SENTENCE) Calling (NOUN-PHRASE) Calling (ARTICLE) ARTICLE returned (THE) Calling (NOUN) NOUN returned (MAN) NOUN-PHRASE returned (THE MAN) Calling (VERB-PHRASE) Calling (VERB) VERB returned (HIT) Calling (NOUN-PHRASE) Calling (ARTICLE) ARTICLE returned (THE) Calling (NOUN) NOUN returned (WOMAN) NOUN-PHRASE returned (THE WOMAN) VERB-PHRASE returned (HIT THE WOMAN) SENTENCE returned (THE MAN HIT THE WOMAN) => (THE MAN HIT THE WOMAN) (untrace) => (SENTENCE NOUN-PHRASE VERB-PHRASE ARTICLE NOUN VERB)
35
Recursion
De ne length in terms of itself: the empty list has length 0 and any other list has a length which is one more than the length of the rest of the list.
(defun length (list) (cond ((null list) 0) (BASE) (t (1+ (length (cdr list)))))) (RECURSION: leap of faith)
Evaluation of recursive call: | Evaluate the argument (cdr list) | Bind it to list after old value is saved on stack. | Upon return, add 1 to result.
Spiral of recursion: unwind when list = NIL (length (a b c))
(length (b c))
(length (c))
(length ()) 0 1 2 3
36
Tail Recursion
Note: compiler has to allocate memory for each recursive call. But not true for all recursive calls!
In rst def of length: add 1 after returning from recursive call.
More ecient to write a \tail-recursive" function: recursive 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 \accumulator"
(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 argument and returns t if that argument is a list of atoms. (defun lat? (l) "Returns t if the argument is a list of atoms, nil otherwise" (cond ((null l) t) ((atom (car l)) (lat? (cdr l))) (t nil))) => LAT? (lat? '(remember the alamo)) => T (lat? '(did you remember (to floss?))) => NIL (lat? long-list) => T (lat? 12) => > Error: Can't take CAR of 12. > While executing: LAT? > Type Command-. to abort. See the Restarts menu item for further choices. 1 > (defun lat? (l) "Returns t if the argument is a list of atoms, nil otherwise" (cond ((not (listp l)) nil) ((null l) t) ((atom (car l)) (lat? (cdr l))) (t nil))) => LAT? (lat? 12) => NIL
38
Sample Session: Recursion (continued)
Here's a generalization of double-cons and quadruple-cons we de ned previously: (defun multi-cons (one-thing another-thing n) "Returns the result of consing one-thing onto another-thing n times" (cond ((equalp n 0) another-thing) (t (cons one-thing (multi-cons one-thing another-thing (- n 1)))))) => MULTI-CONS (multi-cons 'hip '(hooray) 5) => (HIP HIP HIP HIP HIP HOORAY)
The following member? function checks to see if its rst argument occurs in its second: (defun member? (a lat) "Returns t if a occurs in lat, nil otherwise" (cond ((null lat) nil) (t (or (equalp (car lat) a) (member? a (cdr lat)))))) => MEMBER? (setq five-colleges '(amherst umass hampshire smith mount-holyoke)) => (AMHERST UMASS HAMPSHIRE SMITH MOUNT-HOLYOKE) (member? 'hampshire five-colleges) => T (member? 'oberlin five-colleges) => NIL
39
MEMBER
Recall that Common Lisp includes a function called MEMBER that does a similar thing. Note, however, that it returns the matching cdr rather than t: (member 'hampshire five-colleges) => (HAMPSHIRE SMITH MOUNT-HOLYOKE) (member 'oberlin five-colleges) => NIL
Note that MEMBER compares using EQ, not EQUALP. For example: (member '(nest) '(a hornet in a hornet (nest) is to be avoided)) => NIL (member? '(nest) '(a hornet in a hornet (nest) is to be avoided)) => T
We can get MEMBER to match using equalp by providing a keyword argument (details on this another day...) (member '(nest) '(a hornet in a hornet (nest) is to be avoided) :test #'equalp) => ((NEST) IS TO BE AVOIDED)
40
A closer look at recursive programming:
Wilensky (Common Lispcraft) p. 89: ...we have the following components to recursive programming:
Breaking down the task at hand into a form that involves simpler versions of the same task.
Specifying a way to combine the simpler versions of the task to solve the original problem.
Identifying the "grounding" situations in which the task can be accomplished directly.
Specifying checks for these grounding cases that will be examined before recursive steps are taken.
(defun factorial (n) "returns the factorial of n" (cond (( FACTORIAL (factorial 5) => 120 (trace factorial) => NIL (factorial 5) Calling (FACTORIAL 5) Calling (FACTORIAL 4) Calling (FACTORIAL 3) Calling (FACTORIAL 2) Calling (FACTORIAL 1) FACTORIAL returned 1 FACTORIAL returned 2 FACTORIAL returned 6 FACTORIAL returned 24 FACTORIAL returned 120 => 120
41
Factorial and Length Revisited
Another version of Factorial:
(defun factorial (n) "returns the factorial of n" (cond ((zerop n) 1) (t (* n (factorial (- n 1)))))) => FACTORIAL (factorial 5) => 120
Using IF we can write a simpler version of factorial: (defun factorial (n) "returns the factorial of n" (if (zerop n) 1 (* n (factorial (- n 1))))) => FACTORIAL (factorial 5) => 120
Here's a recursive length function: (defun my-length (list) "returns the length of list" (if (null list) 0 (+ 1 (my-length (cdr list))))) => MY-LENGTH (my-length '(let it snow let it snow let it AAAARRRRRGGGGGHHHH!!!!)) => 9
42
Iteration: DOTIMES
Sometimes iteration seems more natural. Common Lisp provides many iteration constructs. DOTIMES (var countform [resultform]) {declaration}* {tag | statement}* [Macro] Executes forms countform times. On successive executions, var is bound to the integers between zero and countform. Upon completion, resultform is evaluated, and the value is returned. If resultform is omitted, the result is nil. (dotimes (n 20 'spumoni) (print n)) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 => SPUMONI
43
Iteration: DOTIMES (continued)
Here was our recursive function multicons:
(defun multi-cons (one-thing another-thing n) "Returns the result of consing one-thing onto another-thing n times" (cond ((equalp n 0) another-thing) (t (cons one-thing (multi-cons one-thing another-thing (- n 1)))))) => MULTI-CONS (multi-cons 'hip '(hooray) 5) => (HIP HIP HIP HIP HIP HOORAY)
Here's a version using dotimes: (defun multi-cons (one-thing another-thing n) "Returns the result of consing one-thing onto another-thing n times" (dotimes (count n) (setq another-thing (cons one-thing another-thing))) another-thing) => MULTI-CONS (multi-cons 'hip '(hooray) 5) => (HIP HIP HIP HIP HIP HOORAY)
44
Iteration: DOLIST DOLIST (var listform [resultform]) {declaration}* {tag | statement}* [Macro] Evaluates listform, which produces a list, and executes the body once for every element in the list. On each iteration, var is bound to successive elements of the list. Upon completion, resultform is evaluated, and the value is returned. If resultform is omitted, the result is nil. (defun greet (people) "prints greetings for all of the people listed." (dolist (person people) (print (append '(so nice to see you) (list person))))) => GREET (setq guests '(sally booboo mr-pajamas ms-potato-head)) => (SALLY BOOBOO MR-PAJAMAS MS-POTATO-HEAD) (greet guests) (SO NICE TO SEE YOU SALLY) (SO NICE TO SEE YOU BOOBOO) (SO NICE TO SEE YOU MR-PAJAMAS) (SO NICE TO SEE YOU MS-POTATO-HEAD) => NIL
45
#'
Funny #' notation (sharp-quote): maps name of function to function itself. Equivalent to FUNCTION.
Always use this when using mapcar, apply, funcall, lambda expressions keywords (e.g., :TEST).
Note: only functions may be quoted (not macros or special forms).
More ecient; means something to compiler: go to compiled code, not through symbol to nd function defn.
Analogous to quote. Use any time a function is speci ed. Mapcar: Expects an n-ary fn as 2st arg, followed by n lists. It applies the fn to the arg list obtained by collecting the rst elt of each list. | (mapcar #'1+ '(5 10 7)) ! (6 11 8) | (mapcar (function 1+) '(5 10 7)) ! (6 11 8) | (mapcar #'cons '(a b c) '(1 2 3)) ! ((A . 1) (B . 2) (C . 3)) | (mapcar #'print '(a b c)) ! (A B C) Note: last case also has side eect of printing A, B, and C. Avoid consing up a whole new list by using Mapc: | (mapc #'print '(a b c)) ! (A B C) [This prints A, B, and C, but then returns the second arg, NOT a new list.] 1
Apply, Funcall, Eval
Following forms equivalent: | (+ 1 2 3 4) | (funcall #'+ 1 2 3 4) | (apply #'+ '(1 2 3 4)) | (apply #'+ 1 2 '(3 4)) | (eval '(+ 1 2 3 4))
Funcall: great to use if we don't know function name in advance | (funcall 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
Eval: in general we can use funcall and apply.
| (apply arg1 arg2 ... arglist) | Applies to arguments; last arg MUST be a list
2
Lambda expressions: apply, funcall
Lambda expressions specify a function without a name. (Ah hah! so now we see how apply/funcall will be used.) Lambda is most primitive way to specify fn; has its roots in lambda calculus of Church (lambda (parameters ...) body ...) A lambda expr is a nonatomic name for a fn, just as append is an atomic name for a built-in. Use the #' notation. Example: (funcall #'(lambda (x) (+ x x)) 2) ! 4 (apply #'(lambda (x) (+ x x)) '(2)) ! 4 (wasteful) (mapcar #'(lambda (x) (+ x x)) '(1 2 3 4 5)) ! '(2 4 6 8 10)
*** Programmers who are used to other langs sometimes fail to see the point of lambda expressions. Why are they useful? (1) It's a pain to think up names and to clutter up a program with lots of functions that are only used very locally; (2) MORE IMPORTANT: can create new functions at run time. Note: cannot give a lambda expr to lisp to eval; \lambda" not a function! But can use it as car of a list: ((lambda (x) (+ x x)) 2) ! 4. (I don't use this though.) Note: Defun is a macro that expands out to lambda; so there's really only one way to specify a fn, not two! (symbol-function ) returns lambda expression. 3
IF vs. COND (slide 21 of previous lecture)
The IF special form is a special case of COND: IF testform thenform [elseform] Evaluates testform. If the result is true, evaluates thenform and returns the result; if the result is nil, evaluates elseform and returns the result. (if (> 100 23) 'sure-is 'sure-aint) => SURE-IS (if (member 'bog '(blig blog bog bumper)) (* 99 99) (sqrt -1)) => 9801 (if (member 'fog '(blig blog bog bumper)) (* 99 99) (sqrt -1)) => #c(0 1)
Note that the thenform and the elseform are both restricted to being single forms. In contrast, you can specify any number of forms in a cond clause: (setq temperature 8) => 8 (cond ((< temperature 32) (print '(yowch -- it is cold!)) (print '(i will play god and change that!)) (setq temperature 78)) (t (print '(well i guess it is not so bad)) (print '(where do you think we are? hawaii?)))) (YOWCH -- IT IS COLD!) (I WILL PLAY GOD AND CHANGE THAT!) => 78
2
PROGN
If you want multiple forms in an IF you can use a PROGN: Evaluates each form in order, left to right. The values of all forms but the last are discarded; the value of the last form is returned. (if (< temperature 32) (progn (print '(yowch -- it is cold!)) (print '(i will play god and change that!)) (setq temperature 78)) (progn (print '(well i guess it is not so bad)) (print '(where do you think we are? hawaii?)))) (WELL I GUESS IT IS NOT SO BAD) (WHERE DO YOU THINK WE ARE? HAWAII?) => (WHERE DO YOU THINK WE ARE? HAWAII?)
3
Sample Session: COND (setq x 23) => 23 (cond ((equalp x 1) '(it was one)) ((equalp x 2) '(it was two)) (t '(it was not one or two))) => (IT WAS NOT ONE OR TWO) (setq x 2) => 2 (cond ((equalp x 1) '(it was one)) ((equalp x 2) '(it was two)) (t '(it was not one or two))) => (IT WAS TWO)
4
Sample Session: Operations on Lists (slide 30 of
previous lecture) The NTH function returns an element of a list by number (setq animals '(giraffe dog dinosaur bug big-bug big-hairy-bug)) => (GIRAFFE DOG DINOSAUR BUG BIG-BUG BIG-HAIRY-BUG) (nth 0 animals) => GIRAFFE (nth 1 animals) => DOG (nth 2 animals) => DINOSAUR (nth 3 animals) => BUG (nth 4 animals) => BIG-BUG (nth 5 animals) => BIG-HAIRY-BUG (nth 6 animals) => NIL
The LENGTH function returns the number of elements in a list (length animals) => 6 (length '(the (deeper (you (go (the (nuller (you (get))))))))) => 2
5
Sample Session: Operations on Lists (continued)
Here's a function to return a random element of a list
(defun random-elt (choices) "returns one element of the list of choices at random" (nth (random (length choices)) choices)) => RANDOM-ELT (random-elt animals) => BUG (random-elt animals) => BIG-BUG
This returns a random element as a singleton list (defun one-of (set) "picks one element of the set and returns it in a list" (list (random-elt set))) => ONE-OF (one-of '(how are you doing today?)) => (YOU)
6
Sample Session: Recursion Revisited (slide 39 of
previous lecture) Here's a recursive function called lat?, that takes one argument and returns t if that argument is a list of atoms. (defun lat? (l) "Returns t if the argument is a list of atoms, nil otherwise" (cond ((null l) t) ((atom (car l)) (lat? (cdr l))) (t nil))) => LAT? (lat? '(remember the alamo)) => T (lat? '(did you remember (to floss?))) => NIL (lat? long-list) => T (lat? 12) => > Error: Can't take CAR of 12. > While executing: LAT? > Type Command-. to abort. See the Restarts menu item for further choices. 1 > (defun lat? (l) "Returns t if the argument is a list of atoms, nil otherwise" (cond ((not (listp l)) nil) ((null l) t) ((atom (car l)) (lat? (cdr l))) (t nil))) => LAT? (lat? 12) => NIL
7
Sample Session: Recursion (continued)
Recall the double-cons and quadruple-cons functions (de ned in previous lecture): (defun double-cons (one-thing another-thing) "Returns the result of consing one-thing onto another-thing twice" (cons one-thing (cons one-thing another-thing))) => DOUBLE-CONS (double-cons 'hip '(hooray)) => (HIP HIP HOORAY) (defun quadruple-cons (one-thing another-thing) "Returns the result of consing one-thing onto another-thing 4x" (double-cons one-thing (double-cons one-thing another-thing))) => QUADRUPLE-CONS (quadruple-cons 'um '(huh?)) => (UM UM UM UM HUH?)
Here's a generalization of double-cons and quadruple-cons we de ned previously: (defun multi-cons (one-thing another-thing n) "Returns the result of consing one-thing onto another-thing n times" (cond ((equalp n 0) another-thing) (t (cons one-thing (multi-cons one-thing another-thing (- n 1)))))) => MULTI-CONS (multi-cons 'hip '(hooray) 5) => (HIP HIP HIP HIP HIP HOORAY)
8
MEMBER
The following member? function checks to see if its rst argument occurs in its second: (defun member? (a lat) "Returns t if a occurs in lat, nil otherwise" (cond ((null lat) nil) (t (or (equalp (car lat) a) (member? a (cdr lat)))))) => MEMBER? (setq five-colleges '(amherst umass hampshire smith mount-holyoke)) => (AMHERST UMASS HAMPSHIRE SMITH MOUNT-HOLYOKE) (member? 'hampshire five-colleges) => T (member? 'oberlin five-colleges) => NIL
Recall that Common Lisp includes a function called MEMBER that does a similar thing. Note, however, that it returns the matching cdr rather than t: (member 'hampshire five-colleges) => (HAMPSHIRE SMITH MOUNT-HOLYOKE) (member 'oberlin five-colleges) => NIL
Note that MEMBER compares using EQ, not EQUALP. For example: (member '(nest) '(a hornet in a hornet (nest) is to be avoided)) => NIL (member? '(nest) '(a hornet in a hornet (nest) is to be avoided)) => T
We can get MEMBER to match using equalp by providing a keyword argument: (member '(nest) '(a hornet in a hornet (nest) is to be avoided) :test #'equalp) => ((NEST) IS TO BE AVOIDED)
9
A closer look at recursive programming (slide 41 of
previous lecture) Wilensky (Common Lispcraft) p. 89: ...we have the following components to recursive programming:
Breaking down the task at hand into a form that involves simpler versions of the same task.
Specifying a way to combine the simpler versions of the task to solve the original problem.
Identifying the "grounding" situations in which the task can be accomplished directly.
Specifying checks for these grounding cases that will be examined before recursive steps are taken.
(defun factorial (n) "returns the factorial of n" (cond (( FACTORIAL (factorial 5) => 120 (trace factorial) => NIL (factorial 5) Calling (FACTORIAL 5) Calling (FACTORIAL 4) Calling (FACTORIAL 3) Calling (FACTORIAL 2) Calling (FACTORIAL 1) FACTORIAL returned 1 FACTORIAL returned 2 FACTORIAL returned 6 FACTORIAL returned 24 FACTORIAL returned 120 => 120
10
Factorial and Length Revisited (slide 42 of previous
lecture) Another version of Factorial:
(defun factorial (n) "returns the factorial of n" (cond ((zerop n) 1) (t (* n (factorial (- n 1)))))) => FACTORIAL (factorial 5) => 120
Using IF we can write a simpler version of factorial: (defun factorial (n) "returns the factorial of n" (if (zerop n) 1 (* n (factorial (- n 1))))) => FACTORIAL (factorial 5) => 120
Here's a recursive length function: (defun my-length (list) "returns the length of list" (if (null list) 0 (+ 1 (my-length (cdr list))))) => MY-LENGTH (my-length '(let it snow let it snow let it AAAARRRRRGGGGGHHHH!!!!)) => 9
11
Iteration: DOTIMES (slide 43 of previous lecture)
Sometimes iteration seems more natural. Common Lisp provides many iteration constructs. We talked about DOLIST last time. Another is DOTIMES. DOTIMES (var countform [resultform]) {declaration}* {tag | statement}* [Macro] Executes forms countform times. On successive executions, var is bound to the integers between zero and countform. Upon completion, resultform is evaluated, and the value is returned. If resultform is omitted, the result is nil. (dotimes (n 20 'spumoni) (print n)) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 => SPUMONI
12
Iteration: DOTIMES (continued)
Here was our recursive function multicons:
(defun multi-cons (one-thing another-thing n) "Returns the result of consing one-thing onto another-thing n times" (cond ((equalp n 0) another-thing) (t (cons one-thing (multi-cons one-thing another-thing (- n 1)))))) => MULTI-CONS (multi-cons 'hip '(hooray) 5) => (HIP HIP HIP HIP HIP HOORAY)
Here's a version using dotimes: (defun multi-cons (one-thing another-thing n) "Returns the result of consing one-thing onto another-thing n times" (dotimes (count n) (setq another-thing (cons one-thing another-thing))) another-thing) => MULTI-CONS (multi-cons 'hip '(hooray) 5) => (HIP HIP HIP HIP HIP HOORAY)
13
Keyword arguments: remove
So what are all these keywords? Let's look at remove: (remove => (2 3 (remove => (2 3 (remove => (1 1 (remove => (1 2
1 2 1 2 1 0 1 3
'(1 2 3 0 -1) '(1 2 3 0) '(1 2 3 -1) '(1 2 3 2 0 -1)
2 1 0 -1)) 2 1 0 -1) :key #'abs) 2 1 0 -1) :test #' 5 LET ({variable | (variable value) }*) {declaration}* {form}* [Special Form]
LET creates a binding for each variable (in parallel) and evaluates forms in the resulting environment. Returns the value of the last form. (setq laadeedaa 'hihohiho) => HIHOHIHO (let ((laadeedaa 'feefifofum)) (list laadeedaa laadeedaa laadeedaa)) => (FEEFIFOFUM FEEFIFOFUM FEEFIFOFUM) laadeedaa => HIHOHIHO
Note that the initializations in a LET are performed \in parallel". You may not rely on the earlier ones being performed before the later ones. If you want sequential initialization behavior, use LET*: (let* ((foo 2) (bar (+ foo 3))) (+ foo bar)) => 7
17
Local Variables (continued)
Recall CMSC 330: \call-bys", activation recs, stacks. Lisp uses lexical binding. (Used to be dynamic.) So cannot access vars not declared locally (unless special global), even if de ned inside of calling fn. (In dynamic scoping, value of var determined by value inside local env OR that of most recent caller containing the var.) Let is the most common way of introducing vars that are not parameters of fns; resist temptation to use a var w/o introducing it (can actually do this in Lisp). Let introduces a new local variable and binds it to a value. General form: (let ((var value) ...) body-containing-vars) Equiv to: ((lambda (var ...) body-containing-vars) value) Let used to initialize LOCALS; Setf used to initialize GLOBALS (or defparameter, defconstant, defvar). You need to know that symbols (name for var or fn) are NOT vars (depends on context). The symbol value does NOT change when fn is called w/ a particular symbol as a local var: (symbol-value 'x) returns global val of x x returns global val of x (symbol-value cannot access the value of a lexical variable)
Current activation record (containing lexical vars) pushed on stack; x bound to particular value, but symbol-value looks at global value, not value at top of the stack. IMPORTANT: Try not to overload programs w/ globals; use locals where possible. 18
Local Variables (continued)
Note: Let (and let*) take up memory; if don't need to allocate at top of fn, then don't: (defun foo (x) (cond ((zerop x) 0) (t (let ((y (1+ x))) (* y x)))))
Let*: is the same as Let except scope begins immediately. More formally: let* = (let (let ...))
(setf x 1) => 1 (let ((x 5) (y (1+ x))) (print (+ x y))) => 7 ;; side effect 7 ;; value (print x) => 1 1
(setf x 1) => 1 (let* ((x 5) (y (1+ x))) (print (+ x y))) => 11 11 (print x) => 1 1
Note: use Let whenever possible.
(let* ((x 6) (y (* x x))) (+ x y)) (let ((x 6)) (let ((y (* x x))) (+ x y))) ((lambda (x) ((lambda (y) (+ x y)) (* x x))) 6)
19
Lexical Closures
What does it mean to create a new function? Every time #' is evaluated, a fn is returned. But in previous examples, it is always the same fn that is returned (e.g., the case given above for doubling arguments). Lexical closures are fns in which free variables have values from environment in which closure is PERFORMED, not APPLIED. | Evaluating a #' form PERFORMS a closure. | Using the result of this evaluation APPLIES the closure. A lexical closure is a box we can pick up and carry around with us; has its own local environment. A lexical closure is a function and its environment. Any function (or lambda form) de ned in a non-null lexical environment|that is, not at the top level|is actually a closure. What does \free variable" mean? Example: variable n in (lambda (x) ... n ...) Normally: by lexical scoping rules, refers to value of global symbol taken at time the lambda expression is APPLIED: the value of n will be whatever the value of n is when this lambda expr is applied. Contrast: lexical closure; refers to value of that symbol taken at time the function is created (i.e., when the closure is PERFORMED). The value PERSISTS between fn calls. 20
Lexical Closures
The existence of variables that are local to a fn but which persist between calls is useful for creating generators. A generator is a function that produces a sequence of related values. (defun make-even (seed) #'(lambda nil (setf seed (+ seed 2)))) => MAKE-EVEN
What is the free var above? SEED. It initially gets its value when the closure is performed: (setf even (make-even 0)) => # (funcall even) => 2 (funcall even) => 4 ;; remembers!
An even neater thing to do: (setf (symbol-function 'even) (make-even 0)) => # (even) => 2 ;; Now use as function (even) => 4
21
Lexical Closures (continued)
Closures can be used in some powerful ways. The following gives us many of the advantages of global variables without the disadvantages. (let ((counter 0)) (defun new-id () (incf counter)) (defun reset-id () (setq counter 0))) => RESET-ID (new-id) => 1 (new-id) => 2 (new-id) => 3 (reset-id) => 0 (new-id) => 1 (new-id) => 2
We can also return closures from functions and then call them with FUNCALL or MAPCAR: (defun make-adder (n) #'(lambda (x) (+ x n))) => MAKE-ADDER (funcall (make-adder 2) 5) => 7 (funcall (make-adder 12) 500) => 512 (mapcar (make-adder 3) '(1 3 10)) => (4 6 13)
22
Lexical Closures (continued)
Note that (adder n): PERFORMS the closure. What does (adder 3) return? (adder 3) => #
This is a closure that REMEMBERS the value 3!!! (n is set to 3 internally). Suppose Y is set to this result. We can now APPLY the closure: (funcall y 2) => 12 (apply y '(2)) => 12 (mapcar y '(1 3 10)) => (11 13 20)
It is also possible to make closures that can be asked to change their state (defun make-adderb (n) ;; allows optional argument (discussed next lecture) #'(lambda (x &optional change) (if change (setq n x) (+ x n)))) => MAKE-ADDERB (setq addx (make-adderb 1)) => # (funcall addx 3) => 4 (funcall addx 100 t) => 100 (funcall addx 3) => 103
23
Characters, Arrays, Strings
Recall: s-expressions are Atoms, lists, dotted pairs
What can an atom be? Symbol, number
What else? Character, array, string: atoms because not conses; but not really atomic. (Made up of components)
Character = #\a | code (integer identi er) | bits ( ags for printing) | font (integer; implementation-dependent) (if bits and font = 0, then standard char; can be stored in string)
Array: collection of integers, chars, symbols, s-expressions.
String: collection of chars (one-dimensional array or vector)
Tests: characterp, arrayp, stringp Note: arrayp returns T on strings!
24
Characters, Arrays, Strings
Characters: Can do things like: (characterp #\a) => T (char< #\a #\b) => T
;; alphanumeric ordering
Equality: char=, char, char>=, char/= (not equal), char-equal (upper/lower allowed), char= (distinguishes between them)
Some other tests: (lower-case-p #\a) = T (upper-case-p #\A) => T (char-downcase #\A) => #\a
25
Characters, Arrays, Strings (continued)
Arrays: collection of int, char, sym, any s-exp [Not strongly typed; can be mixed] Dimensions: 0 1 2
0 1 2 3 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)) => # (setf a (make-array '(1) :element-type 'character))
Some compilers can store the array more eciently and create ecient code for accessing if it is typed.
27
Strings:
Combines arrays and chars (a string is a 1-d array (vector) of chars)
X = | #\a | #\b | #\c | ! "abc" (shorthand) { (char x 0) = #\a (most appropriate) { (aref x 0) = #\a { (elt x 0) = #\a (least ecient: works on lists too) How do we create a string? (make-string 7 :initial-element #\z) => "zzzzzzz" (string #\z) => "z" (string 'z) => "z" (string "z") => "z" (format nil "~s" 'z) ;; stream is nil (just return value) => "z"
28
Sequences
A sequence is: { A 1-d array (vector): #(a b c d) { A list: (a b c d) { A string (special case vector): #(#\a #\b #\c #\d) = "abcd" Can perform certain types of ops on all sequences: (remove 'a '(a b c a) :test #'eq) => (B C) (remove #\a "abc" :test #'char=) => "bc" (reverse '(a b c) => (C B A) (reverse "abc") => "cba" (elt '(a b c a) 1) => B
Note: Nth more ecient than Elt: (nth '(a b c a) 1) => B
Nth works on lists only (not strings, vectors). Elt used for all sequences. If type is known, use: { nth for lists { aref for vectors/arrays { char for strings 29
Sequences (continued)
Other general sequence functions:
(concatenate 'string "a" "b") => "ab" (concatenate 'list '(a) '(b)) => (A B) ;; Inefficient: Use append (concatenate 'array #(a b c d) #(e f)) => #(a b c d e f) (length "abc") => 3 (length '(a b c)) => 3 (length #(a b c)) => 3 (subseq "abc" 2 3) => "c" (subseq '(a b c) 2 3) => (C) (subseq #(a b c) 2 3) => #(c)
30
More Sequence Functions
Suppose x = "abcd"
FIND: (find
item sequence :test )
(find #\a x :test #'char=) => #\a (find #\b x :test #'char #\a
POSITION: (position
;; Useful with #'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 *standardinput* (the screen usually). So if you say (read), it'll sit there until user types in.
(read-line stream) is a variant of read: reads a whole line and returns a string. Can then use read-from-string. (setf n 0) (setf x (read-line stream)) "this is a line" => "this is a line" (read-from-string x :start 5) => THIS => 5
Note: read-from-string returns two values (atom and pointer of next char); we'll learn how to catch these values shortly. Note: This is NOT really correct! Need to specify two optional parameters ... 32
Input in Lisp (continued)
Here's the full format for read-from-string: (read-from-string string eof-error-p eof-value ) ;; 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") ; (load "x.fasl") ; (compile-file "x") (load "x")
source compiled ; turns .lisp into binary file ; can say this and it'll look for binary ; if none then it'll look for .lisp
Note: If you have two modules, A, and B, where B depends on A, need to be careful about order of compilation and loading: A macro defns
B uses macros
compile and load this first
then compile and load this
B must know about A before it is compiled and loaded. 33
Programming Example (slide 32 of previous lecture)
(Taken from Chapter 2 of Norvig, (PoAIP)) A Grammar for a (miniscule) Subset of English: Sentence => Noun-Phrase + Verb-Phrase Noun-Phrase => Article + Noun Verb-Phrase => Verb + Noun-Phrase Article => the, a, ... Noun => man, ball, woman, table ... Verb => hit, took, saw, liked ...
;; The grammar can be used to generate sentences: To get a Sentence, append a Noun-Phrase and a Verb-Phrase To get a Noun-Phrase, append an Article and a Noun Choose "the" for the Article Choose "man" for the Noun The resulting Noun-Phrase is "the man" To get a Verb-Phrase, append a Verb and a Noun-Phrase Choose "hit" for the Verb [etc.] ;; The grammar can be used as the basis of the following ;; Lisp functions: (defun sentence () "generates and returns a sentence as a list of atoms" (append (noun-phrase) (verb-phrase))) => SENTENCE (defun noun-phrase () "generates and returns a noun-phrase as a list of atoms" (append (article) (noun))) => NOUN-PHRASE
34
Programming Example (continued) (defun verb-phrase () "generates and returns a verb-phrase as a list of atoms" (append (verb) (noun-phrase))) => VERB-PHRASE (defun article () "generates and returns an article as an atom in a list" (one-of '(the a))) => ARTICLE (defun noun () "generates and returns a noun as an atom in a list" (one-of '(man ball woman table))) => NOUN (defun verb () "generates and returns a noun as an atom in a list" (one-of '(hit took saw liked))) => VERB
35
Programming Example (continued) (sentence) => (A MAN TOOK THE WOMAN) (sentence) => (THE MAN SAW A MAN) (sentence) => (THE WOMAN TOOK A BALL) (noun-phrase) => (THE WOMAN) (verb-phrase) => (TOOK THE BALL) (noun) => (BALL) (verb) => (LIKED)
36
Using the Trace Facility (slide 35 of previous lecture) (trace sentence noun-phrase verb-phrase article noun verb) => NIL (sentence) Calling (SENTENCE) Calling (NOUN-PHRASE) Calling (ARTICLE) ARTICLE returned (THE) Calling (NOUN) NOUN returned (MAN) NOUN-PHRASE returned (THE MAN) Calling (VERB-PHRASE) Calling (VERB) VERB returned (HIT) Calling (NOUN-PHRASE) Calling (ARTICLE) ARTICLE returned (THE) Calling (NOUN) NOUN returned (WOMAN) NOUN-PHRASE returned (THE WOMAN) VERB-PHRASE returned (HIT THE WOMAN) SENTENCE returned (THE MAN HIT THE WOMAN) => (THE MAN HIT THE WOMAN) (untrace) => (SENTENCE NOUN-PHRASE VERB-PHRASE ARTICLE NOUN VERB)
37
Multiple Values
So far we've spoken of \the value returned by a function." Historically: Lisp designed so that every fn returns a val. But sometimes we want more than one piece of info (e.g., read-from-string). Rather than creating a list for multiple types of info, can return more than one value. Consider read-from-string. Suppose we want to \catch" the two values: x THIS 5
We need to catch these two values by using multiplevalue-bind: (multiple-value-bind (atom ptr) (read-from-string x nil nil :start n))
Note that this looks like a LET. Can use it to start out a function; close it o at the end. Now we can pluck out the tokens in the string: x MARY BILL ;; no space at end.
4
Output in Lisp: Format (continued)
Format is used for output to a le (use with-open- le; implicit progn): (with-open-file (mystream "x.lisp" :direction :output) (format stream "Hi."))
File is saved out when \with" is exited. Value: specify nil for stream name (returns a string; no side eect): (format nil "~%Here are 3 s-expressions: ~s, ~s, and ~s." 'a '(b c) "de") => "Here are 3 s-expressions: A, (B C), and "de"."
5
Output in Lisp: Examples (format t "Howdy there in TV land!") ;; Simple cases (no directives): Howdy there in TV land! => NIL (format t "I had a little froggy.") I had a little froggy. => NIL
One of the simplest and most useful directives is ~%, which means to output a newline character. (format t "~%Bop bop~% shoo~% Bop bop shoo bob bop bop bam => NIL
bob~%
bop bop bam")
Even ~% has more options { for example, a number between the ~ and the % will cause that many newlines to be printed: (format t "jump down ~5% turn around.") jump down
turn around. => NIL
The second handiest format directive is ~a. This means \go get the next lisp object from the argument list and print it here." (format t "~%this is ~a, so ~a ~a ~a!" 'swell 'stomp 'and 'yell) this is SWELL, so STOMP AND YELL! => NIL (format t "~%this is ~a, so ~a ~a ~a!" '(a list) 4 "ever" 'tell) this is (A LIST), so 4 ever TELL! => NIL
6
Output in Lisp: Examples (continued)
There must be enough arguments in the call to format; extras will be ignored: (format t "~%this is ~a, so ~a ~a ~a!" '(a list) 4 "ever") > Error: Missing argument (format t "~%this is ~a, so ~a ~a ~a!" '(a list) 4 "ever" 'tell 'somebody) this is (A LIST), so 4 ever TELL! => NIL
Format directives can be used to print numbers in a wide range of formats. (setq n 54321) => 54321 (format t "Here's n:~A." n) Here's n:54321. => NIL (format t "Here's n:~O." n) Here's n:152061. => NIL (format t "Here's n:~X." n) Here's n:D431. => NIL (format t "Here's n:~E." n) Here's n:5.4321E+4. => NIL (format t "Here's n:~B." n) Here's n:1101010000110001. => NIL (format t "Here's n:~5R." n) ;; base 5 Here's n:3214241. => NIL
7
Output in Lisp: Examples (continued) (format t "This year:~@R." 1995) ;; roman This year:MCMXCV. => NIL (format t "Here's n:~R." n) ;; english Here's n:fifty-four thousand three hundred twenty-one. => NIL (format t "Here's n:~10D." n) Here's n: 54321. => NIL (format t "Here's PI:~F." PI) Here's PI:3.141592653589793. => NIL (format t "Here's PI:~15,5F." PI) Here's PI: 3.14159. => NIL (format t "Here's PI:~15,5,,,'*F." PI) Here's PI:********3.14159. => NIL (format t "Here's PI as money:$~$." PI) Here's PI as money:$3.14. => NIL
8
Format: More Variations
See CLTL2 for many more variations. The power of FORMAT goes well beyond numeric formatting. Consider this example from Wilensky: (setq n 1) => 1 (format t "~D boy~:P left" n) 1 boy left => NIL (setq n 2) => 2 (format t "~D boy~:P left" n) 2 boys left => NIL
The ~{ and ~} directives can be used to perform iteration within a format string: (setq folks '(candy sandy wendy henry ben)) => (CANDY SANDY WENDY HENRY BEN) (format t "It was a mess. ~{~%~A fell down,~}OUCH!" folks) It was a mess. CANDY fell down, SANDY fell down, WENDY fell down, HENRY fell down, BEN fell down,OUCH! => NIL
There are format directives for case-conversion, tables, indirection, etc... See CLTL2 for details. Providing NIL as the rst argument to FORMAT causes the string to be RETURNED rather than printed. (progn (setq my-string (format nil "wowie zowie!")) 'hmmm) => HMMM my-string => "wowie zowie!"
9
Function Parameters:
&optional
Ordinary function de nitions specify some number of REQUIRED parameters; calls to these functions must pass exactly 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 parameters. A function is speci ed to take optional parameters by putting &optional, followed by parameter names, in the parameter list. (defun shout (stuff &optional more-stuff) (list 'hey 'you! '--- stuff more-stuff '!)) => SHOUT (shout 'look 'out) => (HEY YOU! --- LOOK OUT !) (shout 'look) => (HEY YOU! --- LOOK NIL !) (defun shout (stuff &optional more-stuff) (if more-stuff (list 'hey 'you! '--- stuff more-stuff '!) (list 'hey 'you! '--- stuff '!))) => SHOUT (shout 'look 'out) => (HEY YOU! --- LOOK OUT !) (shout 'look) => (HEY YOU! --- LOOK !)
10
Function Parameters:
(continued) If you don't want your optional parameter to default to nil, you can provide another default value: &optional
(defun shout (stuff &optional (more-stuff 'around)) (list 'hey 'you! '--- stuff more-stuff '!)) => SHOUT (shout 'look 'out) => (HEY YOU! --- LOOK OUT !) (shout 'look) => (HEY YOU! --- LOOK AROUND !)
You can specify as many optional arguments as you want: (defun fill-grocery-bag (item &optional (item2 'milk) (item3 'eggs)) (list 'the 'bag 'contains item item2 'and item3)) => FILL-GROCERY-BAG (fill-grocery-bag 'spam) => (THE BAG CONTAINS SPAM MILK AND EGGS) (fill-grocery-bag 'spam 'flan) => (THE BAG CONTAINS SPAM FLAN AND EGGS) (fill-grocery-bag 'spam 'flan 'marjoram) => (THE BAG CONTAINS SPAM FLAN AND MARJORAM) (fill-grocery-bag 'spam 'flan 'marjoram 'dan) > Error: Too many arguments.
11
Function Parameters:
(continued) You can also specify SUPPLIED-P variables with your optional parameters: &optional
(defun eat (what &optional (when 'supper when-supplied)) (if (and (eq when 'supper) when-supplied) (print '(supper is the default -- you could have left it out))) (list 'you 'ate what 'at when)) => EAT (eat 'gefilte-fish 'lunch) => (YOU ATE GEFILTE-FISH AT LUNCH) (eat 'gefilte-fish) => (YOU ATE GEFILTE-FISH AT SUPPER) (eat 'gefilte-fish 'supper) (SUPPER IS THE DEFAULT -- YOU COULD HAVE LEFT IT OUT) => (YOU ATE GEFILTE-FISH AT SUPPER)
12
Function Parameters:
&rest
We can write functions that are even more exible by using &rest parameters. An &rest parameter gets bound to ALL remaining arguments in the function call. (defun eat (first-food &rest more-foods) (format t "~%you started with ~A" first-food) (dolist (next-food more-foods) (format t "~%and then you had ~A" next-food)) 'burp) => EAT (eat 'gefilte-fish) you started with GEFILTE-FISH => BURP (eat 'gefilte-fish 'horseradish) you started with GEFILTE-FISH and then you had HORSERADISH => BURP (eat 'gefilte-fish 'horseradish 'pickles) you started with GEFILTE-FISH and then you had HORSERADISH and then you had PICKLES => BURP (eat 'gefilte-fish 'horseradish 'pickles 'olives) you started with GEFILTE-FISH and then you had HORSERADISH and then you had PICKLES and then you had OLIVES => BURP
13
Function Parameters:
&keyword
The &keywords facility allows us to specify NAMED optional parameters. (defun make-poem (title &key (subject 'flower)) (format t "~%~A, by Lulu Lispy" title) (format t "~%--------------------------") (format t "~%'twas a stupendously beautiful ~A" subject) (format t "~%upon my snow-covered porch") (format t "~%such a lovely lovely ~A" subject) (format t "~%I blasted it with my torch.")) => MAKE-POEM (make-poem 'spring) SPRING, by Lulu Lispy -------------------------'twas a stupendously beautiful FLOWER upon my snow-covered porch such a lovely lovely FLOWER I blasted it with my torch. => NIL (make-poem 'spring :subject 'cat) SPRING, by Lulu Lispy -------------------------'twas a stupendously beautiful CAT upon my snow-covered porch such a lovely lovely CAT I blasted it with my torch. => NIL
Note that the keyword must be provided. This causes an error: (make-poem 'spring 'cat) > Error: Incorrect keyword arguments in (CAT) .
14
Function Parameters:
(continued) The nifty thing about keyword parameters is that you can specify any subset of a function's arguments, in any order: &keyword
(defun make-poem (title &key (subject 'flower) (location 'porch) (blaster 'torch) (covering 'snow)) (format t "~%~A, by Lulu Lispy" title) (format t "~%--------------------------") (format t "~%'twas a stupendously beautiful ~A" subject) (format t "~%upon my ~A-covered ~A" covering location) (format t "~%such a lovely lovely ~A" subject) (format t "~%I blasted it with my ~A." blaster)) => MAKE-POEM (make-poem 'spring) SPRING, by Lulu Lispy -------------------------'twas a stupendously beautiful FLOWER upon my SNOW-covered PORCH such a lovely lovely FLOWER I blasted it with my TORCH. => NIL (make-poem 'spring :location 'veranda :blaster 'power-sander) SPRING, by Lulu Lispy -------------------------'twas a stupendously beautiful FLOWER upon my SNOW-covered VERANDA such a lovely lovely FLOWER I blasted it with my POWER-SANDER. => NIL
15
Function Parameters:
&keyword
(continued)
(make-poem 'spring :location 'veranda :blaster 'power-sander :subject 'pumpkin) SPRING, by Lulu Lispy -------------------------'twas a stupendously beautiful PUMPKIN upon my SNOW-covered VERANDA such a lovely lovely PUMPKIN I blasted it with my POWER-SANDER. => NIL (make-poem 'spring :subject 'space-alien :location 'veranda :blaster 'power-sander :covering 'glitter) SPRING, by Lulu Lispy -------------------------'twas a stupendously beautiful SPACE-ALIEN upon my GLITTER-covered VERANDA such a lovely lovely SPACE-ALIEN I blasted it with my POWER-SANDER. => NIL
16
Function Parameters:
(continued) &keyword parameters can also be given SUPPLIED-P variables, and they may be combined with &optional and &rest parameters. Note that many built-in functions use &keyword parameters for optional features. For example, MEMBER has 3 keyword parameters: TEST, TEST-NOT, and KEY. (It sounds funny to have a &keyword parameter called KEY, but that's just a coincidence.) Here are some examples: &keyword
(member 'prune '(raisin prune cheese)) => (PRUNE CHEESE) (defun young-version (food) (case food (raisin 'grape) (prune 'plum) (cheese 'milk))) => YOUNG-VERSION (young-version 'prune) => PLUM (member 'plum '(raisin prune cheese)) => NIL (member 'plum '(raisin prune cheese) :key #'young-version) => (PRUNE CHEESE) (member 'green '((blue sea)(green grass) (yellow teeth))) => NIL (member 'green '((blue sea)(green grass) (yellow teeth)) :key #'car) => ((GREEN GRASS) (YELLOW TEETH))
17
Function Parameters:
(continued) NOTE: KEY defaults to the function IDENTITY. &keyword
(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)) (dolist (element list) (incf len)) len))
; start with LEN=0 ; and on each iteration ; increment LEN by 1 ; 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)) (l list (rest l))) ((null l) len)))
; start with LEN=0, increment ; ... on each iteration ; (until the end of the list)
Note: do/do* anal. to let/let*.
19
Loop
There are more complicated looping constructs than those described in Tanimoto, i.e., \loop". Note: You should not be using loop in lieu of sequence operations. (e.g., nding an elt in a list should be done with nd or nd-if, not loop or do).
Note from last time: (return x); will return out of a loop of any kind (do, dolist, etc.) with the value x. (We'll look at this in a minute.)
Steele 84: (loop body ...) This is the old \loop forever" loop. (Tanimoto)
Steele 90: (loop do body ...) This is the more sophisticated loop macro that allows counting, summing, etc. Many di types of \loop keywords." Example (from page 60): (defun length6 (list) (loop with len = 0 until (null list) for element = (pop list) do (incf len) finally (return len)))
; ; ; ; ;
start with LEN=0 and (until end of list) on each iteration increment LEN by 1 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 dierent: the compiler expands macros into some other code. When do we use macros? When we want some \nice" syntax that expands into uglier code that we don't care about looking at. (1) Decide what the \nice" syntax is. (2) Decide what the \ugly" code is. Why used? (1) Eciency: expanded in-line at compile time |More ecient than calling a fn at RT (2) Eval'n of args can be suppressed |Example: setf does not eval rst arg. (3) CT: can base expansion of macro on known arg vals |(setf
(aref a 1) 0):
(let* ((#:g332 a) (#:g331 0)) (excl::.inv-s-aref #:g331 #:g332 0))
| (setf
a 1): (setq a 1)
(4) Shorthand: allows you to say things more concisely Evaln of macro: extra evaln step before standard form evaln takes place. Use macroexpand to see what a macro expands out to. Every time you de ne a macro, you are actually de ning a new language and writing a compiler for it! So don't take macros lightly. 21
Defmacro
Defmacro: (1) args bound to formals (2) macro's body eval'd (produce new form) (3) eval the new form at RT to produce nal value
(while test body ...)
(loop (unless test (return nil)) body)
What does the macro look like?
Note: unless has intuitive meaning.
(defmacro while (test &rest body) (list* 'loop (list 'unless test '(return nil)) body))
Note: list* appends last arg to end of list of other args.
(macroexpand '(while (< i 10) (print (* i i)) (setf i (+ i 1))))
Expands to:
(loop (unless (< i 10) (return nil)) (print (* i i)) (setf i (+ i 1)))
How do we know when something is a macro? (1) Are args quoted? (If not then probably macro, but not very reliable.) (2) Macro-function (3) Macroexpand: if returns same thing, then not macro: (macroexpand '(cons 'a 'b)): (cons 'a 'b)
22
DEFMACRO: More details DEFMACRO symbol lambda-list {declaration | doc-string}* {form}* [Macro]
DEFMACRO constructs a global macro de nition, binds it to symbol, marks symbol as a macro, and returns symbol. Defmacro is the macro equivalent of defun. (defvar *people-ages* '((sally 22) (john 21) (tina 56) (eugene 43) (wendy 11) (sam 1))) => *PEOPLE-AGES* (defun get-age (name &optional (people-ages *people-ages*)) (cond ((null people-ages) nil) ((eq name (car (car people-ages))) (car (cdr (car people-ages)))) (t (get-age name (cdr people-ages))))) => GET-AGE (get-age 'tina) => 56 (get-age tina) > Error: Unbound variable: TINA (defmacro age (name) (list 'get-age (list 'quote name))) => AGE (age tina) => 56 (macroexpand '(age tina)) => (GET-AGE 'TINA)
23
DEFMACRO: More details (continued)
Macros have more interesting applications. They are often used to extend the syntax of lisp. (defmacro while (test &rest body) (append (list 'do nil (list (list 'not test))) body)) => WHILE (let ((n 1)) (while (< n 10) (print n) (setq n (+ n 1)))) 1 2 3 4 5 6 7 8 9 => NIL (macroexpand-1 '(while (< n 10) (print n) (setq n (+ n 1)))) => (DO () ((NOT (< N 10))) (PRINT N) (SETQ N (+ N 1)))
24
Backquote/Comma
Hardest part about de ning macro is building code that is the expansion of the macro. Have to use list, cons, append, etc. when writing a macro.
Use backquote (`): Gives more immediate way of building code.
Handy in writing macros: acts just like quote except that any expression preceded by a comma is considered to be unquoted: (setf name 'fred) `(I gave ,name about ,(* 25 8) dollars) becomes (i gave fred about 200 dollars)
Backquote useful for other things (not always used in macro): (list (list x)) where x = (b c d) can be speci ed as: `((,x)) ! (((b c d))). Other examples: '(spring forward fall back) => (SPRING FORWARD FALL BACK) `(spring forward fall back) => (SPRING FORWARD FALL BACK) `(spring forward fall back ,(+ 100 265) times) => (SPRING FORWARD FALL BACK 365 TIMES) '(spring forward fall back ,(+ 100 265) times) > Error: Comma not inside backquote (list 'spring 'forward 'fall 'back (+ 100 265) 'times) => (SPRING FORWARD FALL BACK 365 TIMES)
(defmacro mysetq (a e) `(set (quote ,a) ,e)) (mysetq x 3) ! (set (quote x) 3) equiv to (setq x 3)
25
Commas, Comma-atsign
Commas can be nested deep within list structure: (setq name 'barney) => BARNEY (setq occupation 'bozo) => BOZO (append '(hello) `(mister ,name (the ,occupation))) => (HELLO MISTER BARNEY (THE BOZO))
COMMA-ATSIGN (,@) within a backquote \splices" its list-valued result into the larger list: `(a ,@x) where x is (b c d) ! (a
b c d)
(setq fudds-law '(if you push something hard enough it will fall over)) => (IF YOU PUSH SOMETHING HARD ENOUGH IT WILL FALL OVER) `(in my youth i thought that ,@fudds-law -- but now i know better) => (IN MY YOUTH I THOUGHT THAT IF YOU PUSH SOMETHING HARD ENOUGH IT WILL FALL OVER -- BUT NOW I KNOW BETTER)
26
Cleaner Macros
Using the backquote, how would we rewrite WHILE without using LIST and LIST*? (i.e., using ` and , and @)? (defmacro while (test &rest body) `(do () ((not ,test)) ,@body))
The backquote syntax allows us to write a much cleaner WHILE macro. (let ((n 1)) (while (< n 10) (print n) (setq n (+ n 1)))) 1 2 3 4 5 6 7 8 9 => NIL (macroexpand-1 '(while (< n 10) (print n) (setq n (+ n 1)))) => (DO () ((NOT (< N 10))) (PRINT N) (SETQ N (+ N 1)))
The function MACROEXPAND-1 can be used to expand only one level of macro de nitions: MACROEXPAND-1 form &optional environment [Function]
MACROEXPAND-1 returns the result of expanding form once within environment. (In eect, macroexpand simply calls macroexpand-1 repeatedly until no more expansion is possible.) 27
Macro Tips
Note: have to be careful not to evaluate an expression more than once:
(defmacro foo (element) `(unless (null ,element) (print ,element)))
Suppose element = (cons 'a '(b))? Don't want to evaluate 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 expands 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, FUNCALL, 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, Prentice Hall, 1994|THE book to read about lisp macros] Here are some of the nifty things you can do with macros:
Argument transformation|e.g., the SETF macro, which picks apart its arguments before evaluation. Conditional evaluation of arguments|like IF, WHEN, COND, etc. Multiple evaluation of arguments|like DO, WHILE, etc. Use of the calling environment|a macro expansion replaces the macro call in the lexical scope of the call|hence it can use and change lexical bindings in ways that functions can't. For example, the behavior of the macro: (defmacro foo (x) `(+ ,x y))
depends on the binding of y where foo is called. Graham notes that \This kind of lexical intercourse is usually viewed more as a source of contagion than a source of pleasure." Reduction of function call overheads|there is no overhead associated with macro calls. By runtime, the macro call has been replaced by its expansion. Computation at compile time|you can sometimes move a *lot* of computation to compile-time, reducing the runtime computation to a minimum. Integration with Lisp|sometimes you can write macros that transform problems, in a higher-level language of your own design, into simple Lisp. 36
The downside of macros:
Functions are data|they can be passed as arguments, returned from other functions, etc. None of this is possible with macros.
Clarity of source code|macro de nitions can get hairy.
Clarity at runtime|macros can be harder to debug, you can't trace them (because they're gone by runtime) and stack backtraces are less informative when you use lots of macros.
Recursion|an expansion function may be recursive, but the expansion itself may not be.
37
Alists, Assoc
The matching functions in chapter 3 use an \association list" (ALIST) for representing the associations of values with variables.
ALIST: A list of lists in which each list is called an entry and the car of each entry is the key. In the book, each list is a dotted pair: ((VAR1 . VAL1) (VAR2 . VAL2) ...)
Dotted pair form of ALIST can be built with ACONS:
Can use to build a table (with or without dot): (setf words '((one uno) (two dos) (three tres) (four cuatro) ( ve cinco)))
Use assoc to access: (assoc 'one words :test #'eq) ! (one uno) Allows you to index by key. (Note, use :TEST.)
(defun translate (x) (second (assoc x words :test #'eq))) (translate 'one) What is the result? ! uno
(acons 'z 3 '((y . 2) ( x . 1))) => ((z . 3) (y . 2) ( x . 1))
26
Alists, Assoc (continued)
Can set keys and values: (setf (second (assoc 'one words)) 'un) ! un What is WORDS?
(setf words '((one un) (two dos) (three tres) (four cuatro) ( ve cinco)))
Can also use RASSOC to index by value rather than key, but the a-list must be stored as cons cells, not lists:
(setf words '((one . un) (two . dos) (three . tres) (four . cuatro) ( ve . cinco))) (rassoc 'tres words) ! (three . tres)
You might nd cons cells easier to work with (as in the book).
27
Defstruct (continued)
Constructor, accessor, assignment automatically de ned when data structure is de ned. DEFSTRUCT name-and-options [doc-string] {slot-description}* [Macro]
DEFSTRUCT de nes a new structure, according to name-andoptions, 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:
Assignment:
(typep x 'elephant) ! T
(elephant-p x) ! T
(elephant-color x)
!
WHITE
(setf (elephant-color x) 'grey)
Modi es color slot.
3
Defstruct vs. Other Data Structures (continued)
You COULD build complex data structures out of lists: I'll represent a PERSON as a list of (name age shoe-size parents children) where: name is a list of atoms age is an integer shoe-size is an integer parents is a list of names children is a list of names
For example: (setq person-1 '((betty byte) 22 4 ((boopsie byte)(barney byte)) ((bobby byte)(belinda byte)))) => ((BETTY BYTE) 22 4 ((BOOPSIE BYTE) (BARNEY BYTE)) ((BOBBY BYTE) (BELINDA BYTE)))
We could then write some functions to access elements of a PERSON: (defun person-name (p) (first p)) => PERSON-NAME (defun person-age (p) (second p)) => PERSON-AGE (defun person-shoe-size (p) (third p)) => PERSON-SHOE-SIZE (defun person-parents (p) (fourth p)) => PERSON-PARENTS (defun person-children (p) (fifth p)) => PERSON-CHILDREN (person-name person-1) => (BETTY BYTE) (person-shoe-size person-1) => 4 (person-children person-1) => ((BOBBY BYTE) (BELINDA BYTE))
4
Defstruct vs. Other Data Structures (continued)
We could also write a function with keyword arguments to make the construction of new PERSONs easier: (defun make-person (&key (name '(ordinary jo)) (age 20) (shoe-size 5) (parents nil) (children nil)) "Returns a PERSON with the given attributes" (list name age shoe-size parents children)) => MAKE-PERSON (make-person :name '(consina cadarowitz)) => ((CONSINA CADAROWITZ) 20 5 NIL NIL) (make-person :name '(consina cadarowitz) :age 15 :parents '((candy car)(charlie cdr))) => ((CONSINA CADAROWITZ) 15 5 ((CANDY CAR) (CHARLIE CDR)) NIL)
We could also write functions to DESTRUCTIVELY modify PERSONs. This requires the use of SETF: (defun set-person-name (p new-name) (setf (car p) new-name)) => SET-PERSON-NAME (set-person-name person-1 '(boxcar willie)) => (BOXCAR WILLIE) person-1 => ((BOXCAR WILLIE) 22 4 ((BOOPSIE BYTE) (BARNEY BYTE)) ((BOBBY BYTE) (BELINDA BYTE)))
5
Defstruct (continued)
DEFSTRUCT makes life much easier: (defstruct person (name '(ordinary jo)) (age 20) (shoe-size 5) (parents nil) (children nil)) => PERSON
This AUTOMATICALLY does the following:
Creates a new data type called PERSON, which is a STRUCTURE with 5 SLOTS.
De nes 5 new functions PERSON-NAME, PERSONAGE, PERSON-SHOE-SIZE, PERSON-PARENTS, and PERSON-CHILDREN, each of which takes a PERSON as its argument and returns the associated slot of that PERSON.
De nes a predicate PERSON-P of one argument that is true if its argument is a PERSON.
De nes a function MAKE-PERSON with 5 keyword arguments corresponding to the slots of PERSONs. Note: (1) Structures are not lists; (2) Structures may be destructively modi ed using SETF and the automatically de ned access functions; (3) DEFSTRUCT has many more features that we'll ignore for now. 6
Defstruct (continued) (setq person-2 (make-person :name '(sally silicon))) => #S(PERSON :NAME (SALLY SILICON) :AGE 20 :SHOE-SIZE 5 :PARENTS NIL :CHILDREN NIL) person-2 => #S(PERSON :NAME (SALLY SILICON) :AGE 20 :SHOE-SIZE 5 :PARENTS NIL :CHILDREN NIL) (person-name person-2) => (SALLY SILICON) (person-shoe-size person-2) => 5 (person-p person-2) => T (person-p nil) => NIL (person-p person-1) => NIL person-2 => #S(PERSON :NAME (SALLY SILICON) :AGE 20 :SHOE-SIZE 5 :PARENTS NIL :CHILDREN NIL) (setf (person-children person-2) '((sam silicon))) => ((SAM SILICON)) person-2 => #S(PERSON :NAME (SALLY SILICON) :AGE 20 :SHOE-SIZE 5 :PARENTS NIL :CHILDREN ((SAM SILICON)))
7