Common Lispのformat関数

Common Lisp では,フォーマット出力用の関数として format が用意されています.巨大な Common Lisp の仕様の中でも特に複雑な組み込み関数の1つであり,中には冗談としか思えない機能もあったりします.Common Lisp使いの人でも,完全に使いこなしているという人は少ないのではないでしょうか.
ということで, format 関数の使い方を自分でまとめて整理したものを公開します.

今まで ~S ~A ~% くらいしか使ったことがないという人は参考にしてみてください.ざっとネット上を検索したところ,仕様書以外で使い方を解説しているドキュメントは(少なくとも日本語では)見当たらないので,少しは需要があるのでは.

(参考文献) Common Lisp HyperSpec (22.3 Formatted Output) :処理系が準拠している仕様のバージョンによって,挙動などが違ってくるかもしれません.



基本事項

書式: (format destination control-sequence arg 0 ... arg n )

戻り値: destination が非 nil のときは nil destination nil のときは出力文字列.


formatディレクティブの共通事項

format の引数 control-sequence の文字列中の以下の並びは formatディレクティブ として扱われ,特別な意味を持つ.

~ [[ パラメータ 1 ] , ][[ パラメータ 2 ] , ] ... [ パラメータ n ][ : ][ @ ] ディレクティブ指定文字

パラメータ には以下のいずれかを指定する.省略も可.

: @ 修飾子 は,ディレクティブの振る舞いを変化させる.どのように変化するかはディレクティブごとに違う.

ディレクティブ指定文字 はこれから説明するアルファベットや記号のいずれか.大文字小文字は区別されない.


各formatディレクティブの説明

1 単純な出力

1.1 ~% Newline

arg の消費: なし

パラメータ: ~ n %

改行 #\Newline を出力. n を指定すると n 回改行する.

評価式 戻り値
(format nil "~%abc~%def~3%ghi") "
abc
def


ghi"

1.2 ~& Fresh-Line

arg の消費: なし

パラメータ: ~ n &

fresh-line 関数を呼ぶ.つまり,現在のカーソルの位置が行の先頭でなければ改行 #\Newline を出力する.
n を指定すると, fresh-line を1回呼んだあと,( n -1) 個の改行を出力する. n が0のときはなにもしない.

評価式 戻り値
(format nil "~&abc~&def~3&ghi") "abc
def


ghi"

1.3 ~| Page

arg の消費: なし

パラメータ: ~ n |

改ページ #\Page を出力. n を指定すると n 個の改ページを出力する.

