Format is a function in Common Lisp that can produce formatted text using a format string similar to the print format string. It provides more functionality than print
, allowing the user to output numbers in various formats (including, for instance: hex, binary, octal, roman numerals, and English), apply certain format specifiers only under certain conditions, iterate over data structures, output data tabularly, and even recurse, calling format
internally to handle data structures that include their own preferred formatting strings. This functionally originates in MIT's Lisp Machine Lisp, [1] where it was based on Multics.
The format
function is specified by the syntax: [2]
format ''destination'' ''controlString'' &rest ''formatArguments''
Directives in the control string are interpolated using the format arguments, and the thus constructed character sequence is written to the destination.
The destination may either be a stream, a dynamic string, T
, or the NIL
constant; the latter of which presents a special case in that it creates, formats and returns a new string object, while T
refers to the standard output, usually being equivalent to the console. Streams in Common Lisp comprehend, among others, string output and file streams; hence, being capable of writing to such a variety of destinations, this function unifies capabilities distributed among distinct commands in some other programming languages, such as C's printf
for console output, sprintf
for string formatting, and fprintf
for file writing.
The multitude of destination types is exemplified in the following:
;; Prints "1 + 2 = 3" to the standard output and returns ``NIL''.(formatt"1 + 2 = ~D"3);; Creates and returns a new string containing "1 + 2 = 3".(formatnil"1 + 2 = ~D"3);; Creates and returns a new string containing "1 + 2 = 3".(with-output-to-string(output)(formatoutput"1 + 2 = ~D"3));; Writes to the file "outputFile.txt" the string "1 + 2 = 3".(with-open-file(output"outputFile.txt":direction:output:if-does-not-exist:create:if-exists:append)(formatoutput"1 + 2 = ~D"3));; Appends to the dynamic string the string "1 + 2 = 3".(let((output-string(make-array0:element-type'character:adjustablet:fill-pointer0)))(declare(typestringoutput-string))(formatoutput-string"1 + 2 = ~D"3)(thestringoutput-string))
The control string may contain literal characters as well as the meta character ~
(tilde), which demarcates format directives. While literals in the input are echoed verbatim, directives produce a special output, often consuming one or more format arguments.
A format directive, introduced by a ~
, is followed by zero or more prefix parameters, zero or more modifiers, and the directive type. A directive definition, hence, must conform to the following pattern:
~[''prefixParameters''][''modifiers'']''directiveType''
The directive type is always specified by a single character, case-insensitive in the case of letters. The data to be processed by a format directive, if at all necessary, is called its format argument and may be zero or more objects of any type compatible. Whether and in which quantity such data is accepted depends on the directive and potential modifiers applied unto it. The directive type ~%
, for instance, abstains from the consumption of any format arguments, whereas ~D
expects exactly one integer number to print, and ~@{
, a directive influenced by the at-sign modifier, processes all remaining arguments.
The following directive, ~B
, expects one number object from the format arguments and writes its binary (radix 2) equivalent to the standard output.
(formatt"~B"5)
Where configurations are permissive, prefix parameters may be specified.
Prefix parameters enable an injection of additional information into a directive to operate upon, similar to the operation of parameters when provided to a function. Prefix parameters are always optional, and, if provided, must be located between the introducing ~
and either the modifiers or, if none present, the directive type. The values are separated by commas, but do not tolerate white spaces on either side. The number and type of these parameters depends on the directive and the influence of potential modifiers.
Two particular characters may be utilized as prefix parameter values with distinctive interpretation: v
or V
acts as a placeholder for an integer number or character from the format arguments which is consumed and placed into its stead. The second special character, #
, is substituted by the tally of format arguments yet abiding their consumption. Both V
and #
enable behavior defined by dynamic content injected into the prefix parameter list.
The V
parameter value introduces a functionality equivalent to a variable in the context of general programming. Given this simple scenario, in order to left-pad a binary representation of the integer number 5
to at least eight digits with zeros, the literal solution is as follows:
(formatt"~8,'0b"5)
The first prefix parameter controlling the output width may, however, be defined in terms of the V
character, delegating the parameter value specification to the next format argument, in our case 8
.
(formatt"~v,'0b"85)
Solutions of this kind are particularly a benefit if parts of the prefix parameter list shall be described by variables or function arguments instead of literals, as is the case in the following piece of code:
(let((number-of-digits8))(declare(type(integer0*)number-of-digits))(formatt"~V,'0b"number-of-digits5))
Even more fitting in those situations involving external input, a function argument may be passed into the format directive:
(defunprint-as-hexadecimal(number-to-formatnumber-of-digits)"Prints the NUMBER-TO-FORMAT in the hexadecimal system (radix 16), left-padded with zeros to at least NUMBER-OF-DIGITS."(declare(typenumbernumber-to-format))(declare(type(integer0*)number-of-digits))(formatt"~V,'0x"number-of-digitsnumber-to-format))(print-as-hexadecimal122)
#
as a prefix parameter tallies those format arguments not yet processed by preceding directives, doing so without actually consuming anything from this list. The utility of such a dynamically inserted value is preponderantly restricted to use cases pertaining to conditional processing. As the argument number can only be an integer number greater than or equal to zero, its significance coincides with that of an index into the clauses of a conditional ~[
directive.
The interplay of the special #
prefix parameter value with the conditional selection directive ~[
is illustrated in the following example. The condition states four clauses, accessible via the indices 0, 1, 2, and 3 respectively. The number of format arguments is employed as the means for the clause index retrieval; to do so, we insert #
into the conditional directive which permits the index to be a prefix parameter. #
computes the tally of format arguments and suggests this number as the selection index. The arguments, not consumed by this act, are then available to and processed by the selected clause's directives.
;; Prints "none selected".(formatt"~#[none selected~;one selected: ~A~;two selected: ~A and ~A~:;more selected: ~@{~A~^, ~}~]");; Prints "one selected: BUNNY".(formatt"~#[none selected~;one selected: ~A~;two selected: ~A and ~A~:;more selected: ~@{~A~^, ~}~]"'bunny);; Prints "two selected: BUNNY and PIGEON".(formatt"~#[none selected~;one selected: ~A~;two selected: ~A and ~A~:;more selected: ~@{~A~^, ~}~]"'bunny'pigeon);; Prints "more selected: BUNNY, PIGEON, MOUSE".(formatt"~#[none selected~;one selected: ~A~;two selected: ~A and ~A~:;more selected: ~@{~A~^, ~}~]"'bunny'pigeon'mouse)
Modifiers act in the capacity of flags intending to influence the behavior of a directive. The admission, magnitude of behavioral modification and effect, as with prefix parameters, depends upon the directive. In some severe cases, the syntax of a directive may be varied to a degree as to invalidate certain prefix parameters; this power especially distinguishes modifiers from most parameters. The two valid modifier characters are @
(at-sign) and :
(colon), possibly in combination as either :@
or @:
.
The following example illustrates a rather mild case of influence exerted upon a directive by the @
modifier: It merely ensures that the binary representation of a formatted number is always preceded by the number's sign:
(formatt"~@B"5)
An enumeration of the format directives, including their complete syntax and modifier effects, is adduced below. [3]
Character | Description | Full form | Modifier | |||
---|---|---|---|---|---|---|
None | : | @ | :@ | |||
~ | Prints the literal ~ character. | ~repetitions~ . | Prints repetitions times the ~ character. | Invalid | ||
c, C | Prints a single character. | ~C . | Prints the format argument character without prefix. | Spells out non-printing characters. | Prepends the readable #\ prefix. | Spells out non-printing characters and mentions shift keys. |
% | Prints an unconditional newline. | ~repetitions% . | Prints repetitions line breaks. | Invalid | ||
& | Prints a conditional newline, or fresh-line. | ~repetitions& . | If the destination is not at the beginning of a fresh line, prints repetitions line breaks; otherwise, prints repetitions - 1 line breaks. | Invalid | ||
| | Prints a page separator. | ~repetitions| . | Prints repetitions times a page separator. | Invalid | ||
r, R | Either prints the number in the specified base (radix) or spells it out. | ~radix,minColumns,padChar,commaChar,commaIntervalR .
| Prints the argument as an English number. | Spells the argument in English ordinal numbers. | Prints the argument in Roman numerals using the usual Roman format (e.g., 4 = IV). | Prints the argument in Roman numerals using the old Roman format (e.g., 4 = IIII). |
d, D | Prints the argument in decimal radix (base = 10). | ~minColumns,padChar,commaChar,commaIntervalD . | Prints as decimal number without + (plus) sign or group separator. | Uses commas as group separator. | Prepends the sign. | Prepends the sign and uses commas as group separator. |
b, B | Prints the argument in binary radix (base = 2). | ~minColumns,padChar,commaChar,commaIntervalB . | Prints as binary number without + (plus) sign or group separator. | Uses commas as group separator. | Prepends the sign. | Prepends the sign and uses commas as group separator. |
o, O | Prints the argument in octal radix (base = 8). | ~minColumns,padChar,commaChar,commaIntervalO . | Prints as octal number without + (plus) sign or group separator. | Uses commas as group separator. | Prepends the sign. | Prepends the sign and uses commas as group separator. |
x, X | Prints the argument in hexadecimal radix (base = 16). | ~minColumns,padChar,commaChar,commaIntervalX . | Prints as hexadecimal number without + (plus) sign or group separator. | Uses commas as group separator. | Prepends the sign. | Prepends the sign and uses commas as group separator. |
f, F | Prints the argument as a float in fixed-point notation. | ~width,numDecimalPlaces,scaleFactor,overflowChar,padCharF . | Prints as fixed-point without + (plus) sign. | Invalid | Prepends the sign. | Invalid |
e, E | Prints the argument as a float in exponential notation. | ~width,numDecimalPlaces,numDigits,scaleFactor,overflowChar,padChar,exponentCharE . | Prints as exponential without + (plus) sign. | Invalid | Prepends the sign. | Invalid |
g, G | Prints the argument either as a float in fixed-point or exponential notation, choosing automatically. | ~width,numDecimalPlaces,numDigits,scaleFactor,overflowChar,padChar,exponentCharG . | Prints as fixed-point or exponential without + (plus) sign. | Invalid | Prepends the sign. | Invalid |
$ | Prints the argument according to monetary conventions. | ~width,numDigits,minWholeDigits,minTotalWidth,padChar$ . | Prints in monetary conventions without + (plus) sign or padding. | Prepends the sign before padding characters. | Prepends the sign. | Invalid |
a, A | Prints the argument in a human-friendly manner. | ~minColumns,colInc,minPad,padCharA . | Prints human-friendly output without justification. | Prints NIL as empty list () instead of NIL . | Pads on the left instead of the right side. | Pads on the left and prints NIL as () . |
s, S | Prints the argument in a manner compatible with the read function. | ~minColumns,colInc,minPad,padCharS . | Prints read -compatible without justification. | Prints NIL as empty list () instead of NIL . | Pads on the left instead of the right side. | Pads on the left and prints NIL as () . |
w, W | Prints the argument in accordance with the printer control variables. | ~W . | Prints in accordance with the currently set control variables. | Enables pretty printing. | Ignores print level and length constraints. | Ignores print level and length constraints and enables pretty printing. |
_ | Prints a line break according to the pretty printer rules. | ~_ . | Prints a line break if a single line is exceeded. | Prints a line break if no single line preceded. | Uses a compact (miser) style. | Always inserts a line break. |
< | Justifies the output. | ~minColumns,colInc,minPad,padChar<expression~> . | Left-justifies the output. | Adds left padding (= right-justifies). | Adds right padding (= left-justifies). | Centers the text. |
i, I | Indents a logical block. | ~I . | Starts indenting from the first character. | Indents starting from the current output position. | Invalid | |
/ | Dispatches the formatting operation to a user-defined function. The function must accept at least four parameters:
Additionally, zero or more arguments may be specified if the function shall also permit prefix parameters. | ~prefixParams/function/ . | Depends on the function implementation. | |||
t, T | Moves the output cursor to a given column or by a horizontal distance. | ~columnNumber,columnIncrementT . | Moves to the specified column. | Orients at section. | Moves the cursor relative to the current position. | Orients relative to section. |
* | Navigates across the format arguments. | ~numberOfArgs* . | Skips the numberOfArgs format arguments. | Moves numberOfArgs back. | Moves to the argument at index numberOfArgs . | Invalid |
[ | Prints an expression based upon a condition. These expressions, or clauses, are separated by the ~; directive, and a default clause can be stated by using ~:; as its leading separator. The number of permitted clauses depends upon the concrete variety of this directive as stated by its modifier or modifiers. The whole conditional portion must be terminated with a ~] . |
| The format argument must be a zero-based integer index, its value being that of the clause to select and print. | Selects the first clause if the format argument is NIL , otherwise the second one. | Only processes the clause if the format argument is T , otherwise skips it. | Invalid |
{ | Iterates over one or more format arguments and prints these. The iterative portion must be closed with a ~} directive. If the directive ~^ is found inside of the enclosed portion, any content following it is only consumed if the current element is not the last one in the processed list. If the prefix parameter numberOfRepetitions is specified, its value defines the maximum number of elements to process; otherwise all of these are consumed. | ~numberOfRepetitions{expression~} . | A single format argument is expected to be a list, its elements are consumed in order by the enclosed directives. | Expects the format argument to be a list of lists, consuming its sublists. | Regards all remaining format arguments as a list and consumes these. | Regards all remaining format arguments as a list of sublists, consuming these sublists. |
? | Substitutes the directive by the next argument, expected to be a format argument, using the subsequent format arguments in the new portion. | ~? . | Expects the subsequent format argument to be a list whose elements are associated with the inserted control string. | Invalid | Expects separate format arguments instead of a list of these for the inserted portion, as one would specify in the usual manner. | Invalid |
( | Modifies the case of the enclosed string. | ~(expression~) . | Converts all characters to lower case. | Capitalizes all words. | Capitalizes the first word only, converts the rest to lower case. | Converts all characters to upper case. |
p, P | Prints a singular or plural suffix depending upon the numeric format argument. | ~P . | Prints nothing if the argument equals 1, otherwise prints plural suffix s . | Moves back to the last consumed format argument, printing nothing if it was 1, otherwise printing s . | Prints a suffix y if the argument equals 1, otherwise prints suffix ies . | Moves back to the last consumed format argument, printing suffix y if it was 1, otherwise printing suffix ies . |
^ | Used in an iteration directive ~{...~} to terminate processing of the enclosed content if no further format arguments follow. | ~P1,P2,P3^ .
| Operates as described. | Invalid | ||
Newline | Skips or retains line breaks and adjacent whitespaces in a multi-line control string. | ~Newline . | Skips the immediately following line break and adjacent whitespaces. | Skips the immediately following line break, but retains adjacent whitespaces. | Retains the immediately following line break, but skips adjacent whitespaces. | Invalid |
An example of a C printf
call is the following:
printf("Color %s, number1 %d, number2 %05d, hex %x, float %5.2f, unsigned value %u.\n","red",123456,89,255,3.14,250);
Using Common Lisp, this is equivalent to:
(formatt"Color ~A, number1 ~D, number2 ~5,'0D, hex ~X, float ~5,2F, unsigned value ~D.~%""red"123456892553.14250);; prints: Color red, number1 123456, number2 00089, hex FF, float 3.14, unsigned value 250.
Another example would be to print every element of list delimited with commas, which can be done using the ~{, ~^ and ~} directives: [4]
(let((groceries'(eggsbreadbuttercarrots)))(formatt"~{~A~^, ~}.~%"groceries); Prints in uppercase(formatt"~:(~{~A~^, ~}~).~%"groceries)); Capitalizes output;; prints: EGGS, BREAD, BUTTER, CARROTS.;; prints: Eggs, Bread, Butter, Carrots.
Note that not only is the list of values iterated over directly by format
, but the commas correctly are printed between items, not after them. A yet more complex example would be printing out a list using customary English phrasing:
(let((template"The lucky winners were:~#[ none~; ~S~; ~S and ~S~ ~:;~@{~#[~; and~] ~S~^,~}~]."))(formatniltemplate);; ⇒ "The lucky winners were: none."(formatniltemplate'foo);; ⇒ "The lucky winners were: FOO."(formatniltemplate'foo'bar);; ⇒ "The lucky winners were: FOO and BAR."(formatniltemplate'foo'bar'baz);; ⇒ "The lucky winners were: FOO, BAR, and BAZ."(formatniltemplate'foo'bar'baz'quux);; ⇒ "The lucky winners were: FOO, BAR, BAZ, and QUUX.")
The ability to define a new directive through ~/functionName/
provides the means for customization. The next example implements a function which prints an input string either in lowercase, uppercase or reverse style, permitting a configuration of the number of repetitions, too.
(defunmydirective(destinationformat-argumentcolon-modifier-supplied-pat-sign-modifier-supplied-p&optional(repetitions1))"This function represents a callback suitable as a directive in a ``format'' invocation, expecting a string as its FORMAT-ARGUMENT to print REPETITIONS number of times to the DESTINATION. --- The COLON-MODIFIER-SUPPLIED-P and AT-SIGN-MODIFIER-SUPPLIED-P flags expect a generalized Boolean each, being the representatives of the ``:'' and ``@'' modifiers respectively. Their influence is defined as follows: - If no modifier is set, the FORMAT-ARGUMENT is printed without further modifications. - If the colon modifier is set, but not the at-sign modifier, the FORMAT-ARGUMENT is converted into lowercase before printing. - If the at-modifier is set, but not the colon-modifier, the FORMAT-ARGUMENT is converted into uppercase before printing. - If both modifiers are set, the FORMAT-ARGUMENT is reversed before printing. --- The number of times the FORMAT-ARGUMENT string is to be printed is determined by the prefix parameter REPETITIONS, which must be a non-negative integer number and defaults to one."(declare(type(ornull(eqlt)streamstring)destination))(declare(typetformat-argument))(declare(typetcolon-modifier-supplied-p))(declare(typetat-sign-modifier-supplied-p))(declare(type(integer0*)repetitions))(let((string-to-printformat-argument))(declare(typestringstring-to-print));; Adjust the STRING-TO-PRINT based upon the modifiers.(cond((andcolon-modifier-supplied-pat-sign-modifier-supplied-p)(setfstring-to-print(reversestring-to-print)))(colon-modifier-supplied-p(setfstring-to-print(string-downcasestring-to-print)))(at-sign-modifier-supplied-p(setfstring-to-print(string-upcasestring-to-print)))(tnil))(looprepeatrepetitionsdo(formatdestination"~A"string-to-print))));; Print "Hello" a single time.(formatt"~/mydirective/""Hello");; Print "Hello" three times.(formatt"~3/mydirective/""Hello");; Print a lowercase "Hello" (= "hello") three times.(formatt"~3:/mydirective/""Hello");; Print an uppercase "Hello" (= "HELLO") three times.(formatt"~3@/mydirective/""Hello");; Print a reversed "Hello" (= "olleH") three times.(formatt"~3:@/mydirective/""Hello")
Whilst format
is somewhat infamous for its tendency to become opaque and hard to read, it provides a remarkably concise yet powerful syntax for a specialized and common need. [4]
A Common Lisp FORMAT summary table is available. [5]
Common Lisp (CL) is a dialect of the Lisp programming language, published in American National Standards Institute (ANSI) standard document ANSI INCITS 226-1994 (S2018). The Common Lisp HyperSpec, a hyperlinked HTML version, has been derived from the ANSI Common Lisp standard.
Lisp is a family of programming languages with a long history and a distinctive, fully parenthesized prefix notation. Originally specified in the late 1950s, it is the second-oldest high-level programming language still in common use, after Fortran. Lisp has changed since its early days, and many dialects have existed over its history. Today, the best-known general-purpose Lisp dialects are Common Lisp, Scheme, Racket, and Clojure.
Lua is a lightweight, high-level, multi-paradigm programming language designed mainly for embedded use in applications. Lua is cross-platform software, since the interpreter of compiled bytecode is written in ANSI C, and Lua has a relatively simple C application programming interface (API) to embed it into applications.
In computer programming, an S-expression is an expression in a like-named notation for nested list (tree-structured) data. S-expressions were invented for and popularized by the programming language Lisp, which uses them for source code as well as data.
The C preprocessor is the macro preprocessor for several computer programming languages, such as C, Objective-C, C++, and a variety of Fortran languages. The preprocessor provides inclusion of header files, macro expansions, conditional compilation, and line control.
In computer science, reflective programming or reflection is the ability of a process to examine, introspect, and modify its own structure and behavior.
In computer programming, a parameter or a formal argument is a special kind of variable used in a subroutine to refer to one of the pieces of data provided as input to the subroutine. These pieces of data are the values of the arguments with which the subroutine is going to be called/invoked. An ordered list of parameters is usually included in the definition of a subroutine, so that, each time the subroutine is called, its arguments for that call are evaluated, and the resulting values can be assigned to the corresponding parameters.
The syntax of the C programming language is the set of rules governing writing of software in C. It is designed to allow for programs that are extremely terse, have a close relationship with the resulting object code, and yet provide relatively high-level data abstraction. C was the first widely successful high-level language for portable operating-system development.
In some programming languages, eval
, short for the English evaluate, is a function which evaluates a string as though it were an expression in the language, and returns a result; in others, it executes multiple lines of code as though they had been included instead of the line including the eval
. The input to eval
is not necessarily a string; it may be structured representation of code, such as an abstract syntax tree, or of special type such as code
. The analog for a statement is exec, which executes a string as if it were a statement; in some languages, such as Python, both are present, while in other languages only one of either eval
or exec
is.
In class-based, object-oriented programming, a constructor is a special type of function called to create an object. It prepares the new object for use, often accepting arguments that the constructor uses to set required member variables.
The syntax of Java is the set of rules defining how a Java program is written and interpreted.
In computer programming, an entry point is the place in a program where the execution of a program begins, and where the program has access to command line arguments.
In mathematics and in computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments. Support for variadic functions differs widely among programming languages.
The syntax of JavaScript is the set of rules that define a correctly structured JavaScript program.
scanf, short for scan formatted, is a C standard library function that reads and parses text from standard input.
This article describes the syntax of the C# programming language. The features described are compatible with .NET Framework and Mono.
In programming languages, a label is a sequence of characters that identifies a location within source code. In most languages, labels take the form of an identifier, often followed by a punctuation character. In many high-level languages, the purpose of a label is to act as the destination of a GOTO
statement. In assembly language, labels can be used anywhere an address can. Also in Pascal and its derived variations. Some languages, such as Fortran and BASIC, support numeric labels. Labels are also used to identify an entry point into a compiled sequence of statements.
This article compares a large number of programming languages by tabulating their data types, their expression, statement, and declaration syntax, and some common operating-system interfaces.
The syntax and semantics of PHP, a programming language, form a set of rules that define how a PHP program can be written and interpreted.
SUPER BASIC, sometimes SBASIC for short, is an advanced dialect of the BASIC programming language offered on Tymshare's SDS 940 systems starting in 1968 and available well into the 1970s.
This article needs additional or more specific categories .(June 2024) |