Category Archives: Uncategorized

The less-familiar parts of Lisp for beginners — sxhash

Our next function of interest is sxhash.  This function provides an implementation-defined way to obtain a non-negative integer from a Lisp object.  The programmer can then use this in his or her own implementation of a hash table, or some similar application that requires a similar sparse mapping to positive integers.  Note that the standard-defined hash tables produced with make-hash-table cannot be made to use a custom hashing function, however check your implementation, they may have extended it to allow this.  SBCL 1.1.14, for instance, defines a &key argument to make-hash-table, hash-function, which allows a user-supplied function to replace the normal internal one.  It’s also worth noting that SBCL 1.1.14 uses sxhash internally when no hash-function is supplied by the programmer.  Finally, that version of SBCL also defines an internal implementation psxhash function, not visible to the programmer, for use when the hash-table is constructed with an equalp test instead of the default equal.

As noted above, the sxhash function is implementation defined.  The implementation is encouraged to make a good effort to return values distributed over the set of non-negative fixnums.

There are a few things to note about the implementation of sxhash, they are described in the CLHS.

  1. If two objects are equal, they must have the same hash returned by sxhash.
  2. Simple objects of type “bit vectors, characters, conses, numbers, pathnames, strings, or symbols” (to quote the CLHS), must return the same hash even from objects resident in different Lisp images.  This means, for instance, that a hash computed at compile time in the image that compiles some Lisp code must be the same as a hash recomputed on a similar object in a different image that loads the compiled code.  If this seems mysterious, you might want to review the earlier article on make-load-form.
  3. The hash code of an object must not change as long as that object is unchanged, or only changed in such a way that the object remains equal to its earlier form.
  4. As noted above, sxhash should me a good effort to produce good, spread-out values.
  5. The sxhash function should always terminate, even when presented with circular objects.

 

The less-familiar parts of Lisp for beginners — subtypep

Our next obscure function is subtypep.  This function allows the program to determine whether one type is a subtype of a second type.  The return values depend on the actual types available in the implementation, as some implementations may not support all available types, and may alias one type to another, in which case they are subtypes of one another.

The subtypep function can also determine whether a class is derived from another class.  All subclasses of a class “C”, even through multiple levels or through multiple inheritance with an unrelated class, are subtypes of class “C”.

This function is somewhat esoteric in its uses.  It can be used to ensure that a passed parameter is of the correct type for the actions that are about to be attempted on it, or in other branching contexts.  Contexts where you might use typecase could possible be amenable to the use of subtypep.

The less-familiar parts of Lisp for beginners — sublis and nsublis

Now, we look at sublis, and its relative nsublis.  You’ve probably realized by now that those functions with the ‘n’ prefix are generally destructive versions of the corresponding unprefixed function.  That is, the nsublis function is permitted to destroy its input tree.

What the sublis function does is to walk a tree and look up all subtrees and nodes as keys in the association list.  When a match is found, the value corresponding to that key is substituted.  A new tree is created that contains the substituted elements, and that may share elements with the input tree.  I will provide some examples below.

Note that the spec does not appear to provide any hard rules about the order of tree traversal, so the programmer should be careful to avoid changes that might feed back into themselves, as can happen with more complex :test functions.

Here are some examples of sublis use.  Note that if you provide your own test function it has to be able to handle receiving both sublists and nodes in its arguments.
*slime-repl sbcl*

CL-USER> (let ((swaps '((1 . "ONE")
                        (2 . "TWO")
                        (3 . "THREE")
                        (4 . "FOUR")))
               (demo-tree '((1 2)
                            (3 4 5 6)
                            (7 (8 9 10) 1))))
           (sublis swaps demo-tree))
(("ONE" "TWO") ("THREE" "FOUR" 5 6) (7 (8 9 10) "ONE"))
CL-USER> (labels
             ((same-type (x y)
                (and (numberp x)
                     (numberp y)
                     (= (mod x 2)
                        (mod y 2)))))
           (let ((types '((1 . "ODD")
                          (2 . "EVEN")))
                 (demo-tree '((1 2)
                              (3 4 5 6)
                              (7 (8 9 10) 1))))
             (pprint-linear t
                            (sublis types demo-tree 
                                    :test #'same-type))))

(("ODD" "EVEN")
 ("ODD" "EVEN" "ODD" "EVEN")
 ("ODD" ("EVEN" "ODD" "EVEN") "ODD"))
NIL

The less-familiar parts of Lisp for beginners — stream-external-format

Our next obscure function is stream-external-format.  This is entirely an implementation-dependent function.  Some implementations may have different on-disc formats for data, and using a different external format may result in different bytes being delivered to the disc.  All return values from this function have implementation-dependent meanings, so the programmer must be aware of this fact when writing code intended to be portable.

To provide a specific example, SBCL allows the user to open a character stream for writing with any of several external formats.  For instance, one can write in UTF-8, UTF-16BE, UTF-32LE, ISO-8859-5, EBCDIC-US, to name just a few.  Each of these has a different external format keyword, which stream-external-format will return.

The less-familiar parts of Lisp for beginners — stream-element-type

Moving on to the stream-element-type function.  We should first describe streams and their element types.  A stream in Lisp looks a little like an iostream in C++.  It is an opaque object that defines an interaction between the program and what a UNIX system would call a character device (as opposed to a block device).  A stream may take special action to distinguish between text and binary data, and has equivalents to tellp and seekp.

A Lisp stream has an associated element type which must be a subtype of character or of integer.  If it’s a subtype of character, it can be used with functions like format, read, and others.  If it’s a subtype of integer, it can be used with functions like read-byte, write-byte, read-sequence, and write-sequence.

The stream-element-type function allows the programmer to detect at runtime whether a stream is a character stream or an integer stream.  Appropriate behaviour can then take place based on the compatibility with that particular stream.

One thing I will note.  While I was testing behaviour in SBCL 1.1.14, I was a bit surprised to see that the write-byte and read-byte functions, while defined in the CLHS as writing individual bytes, actually write and read one entry from the stream.  If the stream element type is (integer 0 65535), the entries are 16 bits long, and read-byte reads a 16-bit word from the stream, not a single byte.  I was unable to find anything in the CLHS among the clarifying notes that would tell me whether this is required, allowed, or a bug.  If anybody knows, please pass on your knowledge in the comments.