評価式 戻り値
(coerce (format nil "~4|") 'list) (#\Page #\Page #\Page #\Page)

1.4 ~~ Tilde

arg の消費: なし

パラメータ: ~ n ~

チルダ ~ を出力する. n を指定すると n 個のチルダを出力する.

評価式 戻り値
(format nil "~10~") "~~~~~~~~~~"

2 引数の値を出力

2.1 汎用

2.1.1 ~A Aesthetic

arg の消費: 1個

パラメータ: ~ mincol , colinc , minpad , padchar A

消費した arg の値を princ 関数の方法で出力する.たとえば文字列の場合,前後のダブルクォートは出力されない.
空リストは nil と出力されるが,修飾子 : をつけておくと () と出力されるようになる.

評価式 戻り値
(format nil "~A ~A ~A ~A~%~A" 123 "abc" 'def #\x (find-package "CL")) "123 abc def x
#<The COMMON-LISP package>"
(format nil "~A ~:A" nil nil) "nil ()"

minpad を指定すると,右側 (修飾子 @ があると左側) minpad 個の padchar (デフォルトは空白) を表示する.
mincol を指定すると,出力が mincol 文字に満たない場合,その文字数以上になるまで padchar colinc 文字ずつ出力する.

評価式 戻り値
(format nil "ABC~,,3A123~,,3@A" "pqr" "xyz") "ABCpqr   123   xyz"
(format nil "ABC~,,3,'@A123~,,3,'*@A" "pqr" "xyz") "ABCpqr@@@123***xyz"
(format nil "~10,,,'-A" '(a "B")) "(a B)-----"
(format nil "~10,4,,'-A" '(a "B")) "(a B)--------"
(format nil "~10,4,1,'-@A" '(a "B")) "-----(a B)"

2.1.2 ~S Standard

arg の消費: 1個

パラメータ: ~ mincol , colinc , minpad , padchar S

消費した arg の値を prin1 関数の方法で出力する.たとえば文字列の場合,前後のダブルクォートも出力される.修飾子やパラメータの意味は ~A と同じ.

評価式 戻り値
(format nil "~S ~S ~S ~S~%~S" 123 "abc" 'def #\x (find-package "CL")) "123 \"abc\" def #\\x
#<The COMMON-LISP package>"
(format nil "~S ~:S" nil nil) "nil ()"
(format nil "~10,4,,'-@S" '(a "B")) "----(a \"B\")"

2.1.3 ~W Write

arg の消費: 1個

パラメータ: なし

消費した arg の値を,無引数で write 関数を呼び出したときの方法で出力する.つまり,出力のされ方は format 実行時の出力制御変数 *print-pretty* など) の値に依存する.
修飾子 : をつけると,出力時に *print-pretty* がtrueにバインドされる.
修飾子 @ をつけると, *print-level* *print-length* nil にバインドされる.

評価式 戻り値
(let ((*print-length* 3) (x '(a b c d e f))) (format nil "~W ~@W" x x)) "(a b c ...) (a b c d e f)"

2.2 文字

2.2.1 ~C Character

arg の消費: 1個

パラメータ: なし

消費する arg はcharacter型でなければならない.
修飾子をつけずに使用すると, write-char 関数の方法でその文字を出力する.
: で修飾すると,印字可能な文字(printing character)の場合は上と同じだが,それ以外の文字に対してはその名前が出力されるようになる (具体的にどのような出力になるかは処理系依存)
: @ の両方で修飾すると,それらの印字可能でない文字をキーボードで入力する際,Shiftキーなどを押す必要があれば,それを考慮した表示になる (・・・らしいが,CMU Common Lispだと,どの文字でも出力はまったく変わらない)
@ だけで修飾すると,文字を 「再入力可能な形式」 で出力する ~S と同じ?)

評価式 戻り値
(let ((c #\C)) (format nil "~C ~:C ~@C ~:@C" c c c c)) "C C #\\C C"
(let ((c #\Newline)) (format nil "~C ~:C ~@C ~:@C" c c c c)) "
 Newline #\\Newline Newline"

2.3 整数

2.3.1 ~D Decimal

arg の消費: 1個

パラメータ: ~ mincol , padchar , commachar , comma-interval D

arg で与えられた整数を10進で出力する.
@ で修飾すると, + - の符号が必ず出力される.
: で修飾すると,下から comma-interval 桁ごとに commachar で指定された文字で数字を区切る comma-interval のデフォルトは3, commachar のデフォルトは ,
mincol が指定されていると,出力文字数が mincol に満たない場合,足りない分が左から padchar (デフォルトは空白) で埋められる.

評価式 戻り値
(format nil "~D ^ ~D = ~D" -2 21 (expt -2 21)) "-2 ^ 21 = -2097152"
(format nil "~@5,'_D ^ ~@5D = ~@5D" -2 21 (expt -2 21)) "-2 ^ +21 = -2097152"
(format nil "~:D ~:,,' ,4D" 1234567890 1234567890) "1,234,567,890 1,234,567,890"

2.3.2 ~B Binary

arg の消費: 1個

パラメータ: ~ mincol , padchar , commachar , comma-interval B

arg で与えられた整数を2進で出力する.パラメータや修飾子の意味は ~D と同じ.

評価式 戻り値
(let ((x 65535)) (format nil "~D(10進) = ~:,,' ,4B(2進)" x x)) "65535(10進) = 1,111,111,111,111,111(2進)"

2.3.3 ~O Octal

arg の消費: 1個

パラメータ: ~ mincol , padchar , commachar , comma-interval O

arg で与えられた整数を8進で出力する.パラメータや修飾子の意味は ~D と同じ.

評価式 戻り値
(let ((x 65535)) (format nil "~D(10進) = ~:,,' ,3O(8進)" x x)) "65535(10進) = 177,777(8進)"

2.3.4 ~X heXadecimal

arg の消費: 1個

パラメータ: ~ mincol , padchar , commachar , comma-interval X

arg で与えられた整数を16進で出力する.パラメータや修飾子の意味は ~D と同じ.

評価式 戻り値
(let ((x 65535)) (format nil "~D(10進) = ~:,,' ,2X(16進)" x x)) "65535(10進) = f,fff(16進)"

2.3.5 ~R Radix

arg の消費: 1個

パラメータ: ~ radix , mincol , padchar , commachar , comma-interval R

1つでもパラメータを指定した場合とすべて省略した場合で機能がまったく違う.
まず,パラメータを指定した場合,消費した arg の値を radix 進で表示する (デフォルトは10進)
このとき,それ以降のパラメータや修飾子の意味は ~D ~B ~O ~X と同じである.

評価式 戻り値
(let ((x 65535) (r 7)) (format nil "~D(10進) = ~vR(~D進)" x r x r)) "65535(10進) = 362031(7進)"

パラメータを1つも指定しなかったときは,修飾子の指定により以下の動作をする.
  • 修飾子なし: 整数を英語の綴り(基数)で出力.
  • : 修飾子のみ: 整数を英語の綴り(序数)で出力.
  • @ 修飾子のみ: 整数をローマ数字で出力.
  • : 修飾子と @ 修飾子の両方: 整数を旧ローマ数字で出力.

評価式 戻り値
(format nil "~R~%~:R~%~@R~%~:@R" 3999 3999 3999 3999) "three thousand nine hundred ninety-nine
three thousand nine hundred ninety-ninth
MMMCMXCIX
MMMDCCCCLXXXXVIIII"
(format nil "~R" (expt 2 100)) "one nonillion two hundred sixty-seven octillion six hundred fifty septillion six hundred sextillion two hundred twenty-eight quintillion two hundred twenty-nine quadrillion four hundred one trillion four hundred ninety-six billion seven hundred three million two hundred five thousand three hundred seventy-six"

2.4 浮動小数点数

2.4.1 ~F Fixed-Format Floating-Point

arg の消費: 1個

パラメータ: ~ w , d , k , overflowchar , padchar F

消費した arg を,浮動小数点数として表示する.
d を指定すると,小数点以下を d 桁まで出力する (桁が足りないときはは0が補充される.多すぎる桁は,四捨五入または切り捨てられる.どちらになるかは処理系依存)
k を指定すると, arg を10 k 倍した値を出力する.
修飾子 @ を指定すると, + - の符号を必ず出力する.

評価式 戻り値
(format nil "pi = ~F" (* (atan 1.0) 4)) "pi = 3.1415927"
(format nil "pi = ~,10F" (* (atan 1.0) 4)) "pi = 3.1415927000"
(format nil "pi*100 = ~@,,2F" (* (atan 1.0) 4)) "pi*100 = +3.1415927"

パラメータ w は出力文字数を指定する. w に満たない場合,足りない分は padchar (デフォルトは空白) で左から埋められる.

評価式 戻り値
(format nil "pi = ~9,3,,,'_F" (* (atan 1.0) 4)) "pi = ____3.142"

w 文字以内ではパラメータに従った出力が不可能であり,かつ overflowchar が指定されていた場合,値は出力せず,かわりに w 文字の overflowchar を出力する overflowchar が指定されていなければ, w 文字をオーバーしてでも出力する)

評価式 戻り値
(format nil "pi = ~5F" (* (atan 1.0) 4)) "pi = 3.142"
(format nil "pi = ~5,3,,'*F" (* (atan 1.0) 4)) "pi = 3.142"
(format nil "pi = ~5,4,,'*F" (* (atan 1.0) 4)) "pi = *****"
(format nil "pi = ~5,4F" (* (atan 1.0) 4)) "pi = 3.1416"

2.4.2 ~E Exponential Floating-Point

arg の消費: 1個

パラメータ: ~ w , d , e , k , overflowchar , padchar , exponentchar E

消費した arg を浮動小数点数として指数表示で出力する.
w d padchar overflowchar および @ 修飾子の意味は ~F と同じである.
指数の出力文字数 (符号は含まない) e 文字に満たない場合,足りない分は exponentchar (デフォルトは 0 で埋められる.
k を指定すると,表示する基数の最大位が10 k -1 の位になる (デフォルトは1の位.つまり k =1)

評価式 戻り値
(format nil "~15,2@E" (sqrt 1000)) "       +3.16e+1"
(format nil "~15,2,3E" (sqrt 1000)) "      3.16e+001"
(format nil "~15,,,-2E" (sqrt 1000)) "0.0031622776e+4"

2.4.3 ~G General Floating-Point

arg の消費: 1個

パラメータ: ~ w , d , e , k , overflowchar , padchar , exponentchar G

浮動小数点数を, arg の絶対値の大きさに応じて~Fと~Eどちらかのスタイルで出力する.どちらが選ばれるか,およびそのとき~Fや~Eに渡されるパラメータは以下の手順で決まる.
  1. 10 n -1 ≤ | arg | < 10 n をみたす整数 n を求める.
  2. ee e +2 (パラメータ e が指定された場合)
    ee ← 4 e が省略された場合)
    (指数の桁数 + +e -e の2文字分)
  3. ww w - ee (パラメータ w が指定された場合)
    ww nil w が省略された場合)
  4. パラメータ d が省略されていた場合,
    d (max argの有効桁数 (min n 7 ))
  5. dd d - n
  6. ここで,0 ≤ dd d なら ~ ww , d ,, overflowchar , padchar F~ ee @T
    そうでなければ, ~ w , d , e , k , overflowchar , padchar , exponentchar E として出力される.
    ~G @ 修飾子が指定されていた場合は,そのまま ~F ~E の処理に適用される.

2.4.4 ~$ Monetary Floating-Point

arg の消費: 1個

パラメータ: ~ d , n , w , padchar $

浮動小数点数を,通貨単位「ドル」に適した形(?)で出力する.
d の意味は(code "~F")などと同じ (小数点以下の出力桁数) .ただし,デフォルト値が2に設定されている.
整数部の文字数が n (デフォルトは1) に満たない場合,足りない分が0で埋められる.
w padchar の意味は ~F ~E と同じ.
修飾子 @ を指定すると, + - の符号を必ず出力する.
修飾子 : を指定すると,符号を padchar による文字埋めより前に表示する.

評価式 戻り値
(format nil "$~$ $~$" 3.0 0.82) "$3.00 $0.82"
(format nil "$~@:,,10,'_$" 123.45) "$+123.45"

3 レイアウト調整

3.1 ~T Tabulate

arg の消費: なし

パラメータ: ~ colnum , colinc T

行の先頭から colnum 文字目の位置にカーソルを移動させる (そこまでの空白を出力する)
すでにカーソルがその位置以降にあったときは,現在のカーソル位置より先で最も近い colnum + k × colinc k は整数) 文字目の位置にカーソルを移動させる. colnum colinc のデフォルト値はいずれも1である.
@ で修飾すると相対タブになる.つまり,まず colnum 個の空白を出力し,そこから一番近い k × colinc 文字目の位置までカーソルを移動させる.

評価式 戻り値
(format nil "~5T0") "     0"
(format nil "title:~6,3T1~6,3T23~6,3T456~6,3T7890") "title:   1  23 456   7890"

3.2 ~< ... ~> Justification

arg の消費: なし

パラメータ: なし

... には通常の control-seqence を記述する.ただし,この中に ~; を書くと,文字列がそこでセグメントに分割される.
分割されたセグメントが colinc の文字幅内で等間隔で出力される
通常,一番左のセグメントは左寄せ,一番右のセグメントは右寄せになるが, ~< : で修飾すると左端, @ で修飾すると右端にも等間隔のスペースが空けられる.

評価式 戻り値
(format nil "~25<1~;10~;100~;1000~;10000~>") "1   10   100  1000  10000"
(format nil "~:25<1~;10~;100~;1000~;10000~>") "110100100010000"
(format nil "~@25<1~;10~;100~;1000~;10000~>") "110100100010000"
(format nil "~:@25<1~;10~;100~;1000~;10000~>") "110100100010000"

セグメントの中に ~^ ディレクティブがあり,その処理の実行時に arg がすべて消費されていた場合そこで処理が中断され,そのセグメントより前のセグメントのみが出力される (中断条件を変えるこもできる.後の ~^ の項を参照)
最初のセグメントの区切りを ~:; のように : で修飾すると,この最初のセグメントは別の扱いとなる.このときの最初のセグメントは,このディレクティブによる出力が現在の行に表示しきれないときのみ,先頭に出力される.
さらに, ~ n :; とパラメータを指定すると,最初のセグメントの出力が行われる条件が 「出力後,行の残りの文字数が n 文字より少なかったとき」 になる.
このとき必要な情報である1行の長さは出力ストリームから獲得するが,それができない場合は72に設定される. ~ n , m :; として,1行の流さを m の値で明示的に設定することもできる.

4 制御フロー操作

4.1 ~* Go-To

arg の消費: パラメータ,修飾子による

パラメータ: ~ n *

なにもせずに arg n (デフォルトは1) 個消費する.
: だけで修飾すると,逆に,消費した arg n 個分戻す.
@ だけで修飾すると,次に消費する arg n 番目の arg になる.

4.2 ~[ ... ~] Conditional Expression

arg の消費: 0または1個

パラメータ: ~ n [

... には ~<...~> と同様 ~; でセグメントに分割された control-seqence を記述する.
n を指定しなかった場合は arg を1つ消費し,その arg 番目のセグメントを出力する.
n を指定すると, arg は消費せず, n 番目のセグメントを出力する.
最後のセグメントを ~:; で区切っておくと,範囲外のセグメント番号が指定された場合に, ~:; の後のセグメントが出力されるようになる. ~:; がない場合,範囲外のセグメント番号を与えるとなにも出力されない.
~:[ のように ~[ : 修飾子をつけると少し意味が変わる.セグメントは ~:[ falsestr ~; truestr ~] のように2つに区切る.このとき, arg が1つ消費され,それが nil であれば falsestr が,非 nil であれば truestr が出力される.
~@[ @ で修飾するとまた別の意味になる.この場合, ~@[ truestr ~] とセグメントは1つだけ用意する.このとき,まず次に消費しようとしている arg の値が参照される.もしその値が非 nil であれば, arg は消費されず, truestr が出力される.値が nil のときは arg が消費され, truestr は出力されない (どちらの場合でも消費される arg の数を同じにするため, truestr の中で arg が1つ消費されるようにしておくのが普通)

4.3 ~{ ... ~} Iteration

arg の消費: 修飾子による

パラメータ: ~ n {

修飾子をつけなかった場合, arg を1つ消費する.この arg はリストでなければならない. ... control-sequence とする format 関数の処理を上記のリストを引数として,その要素を使いきるまで繰り返し実行し,結果を順に出力する.
~{ : で修飾すると再帰的な format への引数の与え方が変わる.この場合も arg を1つ消費するが,これはリストを要素とするリストでなければならない.そして k 回目のループでは arg 中の k 番目のリストの要素を引数として format の処理を実行する (つまり,繰り返し回数は arg のリストの要素数となる)
@ ~{ を修飾すると,今度は現在の format 残りの arg を引数として再帰的な format の処理をそれらを使いきるまで繰り返す.
: @ の両方で修飾した場合,現在の format の残りの arg はすべてリストでなければならない.各ループでそれらを1つづつ引数として消費しながら再帰的な format の処理をを繰り返し実行する.
パラメータ n を与えると,引数が残っていても最大 n 回までしか繰り返さない.
~} : で修飾しておくと,引数が最初から空でも,最低1回はループ内の処理を行う (ただし n が0だとそちらが優先される)
... に何も書かなかった場合,まず arg が消費され,その値 (文字列でなければならない) が再帰的な format control-sequence として使われる.
... 中に ~^ が含まれていると,消費すべき arg が残っていない場合そこでループ処理を中断する (ただし : 修飾子つきのループ内の場合は,ループから完全に脱出するのではなく,次のループ処理に移る.このとき,完全に脱出したい場合は ~:^ を使う.この場合 arg が残っていない」 とは, ~^ ではsublist内の要素を使い切ったことであり, ~:^ ではsublistを使い切ったことであることに注意)
中断条件は, ~^ へのパラメータで変えることもできる.詳しくは後の ~^ の項を参照.

4.4 ~? Recursive Processing

arg の消費: 1または2個

パラメータ: なし

修飾子をつけなかった場合,2つ arg を消費する.1つめを control-sequence ,2つめ (リストでなければならない) の要素を引数として再帰的に format 関数の処理を行い,結果を出力する.
修飾子 @ をつけると,1つめの arg を消費し,これを control-sequence とするところまでは上と同じだが,引数は現在実行中の format の残りの arg から消費する ~@? の位置に control-sequence が置き換わると考えればよい)

5 その他

5.1 ~( ... ~) Case Conversion

arg の消費: なし

パラメータ: なし

... の処理結果中のアルファベットをすべて小文字にする.
~( : で修飾すると単語の先頭のみを大文字,それ以外を小文字にする.
@ で修飾すると1つめの単語の先頭を大文字,それ以外を小文字にする.
両方で修飾すると全てのアルファベットを大文字にする.

5.2 ~P Plural

arg の消費: 0または1個

パラメータ: なし

: で修飾しなかった場合は arg を1つ消費し,修飾した場合は最後に消費した arg を参照する.その値が1と eql の意味で等しければ s を出力する.等しくなければなにも出力しない.
修飾子 @ をつけておくと,参照した値が1と eql のときは y ,そうでないときに ies が出力される.

5.3 ~^ Escape Upward

arg の消費: なし

パラメータ: ~ n , m , p ^

~< ... ~> 内で使用すると,消費できる arg が残っていなければ以降のセグメントの処理を中断する. ~{ ... ~} 内で使用すると,同様の場合そこでループを抜ける.これらに特有の話はそれぞれの項を参照.
それ以外の場所で使用すると, arg が残っていない場合 format の処理自体をそこで中断する.
パラメータ n を与えると,中断条件が n =0のとき」 となる.デフォルトは残りの arg の数である. (したがって, ~#^ とすればデフォルトと同じ条件になる.)
m も指定すると,中断条件は n = m のとき」 になる.
さらに p も指定すると,中断条件は n m p のとき」 になる.

5.4 ~〈改行〉 Ignored Newline

arg の消費: なし

パラメータ: なし

~ の直後の改行を含め,空白文字以外の文字が現れるまでそれらの空白文字をすべて無視する.
ただし, : で修飾すると直後の改行以外の空白は無視しない.
@ で修飾すると ~ の直後の改行は無視しない.

評価式 戻り値
(format nil "abc
   def~
   12345~:
   xyz~@
   foobar")
"abc
   def12345   xyz
foobar"