y or ies.

(format nil '~r famil~:@p' 1) ==> 'one family'

(format nil '~r famil~:@p' 10) ==> 'ten families'

(format nil '~r famil~:@p' 0) ==> 'zero families'

Obviously, ~P can't solve all pluralization problems and is no help for generating messages in other languages, but it's handy for the cases it does handle. And the ~[ directive, which I'll discuss in a moment, gives you a more flexible way to conditionalize parts of FORMAT's output.

The last directive for dealing with emitting English text is ~(, which allows you to control the case of text in the output. Each ~( is paired with a ~), and all the output generated by the portion of the control string between the two markers will be converted to all lowercase.

(format nil '~(~a~)' 'FOO') ==> 'foo'

(format nil '~(~@r~)' 124) ==> 'cxxiv'

You can modify ~( with an at sign to make it capitalize the first word in a section of text, with a colon to make it to capitalize all words, and with both modifiers to convert all text to uppercase. (A word for the purpose of this directive is a sequence of alphanumeric characters delimited by nonalphanumeric characters or the ends of the text.)

(format nil '~(~a~)' 'tHe Quick BROWN foX') ==> 'the quick brown fox'

(format nil '~@(~a~)' 'tHe Quick BROWN foX') ==> 'The quick brown fox'

(format nil '~:(~a~)' 'tHe Quick BROWN foX') ==> 'The Quick Brown Fox'

(format nil '~:@(~a~)' 'tHe Quick BROWN foX') ==> 'THE QUICK BROWN FOX'

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) ==> ''

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

Вы читаете Practical Common Lisp
Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату