Looking at the Database Contents

You can also see the current value of *db* whenever you want by typing *db* at the REPL.

  1. CL-USER> *db*
  2. ((:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
  3. (:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T)
  4. (:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T))

However, that’s not a very satisfying way of looking at the output. You can write a dump-db function that dumps out the database in a more human-readable format, like this:

  1. TITLE: Home
  2. ARTIST: Dixie Chicks
  3. RATING: 9
  4. RIPPED: T
  5. TITLE: Fly
  6. ARTIST: Dixie Chicks
  7. RATING: 8
  8. RIPPED: T
  9. TITLE: Roses
  10. ARTIST: Kathy Mattea
  11. RATING: 7
  12. RIPPED: T

The function looks like this:

  1. (defun dump-db ()
  2. (dolist (cd *db*)
  3. (format t "~{~a:~10t~a~%~}~%" cd)))

This function works by looping over all the elements of *db* with the **DOLIST** macro, binding each element to the variable cd in turn. For each value of cd, you use the **FORMAT** function to print it.

Admittedly, the **FORMAT** call is a little cryptic. However, **FORMAT** isn’t particularly more complicated than C or Perl’s printf function or Python’s string-% operator. In Chapter 18 I’ll discuss **FORMAT** in greater detail. For now we can take this call bit by bit. As you saw in Chapter 2, **FORMAT** takes at least two arguments, the first being the stream where it sends its output; t is shorthand for the stream *standard-output*.

The second argument to **FORMAT** is a format string that can contain both literal text and directives telling **FORMAT** things such as how to interpolate the rest of its arguments. Format directives start with ~ (much the way printf‘s directives start with %). **FORMAT** understands dozens of directives, each with their own set of options.3 However, for now I’ll just focus on the ones you need to write dump-db.

The ~a directive is the aesthetic directive; it means to consume one argument and output it in a human-readable form. This will render keywords without the leading : and strings without quotation marks. For instance:

  1. CL-USER> (format t "~a" "Dixie Chicks")
  2. Dixie Chicks
  3. NIL

or:

  1. CL-USER> (format t "~a" :title)
  2. TITLE
  3. NIL

The ~t directive is for tabulating. The ~10t tells **FORMAT** to emit enough spaces to move to the tenth column before processing the next ~a. A ~t doesn’t consume any arguments.

  1. CL-USER> (format t "~a:~10t~a" :artist "Dixie Chicks")
  2. ARTIST: Dixie Chicks
  3. NIL

Now things get slightly more complicated. When **FORMAT** sees ~{ the next argument to be consumed must be a list. **FORMAT** loops over that list, processing the directives between the ~{ and ~}, consuming as many elements of the list as needed each time through the list. In dump-db, the **FORMAT** loop will consume one keyword and one value from the list each time through the loop. The ~% directive doesn’t consume any arguments but tells **FORMAT** to emit a newline. Then after the ~} ends the loop, the last ~% tells **FORMAT** to emit one more newline to put a blank line between each CD.

Technically, you could have also used **FORMAT** to loop over the database itself, turning our dump-db function into a one-liner.

  1. (defun dump-db ()
  2. (format t "~{~{~a:~10t~a~%~}~%~}" *db*))

That’s either very cool or very scary depending on your point of view.