Monthly Archives: February 2014

The less-familiar parts of Lisp for beginners — ldiff and tailp

Next, the ldiff and tailp functions.  These are fairly simple in their descriptions, but it is important to note that they look for sublists that are eq, not equal.  That is, their special behaviour is only apparent on sublists that are part of the list they’re being compared against.  An example might help:

CL-USER> (let ((list-1 '(1 2 3 4 5 6))
               (list-2 '(4 5 6)))
           (format t "ldiff on non-eq lists: ~A~%"
                   (ldiff list-1 list-2)))
ldiff on non-eq lists: (1 2 3 4 5 6)
CL-USER> (let* ((list-1 '(1 2 3 4 5 6))
                (list-2 (cdddr list-1)))
           (format t "ldiff on true sublists: ~A~%"
                   (ldiff list-1 list-2)))
ldiff on true sublists: (1 2 3)
CL-USER> (let ((list-1 '(1 2 3 4 5 6))
               (list-2 '(4 5 6)))
           (format t "tailp on non-eq lists: ~A~%"
                   (tailp list-2 list-1)))
tailp on non-eq lists: NIL
CL-USER> (let* ((list-1 '(1 2 3 4 5 6))
                (list-2 (cdddr list-1)))
           (format t "tailp on true sublists: ~A~%"
                   (tailp list-2 list-1)))
tailp on true sublists: T

So, as long as you remember this constraint, and also remember that the order of the arguments is reversed between tailp and ldiff, you will have no troubles with these functions.

The less-familiar parts of Lisp for beginners — lambda

Now, the newcomer to Lisp, arriving from C++, has certainly encountered lambda before, as part of a construct to build functions that can be passed to functions.  Just about any introduction to Lisp will show lambda forms being passed to mapcar or sort.  There is, however, a short digression we can go into in order to clear up some confusion.

While reading over Lisp code, you’ve almost certainly seen lambda used in two visually distinct ways:

(let ((my-list (list 1 2 3 4 5)))
  (mapcar #'(lambda (x) (* x 3)) my-list))

(let ((my-list (list 1 2 3 4 5)))
  (mapcar (lambda (x) (* x 3)) my-list))

What is the difference between these two?  Why do you sometimes see one form, and sometimes another?

You’ll recall we talked about read-macros, and that the form #’<STUFF> is rewritten (function <STUFF>).  The standard Lisp implementation also defines a macro for lambda, which expands to #’, as follows:

CL-USER> (macroexpand '(lambda (x) (* x 3)))
#'(LAMBDA (X) (* X 3))

So, in fact, there is no difference at all between the two forms, they are interpreted the same way by the Lisp instance.  It comes down to a matter of preference.  Personally, I use the prefixed form of lambda, because I find that it sticks out a bit more that way, a lambda is built a bit like a function invocation, and the syntactic prefix helps to separate that in my mind when quickly skimming through code.

The less-familiar parts of Lisp for beginners — initialize-instance

Next in our list of functions that the newcomer to Lisp might not have encountered is initialize-instance.  Now, you might immediately think of this as a function to be used by the implementation, of little interest to the programmer, but this is actually a very useful method when  you’re programming with objects.

So, initialize-instance is used by the implementation as it constructs instances of objects, and is called by make-instance.  What makes it interesting to the Lisp programmer?  It’s that you can define a specialized :after method for make-instance.  That is, after the make-instance call has done those things that you asked of it, such as filling in :initarg values and executing :initform forms, your :after method will be called to do additional work.  Where the primary initialize-instance method looks like a C++ initialization list, the :after method looks like the body of the constructor.  It is here that the programmer can perform initialization operations based on the values of slots in the class.

Here is an example, part of some code that I wrote for working with probability distributions:

(defclass uniform-distribution (distribution)
  ((low-bound           :reader get-low
                        :initarg :low
                        :initform 0.0)
   (high-bound          :reader get-high
                        :initarg :high
                        :initform 1.0)
   (width               :reader get-width)
   (height              :reader get-height)))

(defmethod initialize-instance :after ((ud uniform-distribution) &key)
  (unless (< (get-low ud) (get-high ud))
    (error "Bad values of low/high for uniform distribution."))
  (setf (slot-value ud 'width) (float (- (get-high ud) (get-low ud))))
  (setf (slot-value ud 'height) (/ 1.0 (- (get-high ud) (get-low ud))))
  (set-limits ud (get-low ud) (get-high ud) (get-low ud) (get-high ud)))

This code allows the user to build a uniform-distribution object with make-instance, optionally supplying overrides for the lower and upper bounds of the distribution, the domain.  Once the low-bound and high-bound slots are filled, the :after specialized initialize-instance method is called which sanity-checks the passed values and then uses them to fill in the other two slots.  Finally, it calls a method on the base class (not shown) to fill in some data that the base class needs.