Common Lisp Tips

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:

;; 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 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)
3.142857

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

* (/ 22.0 7.0)
3.142857142857143

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*) 
    (print-marketing-report))
"Source,Hits
twitter,243
google,805
direct,47
"

To write it out to a file:

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

Oct 04

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)

Oct 03

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)

(Thanks to stassats for the flies and mice.)

Oct 02

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 string
          if (char= char split-character)
          do (push (get-output-stream-string stream) result)
          else
          do (write-char char stream))
    (push (get-output-stream-string stream) result)
    (nreverse result)))

It could be used like this:

* (split "The quick brown fox.")
("The" "quick" "brown" "fox.")

I learned about this technique from an Erik Naggum post.

Oct 01

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)))
  (app:noisy-code))

Sep 30

http://slime-tips.tumblr.com/