Monthly Archives: May 2014

The less-familiar parts of Lisp for beginners — read-delimited-list

We now come across another Lisp feature, read-delimited-list.  This function is generally expected to be used in the construction of reader macros.  You might want to review the discussion of reader macros under gensym and get-dispatch-macro-character.  You would use it to build a reader macro that interprets a sequence of objects between bounding characters as being a list.  The standard reader macro for the #\( character could invoke (read-delimited-list #\)) to retrieve the list between the parentheses.

Because this function doesn’t have any resilience for handling bad cases, it is fairly brittle, and so I would tend to discourage its use in other contexts where the input stream format is less rigidly enforced.

The less-familiar parts of Lisp for beginners — read and read-preserving-whitespace

I’m going to take a brief stop here at read.  While that’s a fairly basic Lisp operation, a newcomer to Lisp might not have seen many examples where programs take input.  So, we’ll talk about read and read-preserving-whitespace a little, to point out what they are, and what they are not.

These two functions are very similar, differing only in how they treat trailing whitespace (read consumes all whitespace up to the start of the next object, or the end-of-file, while read-preserving-whitespace stops immediately after the object read).  It’s important to note, though, that they are not general-purpose input methods.  The read function consumes enough characters from its input stream to produce a single Lisp object.  It is assumed that the input stream consists of readable objects (by the definition of *print-readably*, see the earlier article on print).  These functions do not handle badly-formatted input well, raising an error if the input can’t be interpreted as a Lisp object.

If you’re writing code that reads in data files, or parses English text, you will probably not be doing it with read.  You’ll use, instead, read-line, which returns a string of all the text up to the next newline or the end of file.  You will then use string parsing techniques to interpret the stream.

Places where I have used read are in client-server command protocols.  Rather than sending bare text sequences over the channel, I send lists which contain keywords and arguments related to the information exchanged.  Since I control both ends of the connection, any read failures are related to bugs in the sending program, which I must find and fix.

The less-familiar parts of Lisp for beginners — random

We’ve been discussing features of Lisp that the newcomer to the language might not have encountered before, and now I’m going to talk a bit about random.  While the newcomer arriving from C++ is probably aware of this function, at least in the abstract, there are some dangers that have to be pointed out for the serious users of pseudo-random numbers.

I am going to assert that there are two categories of users of pseudo-random numbers.  First, there are those I’d call the “casual” users.  These users are interested in producing one or more pseudo-random numbers with no eye to recovering or replaying those specific numbers later.  People generating cryptographic keys, or shuffling a deck of cards for a game, or making a salt or other sequence number, would all fall under the category of casual users.  The second group are the “serious” users.  These are the Monte Carlo simulator people (among whose ranks I count myself).  These people would like to be able to run the same program with 1000 different random number sequences, and would like to be able to re-run specific instances for debugging purposes.

The Common Lisp standard defines its pseudo-random number behaviour in a way that accommodates the casual users, but not the serious users.  We’ll start by explaining what the standard does require.

First, the Common Lisp random function takes, as an optional argument, a random state object.  This is quite useful, as it allows the programmer, in effect, to produce multiple independent pseudo-random number streams.  This is unlike the BSD srandom() and random() functions, which modify a global state and don’t allow for easy manipulation of it.  It is, though, somewhat like the GNU libc extensions, srandom_r() and random_r(), which take a state vector as an additional argument.

That random state object used in the Lisp random function can be constructed using make-random-state, operating in one of three modes.  The make-random-state function can be used to create a copy of an existing random state, to create a copy of the default random state, or to build a new random state using an implementation-defined technique that is hoped to give a different result every time it is used.  None of these use cases allows what the programmer would think of as a “random number seed”, which the Monte Carlo modelers absolutely require for their work.  The Common Lisp standard also does not appear to require that the implementation supply a way to produce a readable representation of the current random state, so that it can be recorded and re-loaded from disc.

EDIT #1: 2014-05-18

As pointed out by mch in the comments, the standard does, in fact, require that *random-state* have a *print-readably* representation, so a compliant implementation must be able to store the current random state in a form that can be re-loaded within the same implementation.  There is no guarantee, however, that this representation is meaningful to any other Lisp implementation.

If you need to produce multiple reproducible pseudo-random number sequences, you really have two choices.  You can either write your own pseudo-random number generator, and use that for your work, or you can check the particular implementation of Lisp that you are using for any non-standard pseudo-random number extensions that may be supplied.  For instance, in SBCL there is a non-standard extension, sb-ext:seed-random-state, that allows the programmer to construct a random state variable based on a supplied seed, exactly what the Monte Carlo programmers require.

The less familiar parts of Lisp for beginners — summary N, P

I’ve written a few posts about features of the Lisp language that a newcomer arriving from C++ might not have encountered.  I’m going through them alphabetically, so here is a summary page of the functions beginning with the letters N and P:

nconc

next-method-p

notany

notinline

pairlis

peek-char

print

pprint-dispatch

pprint-exit-if-list-exhausted

pprint-fill, pprint-linear, pprint-tabular

pprint-indent

pprint-logical-block

pprint-newline

pprint-pop

pprint-tab

print-object

print-unreadable-object

proclaim

prog and prog*

progv

The less-familiar parts of Lisp for beginners — progv

Continuing our survey of obscure Lisp features, next is the progv special operator.  This is a feature that allows the creation or local override of dynamic (special) variables at runtime.  It binds the supplied dynamic variable names to supplied values for the duration of the progv block.  There are some good examples in the CLHS, and I’ll review them briefly here.

The first example shows an existing special variable whose value is temporarily replaced within the progv, and in any functions called from it.  When the progv form exits, the special variable reverts to its former value.  If this is unfamiliar to you, review the side discussion on special variables in the earlier article about print.

Here’s the first example, adapted from the CLHS:
*slime-repl sbcl*

CL-USER> (defparameter *x* 1)
*X*
CL-USER> *x*
1
CL-USER> (progv '(*x*) '(2) *x*)
2
CL-USER> *x*
1

In the second example, a local variable is declared in a let, and the progv form lies underneath that.  Despite the fact that the progv is nested within the let, it does not shadow the definition in the let, so a bare evaluation of the symbol returns the value assigned from the let.  Note, though, that let does not intern a symbol, so symbol-value sees the value defined in the progv form.  Also, the print-x function I define in the labels form sees the dynamic value of x, even though it is not passed as a parameter.  The example, adapted from the CLHS, is here:
*slime-repl sbcl*

CL-USER> (labels
             ((print-x ()
                (declare (special x))
                (format t "~A~%" x)))
           (let ((x 3))
             (progv '(x) '(4)
               (print-x)
               (list x (symbol-value 'x)))))
4
(3 4)