Common Lisp Tips

Jan 03

Evaluating the last expression

In the REPL, +, ++, and +++ have as values the three most recently evaluated expressions. A quick way to evaluate the previous expression, especially handy in a REPL without input history, is

#.+

It’s equivalent to (eval +).

(Thanks to Anton Kovalenko.)

Jan 01

Un-displacing an array

Here’s a function to get the underlying array on which a displaced array is based:

(defun undisplace-array (array)
  "Return the fundamental array and the start and end positions into
it of a displaced array."
  (let ((length (length array))
        (start 0))
    (loop
      (multiple-value-bind (to offset) (array-displacement array)
        (if to
            (setq array to
                  start (+ start offset))
          (return (values array start (+ start length))))))))

From an article by Erik Naggum.

Dec 30

Referring to method parameters

In defmethod lambda lists, required parameters that aren’t explicitly specialized default to specializing on the system class t. But there’s a difference between an implicit and explicit specialization on t. The hyperspec explains:

The expansion of the defmethod macro “refers to” each specialized parameter (see the description of ignore within the description of declare). This includes parameters that have an explicit parameter specializer name of t. This means that a compiler warning does not occur if the body of the method does not refer to a specialized parameter, while a warning might occur if the body of the method does not refer to an unspecialized parameter. For this reason, a parameter that specializes on t is not quite synonymous with an unspecialized parameter in this context.

This makes a difference in Clozure CL; here’s what you get if you don’t use an implicitly specialized required parameter:

? (defmethod foo (bar) 42)
;Compiler warnings :
;   In (FOO (T)) inside an anonymous lambda form: Unused lexical variable BAR
#<STANDARD-METHOD FOO (T)>

There is no warning with an explicit specialization:

? (defmethod bar ((baz t)) 42)
#<STANDARD-METHOD BAR (T)>

(Thanks to Hans Hübner.)

Dec 15

Describing objects

Everyone writes new methods for print-object for specialized printing of their own objects. But the output of describe can be specialized as well via describe-object. For example, if you have an object that has a vector of keys and a corresponding vector of values, the standard describe output might not show them in a way that’s very readable:

* (describe record)
#<RECORD {1002A87A03}>
  [standard-object]

Slots with :INSTANCE allocation:
  SOURCE  = #P"data.txt"
  ID      = 47
  KEYS    = #("Rating" "Unit Price" "Description" "Acquired Date")
  VALS    = #(7.2 21 "Blue-fringed tarpaulin with star pattern"..

You can add your own primary or auxiliary methods to change what describe prints:

(defmethod describe-object :after ((record record) stream)
  (write-line "Data values:")
  (loop for key across (keys record)
        for val across (vals record)
        do (format stream "  ~A: ~A~%" key val)))

Then the describe output can be more readable:

#<RECORD {1002B54023}>
  [standard-object]

Slots with :INSTANCE allocation:
  SOURCE  = #P"data.txt"
  ID      = 47
  KEYS    = #("Rating" "Unit Price" "Description" "Acquired Date")
  VALS    = #(7.2 21 "Blue-fringed tarpaulin with star pattern"..
Data values:
  Rating: 7.2
  Unit Price: 21
  Description: Blue-fringed tarpaulin with star pattern
  Acquired Date: 3532942160

Dec 08

Forcing buffered output

Many CL implementations (SBCL in particular) perform output in a buffered manner. Sometimes this may cause a confusion, because it may reverse the order of effects. Like in the following example it may be possible to enter something before seeing ‘?’.

(defun ask ()
   
(princ '?)
   
(read))

Also if you are writing an application, that should interact with some other program (for example over a pipe) and there are hard limits on the interaction speed, buffering can be a problem.

For such cases there’s a standard function finish-output, that initiates dumping any buffered output. There’s also force-output, that does the same thing, but asynchronously (i.e. doesn’t wait for the actual writing to finish).

Nov 06

String functions

Common Lisp has relatively few functions that work exclusively on strings. There is no function, for example, to extract a substring from a string. However, strings are defined as specialized vectors of character objects. Since vectors are sequences, all the sequence-related functions also apply to strings. To extract a substring from a string, use subseq.

Nov 05

A concatenated stream trick

If you pass a stream to a library function that closes it (as with with-open-stream), but you want the stream to remain open, you can wrap the stream in a concatenated stream:

(auto-closing-function (make-concatenated-stream my-stream))

When the concatenated stream is closed, the wrapped stream my-stream remains open.

(Thanks to Rob Warnock for this tip.)

Nov 04

Working on multidimensional arrays

What to use if you have a multidimensional array and you want to do something to each element?

row-major-aref can access an element in a multidimensional array with a single index. array-total-size returns the total number of elements in an array. Together, you can do something like this:

(dotimes (i (array-total-size array))
  (incf (row-major-aref array i) 42))

You can also displace a one-dimensional array to the multidimensional array and work on it with sequence or vector functions:

(let ((vector (make-array (array-total-size array) :displaced-to array)))
  (fill vector 42))

Oct 29

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

42 distinct things can be obtained like this:

(map-into (make-array 42) #'make-foo)

map-into calls make-foo 42 times and returns the initialized array as its result.

Oct 23

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.