October 2011
24 posts
Initialize a vector with map-into
Novices who want an array of distinct things sometimes write something like this, where make-foo returns a fresh object of some sort:
(make-array 42 :initial-element (make-foo))
This actually creates a vector containing the same identical (eq) object at all 42 indexes. The initial-element argument is evaluated only once to produce the result, and the result is used 42 times to initialize the...
Graham Crackers
If you’re trying to learn Common Lisp from Paul Graham’s ANSI Common Lisp (which I don’t recommend), be sure to read Graham Crackers. These course notes outline some of the cases where the style of ANSI Common Lisp differs from good Common Lisp style.
Division shortcuts
floor, truncate, and related functions can take an optional second argument to use as the divisor. The default value is the integer 1. Instead of (floor (/ x 42)) you can use (floor x 42).
:initform and :default-initargs
There are two means of automatic initialization of CLOS slot values, :initform and :default-initargs. Chris Riesbeck makes a strong case for preferring :default-initargs.
Here’s a simplified example of the syntactic difference, taken from some Quicklisp code. Consider modeling version control commands with CLOS. Here’s a possible design:
(defclass vc-command ()
((command
...
Multiple value division
floor, truncate, and related functions return multiple values for division-related operations: the quotient, and the remainder. There are several situations where both values are useful.
For example, if you have a vector of octet values and you want to find the value of a particular bit, you can use something like this:
(defun bit-ref (octet-vector index)
(multiple-value-bind (octet-index...
The Common Lisp and Unix epochs
The Common Lisp epoch begins at 00:00 on January 1, 1900, GMT. get-universal-time returns a universal time, defined as the number of seconds elapsed since then.
The Unix epoch begins at 00:00 on January 1, 1970, GMT. time() returns the number of seconds elapsed since then.
It’s easy to get the difference between a Common Lisp universal time and a Unix epoch timestamp:
*...
"How do I apply AND?"
I sometimes see a question like this: “I have a list of things and I want to see if they’re all true, so how can I apply ‘and’ to it?” Since and is a macro, it can’t be applied, but there are standard functions that can answer the question instead.
(every #’identity list) will return nil as soon as it encounters any nil entries in list, and true if no...
Controlling loop flow with simple restarts
Sometimes you know in advance how you want to change control flow in a loop. Other times you might want to defer that decision by offering a restart. For example, here’s a loop with a pair of with-simple-restarts:
(with-simple-restart (stop-processing "Stop processing users")
(dolist (user (pending-user-list))
(with-simple-restart (skip "Skip user ~A" user)
(process-user...
Slow pretty-printing
Producing “pretty” output can be time consuming. Some implementations do a lot of work to determine whether to e.g. break lines when printing with functions like print and format.
If printing is a bottleneck and aesthetic output isn’t required, binding or setting *print-pretty* to NIL can significantly improve output speed for some implementations.
(setf values)
To assign the multiple return values of a function to multiple variables, you could use this:
(multiple-value-setq (whole partial) (truncate x 1024))
(setf values) is more general, and works on places:
(setf (values whole partial) (truncate x 1024))
(setf (values (aref v 0) (aref v 1)) (truncate x 1024))
Fine-grained control flow
The “do” macros (do, do*, dolist, and dotimes) have bodies that act like tagbody. You can put go tags anywhere in the body and use go to jump to them from arbitrary places. This can be useful for skipping, retrying, or otherwise changing the flow of iteration.
For example:
(dolist (users (get-user-list))
:retry
...
(when some-condition
(go :retry)
...)
Comparing many objects
The numeric comparison functions =, /=, <, <=, >, and >= can take more than two arguments. This is handy to e.g. check if several variables have monotonically increasing numeric value:
(< a b c d)
For numbers in a sufficiently short list, apply does the trick:
(apply #'< list)
Non-numeric comparison functions generally take exactly two objects to compare, so e.g. (string= x y...
:start and :end with parse-integer
parse-integer takes :start and :end arguments, so you don’t have to extract integer subsequences from strings to pass them to parse-integer. For example, to parse date strings that look like “2011-10-01” into year, month, and date integers, you can do this:
(defun parse-date (string)
"Parse a date string in the form YYYY-MM-DD and return the
year, month, and day as multiple...
Converting characters to integers
If you have the character #\7 and you want the integer 7, you might be tempted to use (parse-integer (string char)) or even this ASCII-oriented technique:
(- (char-int char) (char-int #\0))
While the former is specified to give the right answer, the latter will only work by coincidence. The spec does discuss character ordering, but it makes no guarantees about the values returned by char-int or...
Multi-line format control strings
You can break up long format control strings with ~ at the end of a line. For example:
* (format t "It was the best of times, ~
it was the worst of times.")
It was the best of times, it was the worst of times.
The ~, newline, and all whitespace following the newline are removed from the output, so you can align the continued control string with the previous line.
The : and @...
PAIP lessons
Paradigms of AI Programming is a great book for learning Common Lisp. Peter Norvig wrote a retrospective on the book and included this list of 52 important lessons:
Use anonymous functions. [p. 20]
Create new functions (closures) at run time. [p. 22]
Use the most natural notation available to solve a problem. [p. 42]
Use the same data for several programs. [p. 43]
Be specific. Use...
Stylish Common Lisp
The Tutorial on Good Lisp Programming Style by Peter Norvig and Kent Pitman is full of useful tips for Common Lisp programmers.
Touching a file
To create an empty file, like the Unix touch command does, you might try something like this:
;; BOGUS
(close (open "foo.txt" :direction :output
:if-does-not-exist :create
:if-exists :append))
open has a :direction option specifically for this purpose, though:
(open "foo.txt" :direction :probe :if-does-not-exist :create)
If “foo.txt” does not exist, it...
Reading floats
When the reader sees a number like “3.0” with no exponent marker, the reader will convert it into a single-float by default. You can change what float type is used for conversion by changing *read-default-float-format* to another float type.
For example:
* (/ 22.0 7.0)
3.142857
* (setf *read-default-float-format* 'double-float)
DOUBLE-FLOAT
* (/ 22.0 7.0)
3.142857142857143
The...
Redirecting output
Got a function that writes to *standard-output* but you really want to redirect it somewhere else? You can bind the *standard-output* special variable in all the macros that create temporary streams.
For example, to return the output as a string:
* (with-output-to-string (*standard-output*)
(print-marketing-report))
"Source,Hits
twitter,243
google,805
direct,47
"
To write it out to a...
Swapping places
The naive way to swap the values of two places, a and b, is something like this:
;; BOGUS
(setf temp a)
(setf a b)
(setf b temp)
psetf (parallel setf) can do it in one form:
(psetf a b b a)
But rotatef is best:
(rotatef a b)
Pluralization
The ~P format directive can do simple pluralization.
* (format nil "You have ~D goat~:P." 42)
"You have 42 goats."
* (format nil "You have ~D goat~:P." 1)
"You have 1 goat."
* (format nil "You have ~D fl~@:P." 42)
"You have 42 flies."
* (format nil "You have ~D fl~@:P." 1)
"You have 1 fly."
Irregular plurals are more complicated:
(format nil "You have ~D ~:*~[mice~;mouse~:;mice~]." n)
...
string output streams
get-output-stream-string doesn’t just return the string accumulated so far in a string-stream. It also resets the accumulation, so you can use the same stream multiple times for separate results. Here’s a simple string splitter:
(defun split (string &optional (split-character #\Space))
(let ((result '())
(stream (make-string-output-stream)))
(loop for char across...
Discarding output
Unix shell nerds discard unwanted output by redirecting it to /dev/null. The equivalent in Common Lisp is the empty broadcast stream returned by a call to make-broadcast-stream with no arguments.
For example, if you have some code that normally prints to *standard-output*, but you want that output discarded, you can do something like this:
(let ((*standard-output* (make-broadcast-stream)))
...