Common Lisp Tips

Oct 12

: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 values."
  (values (parse-integer string :start 0 :end 4)
          (parse-integer string :start 5 :end 7)
          (parse-integer string :start 8 :end 10)))

Oct 11

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 char-code.

What to use, then? digit-char-p not only returns a true value if its first argument represents a digit, the true value it returns is the integer value of that digit:

* (digit-char-p #\7)

It also works with other radixes:

* (digit-char-p #\a 16)

If the character is not a digit, digit-char-p returns nil.

Oct 10

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 @ modifiers have additional meaning:

With a :, the newline is ignored, but any following whitespace is left in place. With an @, the newline is left in place, but any following whitespace is ignored.

Oct 09

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:

Oct 08

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.

Oct 07

Touching a file

To create an empty file, like the Unix touch command does, you might try something like this:

(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 will be created. The stream returned is already closed. The spec says this:

[:probe causes] the creation of a “no-directional” file stream; in effect, the file stream is created and then closed prior to being returned by open.

Oct 06

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)

* (setf *read-default-float-format* 'double-float)

* (/ 22.0 7.0)

The printer will also omit exponent markers if the float type of the number being printed matches *read-default-float-format*.

Oct 05

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

To write it out to a file:

* (with-open-file (*standard-output* #p"file.txt" :direction :output)

Oct 04

Swapping places

The naive way to swap the values of two places, a and b, is something like this:

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

Oct 03


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)

(Thanks to stassats for the flies and mice.)