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.
(format nil "~[cero~;uno~;dos~]" 0) ==> "cero"
(format nil "~[cero~;uno~;dos~]" 1) ==> "uno"
(format nil "~[cero~;uno~;dos~]" 2) ==> "dos"
If the value of the argument is greater than the number of clauses, nothing is printed.
(format nil "~[cero~;uno~;dos~]" 3) ==> ""
However, if the last clause separator is ~:;
instead of ~;
, then the last clause serves as a default clause.
(format nil "~[cero~;uno~;dos~:;mucho~]" 3) ==> "mucho"
(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:
(defparameter *list-etc*
"~#[NONE~;~a~;~a and ~a~:;~a, ~a~]~#[~; and ~a~:;, ~a, etc~].")
and then use it like this:
(format nil *list-etc*) ==> "NONE."
(format nil *list-etc* 'a) ==> "A."
(format nil *list-etc* 'a 'b) ==> "A and B."
(format nil *list-etc* 'a 'b 'c) ==> "A, B and C."
(format nil *list-etc* 'a 'b 'c 'd) ==> "A, B, C, etc."
(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:
(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.
(format nil "~@[x = ~a ~]~@[y = ~a~]" 10 20) ==> "x = 10 y = 20"
(format nil "~@[x = ~a ~]~@[y = ~a~]" 10 nil) ==> "x = 10 "
(format nil "~@[x = ~a ~]~@[y = ~a~]" nil 20) ==> "y = 20"
(format nil "~@[x = ~a ~]~@[y = ~a~]" nil nil) ==> ""