want is a first-order approximation of what this line of code is doing, that's immediately available.
At any rate, you should have at least a reading knowledge of FORMAT
, and it's worth getting a sense of what it can do before you affiliate yourself with the pro- or anti- FORMAT
camp. It's also important to understand at least the basics of FORMAT
because other standard functions, such as the condition-signaling functions discussed in the next chapter, use FORMAT
-style control strings to generate output.
To further complicate matters, FORMAT
supports three quite different kinds of formatting: printing tables of data, FORMAT
.
Pretty-printing is likewise beyond the scope of this book—not because it's passe but just because it's too big a topic. Briefly, the Common Lisp pretty printer is a customizable system for printing block-structured data such as —but not limited to—s-expressions while varying indentation and dynamically adding line breaks as needed. It's a great thing when you need it, but it's not often needed in day-to-day programming.[194]
Instead, I'll focus on the parts of FORMAT
you can use to generate human-readable strings with interpolated values. Even limiting the scope in that way, there's still a fair bit to cover. You shouldn't feel obliged to remember every detail described in this chapter. You can get quite far with just a few FORMAT
idioms. I'll describe the most important features of FORMAT
first; it's up to you how much of a FORMAT
wizard you want to become.
As you've seen in previous chapters, the FORMAT
function takes two required arguments: a destination for its output and a control string that contains literal text and embedded
The first argument to FORMAT
, the destination for the output, can be T
, NIL
, a stream, or a string with a fill pointer. T
is shorthand for the stream *STANDARD- OUTPUT*
, while NIL
causes FORMAT
to generate its output to a string, which it then returns.[195] If the destination is a stream, the output is written to the stream. And if the destination is a string with a fill pointer, the formatted output is added to the end of the string and the fill pointer is adjusted appropriately. Except when the destination is NIL
and it returns a string, FORMAT
returns NIL
.
The second argument, the control string, is, in essence, a program in the FORMAT
language. The FORMAT
language isn't Lispy at all—its basic syntax is based on characters, not s-expressions, and it's optimized for compactness rather than easy comprehension. This is why a complex FORMAT
control string can end up looking like line noise.
Most of FORMAT
's directives simply interpolate an argument into the output in one form or another. Some directives, such as ~%
, which causes FORMAT
to emit a newline, don't consume any arguments. And others, as you'll see, can consume more than one argument. One directive even allows you to jump around in the list of arguments in order to process the same argument more than once or to skip certain arguments in certain situations. But before I discuss specific directives, let's look at the general syntax of a directive.
All directives start with a tilde (~
) and end with a single character that identifies the directive. You can write the character in either upper- or lowercase. Some directives take ~$
directive, one of the directives used to print floating-point values, by default prints two digits following the decimal point.
CL-USER> (format t '~$' pi)
3.14
NIL
However, with a prefix parameter, you can specify that it should print its argument to, say, five decimal places like this:
CL-USER> (format t '~5$' pi)
3.14159
NIL
The values of prefix parameters are either numbers, written in decimal, or characters, written as a single quote followed by the desired character. The value of a prefix parameter can also be derived from the format arguments in two ways: A prefix parameter of v
causes FORMAT
to consume one format argument and use its value for the prefix parameter. And a prefix parameter of #
will be evaluated as the number of remaining format arguments. For example:
CL-USER> (format t '~v$' 3 pi)
3.142
NIL
CL-USER> (format t '~#$' pi)
3.1
NIL
I'll give some more realistic examples of how you can use the #
argument in the section 'Conditional Formatting.'
You can also omit prefix parameters altogether. However, if you want to specify one parameter but not the ones before it, you must include a comma for each unspecified parameter. For instance, the ~F
directive, another directive for printing floating-point values, also takes a parameter to control the number of decimal places to print, but it's the second parameter rather than the first. If you want to use ~F
to print a number to five decimal places, you can write this:
CL-USER> (format t '~,5f' pi)
3.14159
NIL
You can also modify the behavior of some directives with colon and at-sign