2. 2
Goals for Level 2
• Build on Level 1
– Assumes you can write functions, use the
debugger, understand lists and list processing
• Detailed Exposure To
– Functions
– Macros
– Objectoriented programming
3. 3
Format of the Course
• One 2hour presentation each week
– Lecture
– Question and answer
– Sample code
• Lecture notes available online
(http://www.franz.com/lab/)
• Homework
• Oneonone help via email
4. 4
Session 1 (today)
• Advanced features of Lisp functions
• Structures, Hash Tables, Bits and Bytes
• Macros
• Closures
5. 5
Session 2
Common Lisp Object System (CLOS)
• Top Ten things to do in CLOS
• Classes, instances, and inheritance
• Methods
• Class precedence list
• Programming style
6. 6
Session 3
• Performance considerations with CLOS
• The garbage collector
• Error conditions
• Using the IDE to make Windows™ windows
7. 7
Homework
• Lisp is best learnt by handson experience
• Many exercises for each session
• Will dedicate class time to reviewing
exercises
• Email instructor for oneonone assistance
doing the homework or any other questions
relevant to the class
– [email protected]
8. 8
Getting Allegro Common Lisp
• This class is based on version 6.2.
• Trial version should be sufficient for this
module
– Download free from http://www.franz.com/
– Works for 60 days
• I will be using ACL on Windows
• You can use ACL on UNIX, but the
development environment is different,
and I won't be using it.
12. 12
Argument List
• Contains names for variables
• that get their values from arguments provided in
calls to the function
• persist throughout the body of the function
• are not typed
• May contain optional and keyword
arguments
13. 13
Keyword Arguments
(defun printarraysize (array
&key
(stream *standardoutput*)
UseGraphics)
(let ((size (length array)))
(if UseGraphics
(drawsize size stream) ; then
(print size stream)) ; else
size)))
• Call the function like this:
– (printarraysize *myarray*)
• Or like this:
– (printarraysize *myarray* :stream myopenfile)
14. 14
Keyword Arguments
• Call the function with any number of the keyword
arguments, in any order
• Arguments specified as name/value pairs
– :stream myopenfile
• Costs a couple microseconds
– Because Lisp must parse the argument list
• Use when some arguments are only rarely needed
– Source code in callers is greatly simplified
• Or when different calls will need different groups of
arguments
15. 15
Keyword Detail
&key (stream *standardoutput*) UseGraphics
• &key specifies the arguments that follow are
keyword arguments
• Each argument can either be a twoelement list, a
threeelement list, or a single symbol
– Threeelement list is used for suppliedp arguments,
described later on
• Default value of an argument, when not passed by
the caller, is the second element of the
twoelement list, or NIL if not otherwise
specified
16. 16
Optional Arguments
(defun printarraysize (array
&optional
(stream *standardoutput*)
UseGraphics)
(let ((size (length array)))
(if UseGraphics
(drawsize size stream) ; then
(print size stream)) ; else
size)))
• Call the function like this:
– (printarraysize *myarray*)
• Or like this:
– (printarraysize *myarray* myopenfile)
17. 17
Optional Arguments
• Call the function with any number of the optional
arguments
• Lisp determines which one is which by position
– To specify the second optional argument, you must
always specify the first as well
• Costs a couple microseconds
– Because Lisp must parse the argument list
• Use when some arguments are only rarely needed,
and when there aren't very many of them
– Source code in callers is greatly simplified
18. 18
Other possibilities
(defun incr (n &optional inc)
;; if the value of inc was not specified,
;; or if it was specified but not a number,
;; then set it to 1.
(if (not (numberp inc)) (setq inc 1))
(+ n inc))
(defun incr (n &optional (inc 1 incrementp))
;; incrementp TRUE when argument was specified
;; by caller
(if (and incrementp (not (numberp inc)))
(print "nonnumeric increment"))
(+ n inc))
19. 19
Optional and Keyword Arguments
• Ways to specify the argument
• <name> defaults to nil
• (<name> <default value>)
• (<name> <default value> <suppliedp>)
• Use optionals for small numbers of non
required arguments that are easy to
remember
• Use keywords with either large numbers of
nonrequired arguments or ones that
are hard to remember
20. 20
&rest example
(defun add (&rest numbers)
(let ((sum 0))
(dolist (n numbers)
(setq sum (+ sum n)))
sum))
• Call it this way:
• (add 1 2 4)
• Or this way:
• (add 9 1 8 4 8 6)
21. 21
&rest Arguments
• Caller may pass any number of arguments,
there is no limit
• Lisp combines all the arguments into a single
list
• Needed only rarely
• Generally used when all the arguments have
the same type and will participate equally in
the operation
22. 22
Pointers to Functions
• The usual way of calling a function:
– (+ 3 4)
• Alternative way:
– (funcall #'+ 3 4)
• #'+ is a pointer to the function "+"
• Function pointers can be passed as
arguments, returned from functions, or stored
as data
23. 23
Pointers to Functions
• #’add
– Is a reference to the function named ADD
(defun combiner (n)
(if (typep n 'list) #'list #'+))
;; Example of returning a function pointer
;; from a function.
(setq *combiner* (combiner 3))
24. 24
Calling a Function by its Pointer
• #'+
– #<FUNCTION +>
• (funcall #'+ 3 4 5 6)
– 18
• (apply #'+ (list 3 4 5 6))
– 18
25. 25
Using Function References
• Functions can be used just like any other type
object
(defun combiner (x)
(typecase x (number #'+) (list #'append) (t #'list)))
COMBINER
USER(49): (defun combine (&rest args)
(apply (combiner (first args)) args))
COMBINE
USER(50): (combine 2 3)
5
USER(51): (combine '(a b) '(c d))
(A B C D)
USER(54): (combine 'a 'b 'c)
(A B C)
26. 26
Function References
• Commonly used in sorting routines
– (sort '(5 1 4 6) #'<)
– #'< small numbers first
– #'> big numbers first
– #'string< A before Z
– #'string> Z before A
• Often used when mapping over a collection
(vector, list, hash table, etc.)
– (mapcar #'print '(5 1 4 6))
27. 27
Lambdas
• Nameless fns
• (setq function #'(lambda (x y) (* x y)))
• (setq function #'(lambda (x y) (+ x y)))
• (funcall function 3 4)
28. 28
Lambda Example
• Example
(defun incrementall (&rest numbers)
(mapcar #'(lambda (x) (+ x 1))
numbers))
• (incrementall 5 6 7 8)
• (6 7 8 9)
29. 29
Where do you use lambdas
• When a function will have only one caller
and it is relatively trivial
• Saves using up you namespace
• Seeing the code in place may improve
readability in some situations
• Commonly used with mapping functions
30. 30
Other Functions of Functions
• Many tools available to investigate your
environment
31. 31
fboundp
• Determines if a symbol is associated with a
function definition or function binding
(fboundp 'double)
#<Interpreted Function DOUBLE>
32. 32
fdefinition
• (fdefinition '+) retrieves the function
object associated with the argument. Same
as symbolfunction for symbols, but also
works on names that are lists (such as
(setf foo)).
• Functionlambdaexpression retrieves the
definition, if it is available (but the
argument must be a function object, not a
function name)
34. 34
Symbolfunction
• Returns a symbol’s fn
• Note: you can also setf this
> (setf (symbolfunction 'double) #'(lambda (x) (+ x x)))
#<Interpreted Function DOUBLE>
> (double 5)
10
35. 35
Global .vs. Local Functions
• Defun’s are global
• Lambda’s can only be used in place unless
the are assigned or stored on something
• flets and labels can be used to create fns that
are only available in a local context
36. 36
Local Functions labels
• Enables you to define a function and use it
within the scope of the labels
• Think of it like a let* for functions
(defun test3rd ()
(labels ((3rd (lst)
(first (rest (rest lst)))))
(print (3rd '(1 2 3 4)))
(print (3rd '(a b c d)))))
37. 37
SETF Functions
• setf is useful when you have a pair of
operations for getting and setting a value
• In other languages, you would name the pair
Getxxx and Setxxx
• With setf, you have only one name
• (first list) ; gets first element of list
• (setf (first list) 17) ; sets first element to 17
38. 38
SETF Function Definition
;;; Get the first element
(defun 1st (list)
(car list))
;;; Set the first element
(defun (setf 1st) (new list)
(rplaca list new)
new)
39. 39
Using SETF
(setq a '(one two three))
(1st a)
> ONE
(setf (1st a) 1)
> 1
(1st a)
> 1
a
> (1 two three)
40. 40
Many Operations in Lisp itself
are setfenabled
• first, second, third, last, nth, elt
• aref, fillpointer
• Elements or fields of a structure
• Elements or slots of a CLOS instance
• symbolvalue, symbolfunction
• gethash
41. 41
The Idea of Mapping Functions
• You pass a “pointer to a function” as one of
the arguments
• The “pointer to a function” is applied to each
element of a collection
• The results of the individual calls may be
collected up into a list
42. 42
mapcar
• (mapcar fn list &rest morelists)
• Example:
> (mapcar #'print '(a b c))
A
B
C
(A B C)
> (mapcar #'cons '(a b c) '(1 2 3))
((A . 1) (B . 2) (C . 3))
43. 43
maphash
• (maphash function hashtable)
– Hash tables covered in more detail later
(maphash #'(lambda (key value)
(format t "~&k=~A,v=~A" key value)
ht)
44. 44
The Idea of Multiple Values
• Sometimes you want a function to return
more than one value
• Option 1: Make an object that contains the
values
– (defun foo () (list 1 2))
• Option 2: Use Lisp multiple values
– (defun foo () (values 1 2))
– 1 is said to be the “first value”
– 2 is said to be the “second value”
45. 45
multiplevaluesetq
• Use it like setq to capture multiple values
(let ((x 0)(y 0))
(print x) (print y) ; values before
(multiplevaluesetq (x y) (foo))
(print x) (print y) ; values after
)
46. 46
MultipleValueBind
• Use it like let or let* to capture multiple
values in new local variables
(multiplevaluebind (left right) (foo)
(print left)
(print right))
48. 48
Userdefined structures
• Can either use CLOS instances or defstructs
– both store data in a userdefined form
– instances can have behavior in addition
– defstructs are more efficient, but less powerful
• Designed to look similar in code
– slot references look like function calls
– can switch between them during development
49. 49
Structures
• Define with defstruct
• create one with (make<name> …)
• access slot with <name><slotname>
> (defstruct routesegment
nodenumber
start
end)
ROUTESEGMENT
> (setf rs (makeroutesegment :nodenumber 5 :start 2 :end 7))
#S(ROUTESEGMENT NODENUMBER 5 START 2 END 7)
> (routesegmentstart rs)
2
50. 50
defstruct with default values
• Example
> (defstruct point
(x 0)
(y 0))
POINT
> (setf pt (makepoint))
#S(POINT X 0 Y 0)
51. 51
Structures
• Standard Lisp Structures are really just
vectors.
• Accessing an element of a structure is as fast
as accessing an element of an array.
• The reader functions generated by defstruct
get compiled into an vector access of a
specific position (fast!)
• If you redefine the positions, you have to
recompile your code
52. 52
defstruct with type
• Example
> (defstruct (point (:type list))
(x 0)
(y 0))
POINT
> (setf pt (makepoint :x 10 :y 20))
(10 20)
53. 53
defstruct with type and name
• Example
> (defstruct (point (:type list) (:named t))
(x 0)
(y 0))
POINT
> (setf pt (makepoint :x 10 :y 20))
(POINT 10 20)
54. 54
Hash tables
• Pairoriented: Associate keys with values,
just like alists and plists
• Could use lists for small ones, but search
time grows proportional to size of list
• hash table computes hash function to use as
index
– speed largely independent of size
• have to build your own in many other
languages
55. 55
Hash table examples
> (setf ht (makehashtable))
#<HASHTABLE #xDD7410>
> (gethash 'color ht)
NIL
NIL
> (setf (gethash 'color ht) 'brown)
BROWN
> (gethash 'color ht)
BROWN
T
56. 56
gethash’s Multiple values
• gethash returns two values
– value associated with key or NIL
– Whether or not the value was found
• Second value helps you distinguish between
– NIL as the value of the key
– NIL as the value you get when the key has no
value
57. 57
Hash Table Fns
• Examples
> (hashtablep ht)
(#<STRUCTURECLASS HASHTABLE #x891668>)
> (gethash 'color ht)
BROWN
T
> (remhash 'color ht)
T
> (gethash 'color ht)
NIL
NIL
58. 58
clrhash
• Examples:
(setf ht (makehashtable))
#<HASHTABLE #xE02D8C>
> (setf (gethash 'color ht) 'brown)
BROWN
> (gethash 'color ht)
BROWN
T
> (clrhash ht)
#<HASHTABLE #xDD8280>
59. 59
Hash table iteration example
(let ((test (makehashtable)))
(setf (gethash 'a test) "This is the a value")
(setf (gethash 'b test) "This is the b value")
(maphash #’(lambda (sym str)
(format t "~&~A = ~S" sym str))
test))
B = "This is the b value"
A = "This is the a value"
NIL
60. 60
maphash
• Iterate over the contents of the hash table,
pair by pair
• (maphash #'(lambda (key value) ..code..) hash)
62. 62
Bits and Bytes
• Normally represented as lisp integers
• Often used for efficiency
– Speed: some operations may compile into a
single machine instruction
– Size: a bit vector is much smaller than a general
vector
• Often used in combination with foreign
function calls
– Arguments to C++ and WIN32 libraries are often
several "flags" passed as a single integer
63. 63
Bits of Integers
• The #b prefix means a binary notation
USER(1): #b10
2 ; decimal integer 2
USER(2): *printbase*
10 ; numbers normally print in decimal
USER(3): (let ((*printbase* 2)) (print #b10) nil)
10 ; decimal integer 2 printed in binary
NIL
USER(4):
64. 64
Bit Combination
• Inclusive OR of bits
USER(1): (logior #b100 #b110)
#b110
• AND of bits
USER(1): (logand #b100 #b110)
#b100
• Less commonly: logxor, logeqv, lognand,
lognor, logandc1, logandc2, logorc1, logorc2
65. 65
Bit Testing
• (defvar *mask* #b1010)
• (logtest flags *mask*)
– True if the second or fourth bit in FLAGS is “on”
• (logbitp 1 flags )
– True if the second bit in FLAGS is “on”
• (logcount flags)
– Counts the number of bits that are “on”
67. 67
Byte Manipulation
USER(5): (setf (ldb (byte 4 4) flags) #b0011)
USER(6): flags
#b100110111
• This line modifies the second four bits of the
bit field.
68. 68
Shift Operation
• ash arithmetic shift (left)
– (ash 1 10) > 1024
– (ash 255 6) > 3
• Note that there is no assumption of integer
size. You eventually get a bignum if you
keep shifting left.
69. 69
How Many Bits?
• (integerlength #b1000) => 4
• Use it to print a binary number:
(defun binarytostring (bits)
(let* ((L (integerlength bits))
(string (makestring L
:initialelement #0)))
(dotimes (I L)
;; Note that bit zero is on the right
;; of the string (character L1).
(when (logbitp ( L I 1) bits)
(setf (char string I) #1)))
string))
70. 70
Vectors of Bits
(setq vector (makearray 1024 :elementtype ‘bit
:initialelement 0))
;; Access and modify as any vector or array
(setf (aref vector 0) 1)
;; But elements must be either zero or one
(setf (aref vector 0) 2) ; ERROR
72. 72
What are Macros?
• Macros take lisp code as input and return lisp
code as output. For example,
When evaluating: (incf x)
Evaluate this instead: (setf x (+ 1 x))
(defmacro incf (place)
(list 'setf place (list '+ 1 place)))
73. 73
Macroexpansion
• When the evaluator sees (incf a)
– It notices that INCF names a macro
– It “runs” or macroexpands the macro, which
transforms the line of code into:
• (setf a (+ 1 a))
– It evaluates that expression instead
– So when you type (incf a) to the lisp listener, it
is as if you had typed (setf a (+ 1 a))
74. 74
Macro Evaluation is Different
• for functions
– gets the function name
– evaluates all the args
– applies the function to the eval’ed args
• for macros
– passes arguments without evaluating them
– the macro function returns another expression
– evaluator evaluates that expression instead of the
original
75. 75
Recursive Macros
• Macros can macroexpand into other macros
• For example
– WHEN macroexpands into COND
– COND macroexpands into IF
• The evaluator (and the compiler) recursively
macroexpand an expression until there are no
more macros left
76. 76
Macroexpand function
• macroexpand is function which lisp uses to
call macro function and get result
– it keeps recursively macroexpanding till no
macros are left
• macroexpand1 just does one step of
macroexpansion
• (macroexpand1 '(incf x))
– (setq x (+ x 1))
77. 77
macro functions
• stored in same function cell of symbol
• stored in a different format so that the system
can tell it is a macro function
• macrofunction <symbol> will return nil if
the symbol has a normal function definition
or none, but will return the expansion
function if the symbol names a macro
78. 78
Macro Examples
• Macros are just functions that transform expressions
• Use macroexpand1 to see definition
> (defmacro nil! (x)
(list 'setf x nil))
NIL!
> (setq x 5)
5
> (nil! x)
NIL
> x
NIL
> (macroexpand1 '(nil! x))
(SETF X NIL)
79. 79
Backquote
• Used extensively in macros
• Used by itself is equivalent to quote
• Protects args from evaluation
• comma (,) will unprotect
> (setq a 1 b 2)
2
> `(a is ,a b is ,b)
(A IS 1 B IS 2)
80. 80
Backquote example
(defmacro incf (place)
`(setf ,place (+ 1 ,place)))
• Compared to earlier definition of INCF, this
version is shorter, more concise, and easier to
understand (but equivalent)
(defmacro incf (place)
(list 'setf place (list '+ 1 place)))
81. 81
,@
• Like comma but splices in list
> (setq lst '(1 2 3 4))
(1 2 3 4)
> `(here are the numbers ,@lst)
(HERE ARE THE NUMBERS 1 2 3 4)
82. 82
&body
• &body is like &rest, but typically reserved for macros
• Example: WITH (shorthand for LET, use it to create
one local variable)
(defmacro with ((var &optional val) &body body)
`(let ((,var ,val))
,@body))
(with (a)
(print a)) ;; this example transforms into:
(let ((a nil))
(print a))
83. 83
withopenfile example
More complex example. There is a builtin lisp macro of the
same name that does almost exactly this.
(defmacro withopenfile ((var &rest args) &body body)
`(let ((,var (open ,@args))) ; open file
(unwindprotect
(progn ,@body) ; execute body
(when (streamp ,var) (close ,var)))) ; close
• unwindprotect is talked about later on, but it
ensures the file is closed even if an error
occurs
84. 84
Macro Examples
• Orderedbounds
(defmacro orderbounds (left bottom right top)
`(progn (if (> ,left ,right) (rotatef ,left ,right))
(if (> ,bottom ,top) (rotatef ,bottom ,top))))
(setq left 10)
(setq right 0)
(setq top 50)
(setq bottom 4)
(orderbounds left bottom right top)
;; Now LEFT is 0, RIGHT is 10,
;; TOP is 4, BOTTOM is 50
85. 85
Macro Examples
• Add onto the end of the list
(defmacro pushlast (item list)
`(setf ,list (nconc ,list (list ,item))))
87. 87
Iteration Macro
(defmacro while (test &body body)
`(do ()
((not ,test))
,@body))
;; Prints even numbers
(setq I 0)
(while (< I 10)
(print I)
(incf I 2))
88. 88
Macro Argument Lists
• Use of &key, &optional, &rest is common in macros
(defmacro withresourcestring ((resourcestring
&key (size 80))
&body body)
`(let ((,resourcestring
(allocateresourcestring :size ,size)))
,@body
(freeresourcestring ,resourcestring)))
89. 89
Macros with Logic
• A macro need not be just a backquoted list
• A macro is an arbitrarily complex function for transforming
one expression into another
(defmacro incf (place)
(if (symbolp place)
`(setq ,place (+ 1 ,place))
`(setf ,place (+ 1 ,place))))
90. 90
Macro writing problems
• A macro is not a function
– Certain uses are not allowed
• multiple evaluation problem
– Inadvertently evaluate args multiple times
• variable capture problem
– Inadvertently shadow a variable name
91. 91
Macros are not Functions
• (apply #’when (> x 3) x)
– This is an error because APPLY only works on
functions
92. 92
Don’t evaluate more than once
• A macro similar to OR
(defmacro or1 (a b)
`(if ,a ,a ,b))
• What happens for: (or1 (print 1) (print 2))
(if (print 1) (print 1) (print 2))
• To avoid multiple evaluation:
(defmacro or2 (a b)
`(let ((temp ,a))
(if temp temp ,b)))
93. 93
Variable Capture
• How would you implement Lisp’s OR
macro?
(defmacro or2 (a b)
`(let ((temp ,a))
(if temp temp ,b)))
(let ((x nil)
(temp 7))
(or2 x temp))
;; Returns NIL (there are two TEMPs)
94. 94
Generate symbols that can’t be
captured
• Gensym
(defmacro or3 (a b)
(let ((symbol (gensym)))
`(let ((,symbol ,a))
(if ,symbol ,symbol ,b))))
95. 95
Turning Functions into Macros
• Do it to eliminate a function call
• Do it when the function is not recursive
(defun second (x) (cadr x))
(defmacro second (x) `(cadr ,x))
(defun sum (&rest numbers) (apply #’+ numbers))
(defmacro sum (&rest numbers) `(+ ,@numbers))
96. 96
When to Use Macros
• Macros help avoid code duplication
(defmacro withresourcestring ((resourcestring
&key (size 80))
&body body)
`(let ((,resourcestring
(allocateresourcestring :size ,size)))
,@body
(freeresourcestring ,resourcestring)))
97. 97
When to use macros
• You have to use macros when
– you need to control evaluation
• binding (like local variables in LET)
• conditional evaluation (like AND or OR or IF)
• looping (like DO)
• Simplification without a function call (like (SETF CAR)
expanding into RPLACA)
• You can use macros to
– do computation at compiletime
– expand in place and avoid a function call
– save typing or code duplication, and to clarify code
98. 98
Problems with using Macros
• You cannot use a macro if you have to funcall or
apply it
• Macro definitions are harder to read
• Macro definitions can be harder to debug
– The code you see in the backtrace may bear little
resemblance to your source code
• Although macros can expand recursively into other
macros, you can’t usually write a recursive
algorithm with them.
99. 99
Redefining Macros
• Code for macro expansion captured in compiled
files of callers of the macro
• If you change the definitions of the macro itself,
you have to recompile the callers
• Defsystem tool allows you to record these
dependencies once
100. 100
learning more
• A lot of Common Lisp is really implemented as
macros
• Looking at the expansions of these can teach you a
lot about how macros work
• (pprint (macroexpand1 ‘(defun foo (a) (+ a 1))))
102. 102
A Macro is not a Function
• (apply #’when (> x 3) (print x))
– This is an error
103. 103
Macro Recursion is not like
Function Recursion
(defmacro nth* (n list)
`(if (= ,n 0) (car ,list)
(nth* ( ,n 1) (cdr ,list))))
• macroexpanding nth* will macroexpand
forever when compiled in a function like
(defun foo (x l) (nth* x l))
• Think of the code you want the macro to
expand into
105. 105
Multiple Evaluation
This definition of OR evaluates A twice
(defmacro or* (a b)
`(if ,a ,a ,b))
Do it this way instead
(defmacro or* (a b)
`(let ((temp ,a)) (if temp temp ,b)))
106. 106
Order of Evaluation
• Evaluation order should be left to right
(defmacro and* (a b)
`(let ((temp2 ,b) (temp1 ,a))
(if (not temp1) nil
(if (not temp2) nil temp2))))
(and* (setq x 2) (setq x 3))
;; Returns 3 but x is 2!
107. 107
Avoid Destroying Arg Lists
(defmacro sumplus1 (&rest args)
(cons '+ (nconc args (list 1))))
(defun foo () (sumplus1 2 3))
(foo) returns 6
(foo) returns 7
(foo) returns 8
• Because the macro is destructively modifying
the source code of the caller
• The source code stops changing
when you compile it
108. 108
Variable Capture
• Using the OR* macro from a few slides back…
(setq x nil)
(let ((temp 7))
(or* x (+ temp 1)))
• This code attempts to add NIL to 1
• Because the macroexpansion of OR* causes
TEMP to get rebound to NIL
109. 109
Global Variable Capture
(defvar width 5) ; global variable
(defmacro noticewidth (w)
`(setq width ,w))
(defun drawrectangle (x y width height)
(noticewidth width)
(noticeheight height)
. . .)
• The macro does not affect the global
variable as intended
110. 110
Avoiding Global Variable Capture
• (defvar *width* 5)
• Use naming conventions that distinguish
local from global variables.
111. 111
Avoiding Variable Capture with
Gensym
(defmacro or* (a b)
(let ((symbol (gensym)))
`(let ((,symbol ,a))
(if ,symbol ,symbol ,b))))
112. 112
Avoiding Variable Capture with
Scope
(defmacro sumsquaresw (x y)
`(let* ((x0 ,x) ; WRONG (X0 captured)
(y0 ,y) ; problem occurs in this line
(x2 (* x0 x0)) ;; if form y refers
(y2 (* y0 y0))) ;; to x0
(+ x2 y2)))
(defmacro sumsquaresr (x y)
`(let ((x0 ,x) ; RIGHT
(y0 ,y))
(let ((x2 (* x0 x0))
(y2 (* y0 y0)))
(+ x2 y2))))
114. 114
SETF: a special kind of macro
> (setq a nil) ; setq only for symbols
a
> (setf a '(one two three)) ; setf of a symbol
(ONE TWO THREE)
> (setf (first a) 1) ; setf of a "place"
1
> A ; list was permanently changed
(1 2 3)
115. 115
Builtin SETF operations
• Lisp knows how to use SETF with many
things (but not everything)
– Lists: first, second, elt, nth, car, cdr
– Arrays: aref
– Objects: slotvalue
– Bits: ldb
– Hash tables: gethash
116. 116
Rolling Your Own
• Define your own SETF procedures in one of
two ways:
• By functions and methods:
(defmethod (setf x) (new (object point))
(setf (slotvalue object 'x) new))
• By defsetf macros (next slide)
117. 117
Using DEFSETF
(defsetf car (x) (new)
`(progn (rplaca ,x ,new) ,new))
(defsetf x (point) (new)
`(setf (aref ,point 1) ,new))
119. 119
What Are Closures?
• Closures are
– Executable functions
– Objects with state
• They usually appear as lambda expressions
• Nothing like them in C, C++, Java, VB, or
any other traditional language
• They are functions with a “memory”
120. 120
A Simple Closure
(setq closure
(let ((list '(one two three four five)))
#’(lambda () (pop list))))
⇒ #<Interpreted Closure (unnamed) @ #x2083818a>
(funcall closure)
⇒ ONE
(funcall closure)
⇒ TWO
(funcall closure)
⇒ THREE
121. 121
What Happened?
• LAMBDA created an unnamed function
• To observe the rules of lexical scoping, the
function can continue to reference LIST even
after returning from the LET
• The function makes a “snapshot” of the
variable at the time it is evaluated
• The function carries that snapshot with it as a
segment of data
122. 122
Another Example
(let ((list '(one two three four five)))
(setq closure1 #’(lambda () (pop list)))
(setq closure2 #’(lambda () (pop list))))
;; two closures, both with a reference to LIST
(funcall closure1)
⇒ ONE
(funcall closure2)
⇒ TWO
123. 123
Now What Happened?
• The two closures both reference a single,
shared closure variable
• They each can see modifications that the
other closure makes
124. 124
Implicit Closures
• Closures happen implicitly when ever a
function refers to something in the lexical
environment
> (defun addtolist (num lst)
(mapcar #'(lambda (x) (+ x num)) lst))
ADDTOLIST
125. 125
Closures and Garbage
• Note that creating a closure allocates
memory and can be a source of garbage and
slowness.
• If the closure can be allocated on the stack,
then do so using dynamicextent.
(defun addtolist (num list)
(labels ((adder (x) (+ x num)))
(declare (dynamicextent #’adder))
(mapcar #’adder list)))