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


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

Lazy slots

The slot-unbound generic function is useful for initializing slot values on demand.

(defmethod slot-unbound (class (object my-object) 
                         (slot-name (eql 'my-lazy-slot)))
  (setf (slot-value object 'my-lazy-slot)
        (expensive-computation object)))

This method computes the value and initializes the slot’s value. The return value of setf becomes the return value of the initial call that triggered the slot-unbound. Since the slot is no longer unbound, subsequent access to the slot will return the saved value without doing the expensive computation.

The slot can later be cleared with slot-makunbound if it needs to be reinitialized on demand.


Dynamic format control

Most format control directives can take parameters. These parameters are often embedded in the control string. For example, to print some text at column 16, you could do this:

* (format t "~16THello, world")
                Hello, world

What if you only know the parameter at runtime? You might be tempted to try something like this:

* (format t (format nil "~~~DTHello, world" width))    ; BOGUS
                Hello, world

Don’t do that! You can use “v” and pass the parameter as a separate argument to format:

* (format t "~vTHello, world" 16)
                Hello, world

Here’s a function that prints the bits in an integer with a configurable width:

(defun bits (integer &optional (width 8))
  (format t "~v,'0B" width integer))

* (bits 42)

* (bits 42 16)

This is one of several ways to avoid creating a format control string at runtime. (format t (format nil …) …) is almost always a mistake.