Borys Bradel's Blog
Notes on Chapter 3 of Practical Common Lisp
Tags: programming, lisp July 14, 2008
I'm currently reading Practical Common Lisp. Chapter 3 is quite interesting and some of its content is noteworthy.
First, lists can contain keyword value pairs which can be combined with the getf function for meaningful accesses to the list, instead of the usual car, cadr, caddr, or first, second, third, et cetera. Namely, (getf '(:a 0 :b t) :b) will return t.
Second, the commands within the command string of the format function are discussed. In particular, ~a removes : from keywords and quotes from strings. Also, ~20t will emit the appropriate number of spaces for whatever comes next to be after the 20th column. Finally, anything between ~{ and ~} will use the next argument, which must be a list, and repeatedly process the entries until the list ends.
Third, function parameters are discussed. All parameters after &key are supposed to be specified with a keyword (i.e. parameter a needs :a 1 instead of just 1). &rest combines the remaining parameters in a list. And when keywords are used a parameter can be a triplet in a list where the first entry is the name, the second entry a default value, and the third entry is a variable that indicates whether the parameter was specified or not (e.g. (defun f (&key (a 5 p)) (format t "~A ~A~%" a p)) (f :a 3) (f) will print out 3 T and 5 NIL).
Fourth, macros are discussed, along with back quotes, commas, and ,@ splices. Back quotes create lists where commas cause their entries to be evaluated and splices are the same as commas except that the evaluated forms, which must result in lists, are integrated into the overall list. The combination, along with the loop macro, allows for a very nice implementation of the where statement in databases that is something like the following:
(defun make-comparisons (entries) (loop while entries collecting `(equal (getf row ,(pop entries) (pop entries)))))
(defmacro where (&rest clauses) `#'(lambda (row) (and ,@(make-comparisons clauses))))
Then to find some entries in the database, the following code can be used: (remove-if-not (where :a 0) db). Similarly, where works with setting some entries, a and b (where if these are nil they should not be set), with (setf db #'(lambda (row) (when (funcall (where :a 0) row) (if a (setf (getf row :a) a)) (if b ((getf row :a) a))) row) db). Of course these can be wrapped up in select and update functions.
Really neat stuff.
Copyright © 2008 Borys Bradel. All rights reserved. This post is only my possibly incorrect opinion.