(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) ==> ""
Iteration
Another FORMAT directive that you've seen already, in passing, is the iteration directive ~{. This directive tells FORMAT to iterate over the elements of a list or over the implicit list of the format arguments.
With no modifiers, ~{ consumes one format argument, which must be a list. Like the ~[ directive, which is always paired with a ~] directive, the ~{ directive is always paired with a closing ~}. The text between the two markers is processed as a control string, which draws its arguments from the list consumed by the ~{ directive. FORMAT will repeatedly process this control string for as long as the list being iterated over has elements left. In the following example, the ~{ consumes the single format argument, the list (1 2 3), and then processes the control string "~a, ", repeating until all the elements of the list have been consumed.
(format nil "~{~a, ~}" (list 1 2 3)) ==> "1, 2, 3, "
However, it's annoying that in the output the last element of the list is followed by a comma and a space. You can fix that with the ~^ directive; within the body of a ~{ directive, the ~^ causes the iteration to stop immediately, without processing the rest of the control string, when no elements remain in the list. Thus, to avoid printing the comma and space after the last element of a list, you can precede them with a ~^.
(format nil "~{~a~^, ~}" (list 1 2 3)) ==> "1, 2, 3"
The first two times through the iteration, there are still unprocessed elements in the list when the ~^ is processed. The third time through, however, after the ~a directive consumes the 3, the ~^ will cause FORMAT to break out of the iteration without printing the comma and space.
With an at-sign modifier, ~{ processes the remaining format arguments as a list.
(format nil "~@{~a~^, ~}" 1 2 3) ==> "1, 2, 3"
Within the body of a ~{...~}, the special prefix parameter # refers to the number of items remaining to be processed in the list rather than the number of remaining format arguments. You can use that, along with the ~[ directive, to print a comma-separated list with an "and" before the last item like this:
(format nil "~{~a~#[~;, and ~:;, ~]~}" (list 1 2 3)) ==> "1, 2, and 3"
However, that doesn't really work right if the list is two items long because it adds an extra comma.
(format nil "~{~a~#[~;, and ~:;, ~]~}" (list 1 2)) ==> "1, and 2"
You could fix that in a bunch of ways. The following takes advantage of the behavior of ~@{ when nested inside another ~{ or ~@{ directive—it iterates over whatever items remain in the list being iterated over by the outer ~{. You can combine that with a ~#[ directive to make the following control string for formatting lists according to English grammar:
(defparameter *english-list*
"~{~#[~;~a~;~a and ~a~:;~@{~a~#[~;, and ~:;, ~]~}~]~}")
(format nil *english-list* '()) ==> ""
(format nil *english-list* '(1)) ==> "1"
(format nil *english-list* '(1 2)) ==> "1 and 2"
(format nil *english-list* '(1 2 3)) ==> "1, 2, and 3"
(format nil *english-list* '(1 2 3 4)) ==> "1, 2, 3, and 4"
While that control string verges on being "write-only" code, it's not too hard to understand if you take it a bit at a time. The outer ~{...~} will consume and iterate over a list. The whole body of the iteration then consists of a ~#[...~]; the output generated each time through the iteration will thus depend on the number of items left to be processed from the list. Splitting apart the ~#[...~] directive on the ~; clause separators, you can see that it's made up of four clauses, the last of which is a default clause because it's preceded by a ~:; rather than a plain ~;. The first clause, for when there are zero elements to be processed, is empty, which makes sense—if there are no more elements to be processed, the iteration would've stopped already. The second clause handles the case of one element with a simple ~a directive. Two elements are handled with "~a and ~a". And the default clause, which handles three or more elements, consists of another iteration directive, this time using ~@{ to iterate over the remaining elements of the list being processed by the outer ~{. And the body of that iteration is the control string that can handle a list of three or more elements correctly, which is fine in this context. Because the ~@{ loop consumes all the remaining list items, the outer loop iterates only once.