hash table at all and being in the table with the value NIL
. GETHASH
solves this problem with a feature I haven't discussed yet—multiple return values. GETHASH
actually returns two values; the primary value is the value stored under the given key or NIL
. The secondary value is a boolean indicating whether the key is present in the hash table. Because of the way multiple values work, the extra return value is silently discarded unless the caller explicitly handles it with a form that can 'see' multiple values.
I'll discuss multiple return values in greater detail in Chapter 20, but for now I'll give you a sneak preview of how to use the MULTIPLE-VALUE-BIND
macro to take advantage of GETHASH
's extra return value. MULTIPLE-VALUE- BIND
creates variable bindings like LET
does, filling them with the multiple values returned by a form.
The following function shows how you might use MULTIPLE-VALUE-BIND
; the variables it binds are value
and present
:
(defun show-value (key hash-table)
(multiple-value-bind (value present) (gethash key hash-table)
(if present
(format nil 'Value ~a actually present.' value)
(format nil 'Value ~a because key not found.' value))))
(setf (gethash 'bar *h*) nil) ; provide an explicit value of NIL
(show-value 'foo *h*) ==> 'Value QUUX actually present.'
(show-value 'bar *h*) ==> 'Value NIL actually present.'
(show-value 'baz *h*) ==> 'Value NIL because key not found.'
Since setting the value under a key to NIL
leaves the key in the table, you'll need another function to completely remove a key/value pair. REMHASH
takes the same arguments as GETHASH
and removes the specified entry. You can also completely clear a hash table of all its key/value pairs with CLRHASH
.
Common Lisp provides a couple ways to iterate over the entries in a hash table. The simplest of these is via the function MAPHASH
. Analogous to the MAP
function, MAPHASH
takes a two- argument function and a hash table and invokes the function once for each key/value pair in the hash table. For instance, to print all the key/value pairs in a hash table, you could use MAPHASH
like this:
(maphash #'(lambda (k v) (format t '~a => ~a~%' k v)) *h*)
The consequences of adding or removing elements from a hash table while iterating over it aren't specified (and are likely to be bad) with two exceptions: you can use SETF
with GETHASH
to change the value of the current entry, and you can use REMHASH
to remove the current entry. For instance, to remove all the entries whose value is less than ten, you could write this:
(maphash #'(lambda (k v) (when (< v 10) (remhash k *h*))) *h*)
The other way to iterate over a hash table is with the extended LOOP
macro, which I'll discuss in Chapter 22.[130] The LOOP
equivalent of the first MAPHASH
expression would look like this:
(loop for k being the hash-keys in *h* using (hash-value v)
do (format t '~a => ~a~%' k v))
I could say a lot more about the nonlist collections supported by Common Lisp. For instance, I haven't discussed multidimensional arrays at all or the library of functions for manipulating bit arrays. However, what I've covered in this chapter should suffice for most of your general-purpose programming needs. Now it's finally time to look at Lisp's eponymous data structure: lists.
12. They Called It LISP for a Reason: List Processing
Lists play an important role in Lisp—for reasons both historical and practical. Historically, lists were Lisp's original composite data type, though it has been decades since they were its
Practically speaking, lists remain in the language because they're an excellent solution to certain problems. One such problem—how to represent code as data in order to support code-transforming and code-generating macros—is particular to Lisp, which may explain why other languages don't feel the lack of Lisp-style lists. More generally, lists are an excellent data structure for representing any kind of heterogeneous and/or hierarchical data. They're also quite lightweight and support a functional style of programming that's another important part of Lisp's heritage.
Thus, you need to understand lists on their own terms; as you gain a better understanding of how lists work, you'll be in a better position to appreciate when you should and shouldn't use them.
Spoon Boy: Do not try and bend the list. That's impossible. Instead . . . only try to realize the truth.
Neo: What truth?
Spoon Boy: There is no list.
Neo: There is no list?
Spoon Boy: Then you'll see that it is not the list that bends; it is only yourself.[131]
The key to understanding lists is to understand that they're largely an illusion built on top of objects that are instances of a more primitive data type. Those simpler objects are pairs of values called CONS
used to create them.
CONS
takes two arguments and returns a new cons cell containing the two values.[132] These values can be references to any kind of object. Unless the second value is NIL
or another cons cell, a cons is printed as the two values in parentheses separated by a dot, a so-called dotted pair.
(cons 1 2) ==> (1 . 2)
The two values in a cons cell are called the CAR
and the