Practical Common Lisp
- Chapter 1 Introduction: Why Lisp?
- Chapter 2 Lather, Rinse, Repeat: A Tour of the REPL
- Chapter 3 Practical: A Simple Database
- Chapter 4 Syntax and Semantics
- Chapter 5 Functions
- Chapter 6 Variables
- Chapter 7 Macros: Standard Control Constructs
- Chapter 8 Macros: Defining Your Own
- Chapter 9 Practical: Building a Unit Test Framework
- Chapter 10 Numbers, Characters, and Strings
- Chapter 11 Collections
- Chapter 12 They Called It LISP for a Reason: List Processing
- Chapter 13 Beyond Lists: Other Uses for Cons Cells
- Chapter 14 Files and File I/O
- Chapter 15 Practical: A Portable Pathname Library
- Chapter 16 Object Reorientation: Generic Functions
- Chapter 17 Object Reorientation: Classes
- Chapter 18 A Few FORMAT Recipes
- Chapter 19 Beyond Exception Handling: Conditions and Restarts
- Chapter 20 The Special Operators
- Chapter 21 Programming in the Large: Packages and Symbols
- Chapter 22 LOOP for Black Belts
- Chapter 23 Practical: A Spam Filter
- Chapter 24 Practical: Parsing Binary Files
- Chapter 25 Practical: An ID3 Parser
- Chapter 26 Practical: Web Programming with AllegroServe
- Chapter 27 Practical: An MP3 Database
- Chapter 28 Practical: A Shoutcast Server
- Chapter 29 Practical: An MP3 Browser
- Chapter 30 Practical: An HTML Generation Library, the Interpreter
- Chapter 31 Practical: An HTML Generation Library, the Compiler
- Chapter 32 Conclusion: What's Next?
Chapter 1 Introduction: Why Lisp?
(no notes)
Chapter 2 Lather, Rinse, Repeat: A Tour of the REPL
install lispbox
slime keys:
- compile defun:
c-c c-c
- switch to repl:
c-c c-z
- close parents:
c-c c-q
- load file:
(load "filename.lisp")
- load fast-load file:
(load (compile-file "filename.lisp"))
(also read slime documentation)
Chapter 3 Practical: A Simple Database
(no notes)
Chapter 4 Syntax and Semantics
(no notes were taken, the whole chapter is important)
Chapter 5 Functions
Defining New Functions
(defun name (parameter*)
"Optional documentation string."
body-form*)
Any symbol can be used as a function name.
The last expression is returned as the value of the function.
Optional Parameters
(defun foo (a b &optional c d) (list a b c d))
;; with default value provided
(defun foo (a &optional (b 10)) (list a b))
;; and with supplied predicate
(defun foo (a b &optional (c 3 c-supplied-p))
;; the default-value expression can refer to parameters that occur earlier in the parameter list
(defun make-rectangle (width &optional (height width)) ...)
Rest Parameters
;; numbers is a list
(defun + (&rest numbers) ...)
Keyword Parameters
keywords are names that start with a colon and that they're automatically defined as self-evaluating constants.
(defun foo (&key a b c) (list a b c))
;; with default value and supplied-p
(defun foo (&key (a 0) (b 0 b-supplied-p) (c (+ a b)))
;; specify the parameter to be different from the name of the actual parameter
(defun foo (&key ((:apple a)) ((:box b) 0) ((:charlie c) 0 c-supplied-p))
;; call with
(foo :apple 10 :box 20 :charlie 30) ==> (10 20 30 T)
Mixing Different Parameter Types
they must be declared in the order I've discussed them: first the names of the required parameters, then the optional parameters, then the rest parameter, and finally the keyword parameters.
either &optional
or &rest
parameters combined with &key
parameters, can lead to somewhat surprising behavior.
Function Return Values
use the RETURN-FROM
special operator to immediately return any value from the function.
(defun foo (n)
(dotimes (i 10)
(dotimes (j 10)
(when (> (* i j) n)
(return-from foo (list i j))))))
Functions As Data, a.k.a. Higher-Order Functions
In Lisp, functions are just another kind of object. The special operator FUNCTION
provides the mechanism for getting at a function object.
CL-USER> (defun foo (x) (* 2 x))
FOO
CL-USER> (function foo)
#<Interpreted Function FOO>
;; The syntax #' is syntactic sugar for FUNCTION
CL-USER> #'foo
#<Interpreted Function FOO>
two functions for invoking a function through a function object: FUNCALL
and APPLY
FUNCALL
is the one to use when you know the number of arguments you're going to pass to the function at the time you write the code.
(foo 1 2 3) === (funcall #'foo 1 2 3)
FUNCALL
, however, doesn't do you any good when the argument list is known only at runtime.
That's where APPLY
comes in. the 2nd parameter is a list, it then applies the function to the values in the list.
APPLY
can also accept "loose" arguments as long as the last argument is a list.
APPLY
doesn't care about whether the function being applied takes &optional
, &rest
, or &key
arguments, the final list must be a legal argument list for the function with enough arguments for all the required parameters and only appropriate keyword parameters.
Anonymous Functions
(lambda (parameters) body)
One way to think of LAMBDA
expressions is as a special kind of function name where the name itself directly describes what the function does. This explains why you can use a LAMBDA
expression in the place of a function name with #'
.
(funcall #'(lambda (x y) (+ x y)) 2 3) ==> 5
;; You can even use a LAMBDA expression as the "name" of a function in a function call expression.
((lambda (x y) (+ x y)) 2 3) ==> 5
LAMBDA
expressions can be used anywhere a normal function name can be.
Chapter 6 Variables
Common Lisp supports two kinds of variables: lexical and dynamic. These two types correspond roughly to "local" and "global" variables in other languages.
Common Lisp is dynamically typed–type errors are detected dynamically. On the other hand, Common Lisp is a strongly typed language in the sense that all type errors will be detected.
;; this function defines three variables--x, y, and z--to hold its arguments.
(defun foo (x y z) (+ x y z))
;; binds the three variables x, y, and z with initial values 10, 20, and NIL
(let ((x 10) (y 20) z)
...)
Like function parameters, variables introduced with LET
are rebound each time the LET
is entered.
The scope of function parameters and LET
variables is delimited by the form that introduces the variable. This form--the function definition or the LET
–is called the binding form.
(defun foo (x)
(format t "Parameter: ~a~%" x) ; |<------ x is argument
(let ((x 2)) ; |
(format t "Outer LET: ~a~%" x) ; | |<---- x is 2
(let ((x 3)) ; | |
(format t "Inner LET: ~a~%" x)) ; | | |<-- x is 3
(format t "Outer LET: ~a~%" x)) ; | |
(format t "Parameter: ~a~%" x)) ; |
Lexical Variables and Closures
By default all binding forms in Common Lisp introduce lexically scoped variables. Lexically scoped variables can be referred to only by code that's textually within the binding form.
;; The binding of count created when the flow of control entered the LET form will stick around for as long as needed
(let ((count 0)) #'(lambda () (setf count (1+ count))))
The anonymous function is called a closure because it "closes over" the binding created by the LET
.
The key thing to understand about closures is that it's the binding, not the value of the variable, that's captured.
Dynamic, a.k.a. Special, Variables
Common Lisp provides two ways to create global variables: DEFVAR
and DEFPARAMETER
.
The difference between the two forms is that DEFPARAMETER
always assigns the initial value to the named variable while DEFVAR
does so only if the variable is undefined.
global variables are conventionally named with names that start and end with *
if you want to temporarily redefine *standard-output*, the way to do it is simply to rebind it, say, with a LET.
(let ((*standard-output* *some-other-stream*))
(stuff))
The name of every variable defined with DEFVAR
and DEFPARAMETER
is automatically declared globally special.
This means whenever you use such a name in a binding form–in a LET
or as a function parameter or any other construct that creates a new variable binding–the binding that's created will be a dynamic binding. This is why the *naming* *convention* is so important.
Constants
All constants are global and are defined with DEFCONSTANT
.
many Lisp programmers follow a naming convention of using names starting and ending with +
for constants.
Assignment
To assign a new value to a binding, you use the SETF
macro
(setf place value)
;; assign to multiple places in sequence
(setf x 1 y 2)
SETF
returns the newly assigned value.
Other Ways to Modify Places
(incf x) === (setf x (+ x 1))
(decf x) === (setf x (- x 1))
(incf x 10) === (setf x (+ x 10))
(rotatef a b) ;; swaps the values of the two variables and returns NIL.
(shiftf a b 10) ;; shifts them to the left, return the original value of the first argument