Conditional Formatting

In addition to directives that interpolate arguments and modify other output, **FORMAT** provides several directives that implement simple control constructs within the control string. One of these, which you used in Chapter 9, is the conditional directive ~[. This directive is closed by a corresponding ~], and in between are a number of clauses separated by ~;. The job of the ~[ directive is to pick one of the clauses, which is then processed by **FORMAT**. With no modifiers or parameters, the clause is selected by numeric index; the ~[ directive consumes a format argument, which should be a number, and takes the nth (zero-based) clause where N is the value of the argument.

  1. (format nil "~[cero~;uno~;dos~]" 0) ==> "cero"
  2. (format nil "~[cero~;uno~;dos~]" 1) ==> "uno"
  3. (format nil "~[cero~;uno~;dos~]" 2) ==> "dos"

If the value of the argument is greater than the number of clauses, nothing is printed.

  1. (format nil "~[cero~;uno~;dos~]" 3) ==> ""

However, if the last clause separator is ~:; instead of ~;, then the last clause serves as a default clause.

  1. (format nil "~[cero~;uno~;dos~:;mucho~]" 3) ==> "mucho"
  2. (format nil "~[cero~;uno~;dos~:;mucho~]" 100) ==> "mucho"

It’s also possible to specify the clause to be selected using a prefix parameter. While it’d be silly to use a literal value in the control string, recall that # used as a prefix parameter means the number of arguments remaining to be processed. Thus, you can define a format string such as the following:

  1. (defparameter *list-etc*
  2. "~#[NONE~;~a~;~a and ~a~:;~a, ~a~]~#[~; and ~a~:;, ~a, etc~].")

and then use it like this:

  1. (format nil *list-etc*) ==> "NONE."
  2. (format nil *list-etc* 'a) ==> "A."
  3. (format nil *list-etc* 'a 'b) ==> "A and B."
  4. (format nil *list-etc* 'a 'b 'c) ==> "A, B and C."
  5. (format nil *list-etc* 'a 'b 'c 'd) ==> "A, B, C, etc."
  6. (format nil *list-etc* 'a 'b 'c 'd 'e) ==> "A, B, C, etc."

Note that the control string actually contains two ~[~] directives—both of which use # to select the clause to use. The first consumes between zero and two arguments, while the second consumes one more, if available. **FORMAT** will silently ignore any arguments not consumed while processing the control string.

With a colon modifier, the ~[ can contain only two clauses; the directive consumes a single argument and processes the first clause if the argument is **NIL** and the second clause is otherwise. You used this variant of ~[ in Chapter 9 to generate pass/fail messages, like this:

  1. (format t "~:[FAIL~;pass~]" test-result)

Note that either clause can be empty, but the directive must contain a ~;.

Finally, with an at-sign modifier, the ~[ directive can have only one clause. The directive consumes one argument and, if it’s non-**NIL**, processes the clause after backing up to make the argument available to be consumed again.

  1. (format nil "~@[x = ~a ~]~@[y = ~a~]" 10 20) ==> "x = 10 y = 20"
  2. (format nil "~@[x = ~a ~]~@[y = ~a~]" 10 nil) ==> "x = 10 "
  3. (format nil "~@[x = ~a ~]~@[y = ~a~]" nil 20) ==> "y = 20"
  4. (format nil "~@[x = ~a ~]~@[y = ~a~]" nil nil) ==> ""