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
)
t
にすると標準出力
(*standard-output*)
に出力.
nil
にするとストリームには出力せず,出力を返り値(文字列)として返す.
printf
における
%d
などのようなもの.詳細は後述)は特別な意味を持つ.
戻り値:
destination
が非
nil
のときは
nil
,
destination
が
nil
のときは出力文字列.
format
の引数
control-sequence
の文字列中の以下の並びは
formatディレクティブ
として扱われ,特別な意味を持つ.
~
[[
パラメータ
1
]
,
][[
パラメータ
2
]
,
] ... [
パラメータ
n
][
:
][
@
]
ディレクティブ指定文字
各 パラメータ には以下のいずれかを指定する.省略も可.
'
文字
: 文字をパラメータにしたい場合.
V
(または
v
)
: 次の
arg
を消費して,その値をパラメータにする.
#
:
arg
の残り数をパラメータにする.
‘
:
’
や
‘
@
’
の
修飾子
は,ディレクティブの振る舞いを変化させる.どのように変化するかはディレクティブごとに違う.
ディレクティブ指定文字 はこれから説明するアルファベットや記号のいずれか.大文字小文字は区別されない.
~%
—
Newline
arg の消費: なし
パラメータ:
~
n
%
#\Newline
)
を出力.
n
を指定すると
n
回改行する. 評価式 | 戻り値 |
(format nil "~%abc~%def~3%ghi")
|
" abc def ghi" |
~&
—
Fresh-Line
arg の消費: なし
パラメータ:
~
n
&
fresh-line
関数を呼ぶ.つまり,現在のカーソルの位置が行の先頭でなければ改行
(
#\Newline
)
を出力する.
fresh-line
を1回呼んだあと,(
n
-1) 個の改行を出力する.
n
が0のときはなにもしない. 評価式 | 戻り値 |
(format nil "~&abc~&def~3&ghi")
|
"abc def ghi" |
~|
—
Page
arg の消費: なし
パラメータ:
~
n
|
#\Page
)
を出力.
n
を指定すると
n
個の改ページを出力する. 評価式 | 戻り値 |
(coerce (format nil "~4|") 'list)
|
(#\Page #\Page #\Page #\Page) |
~~
—
Tilde
arg の消費: なし
パラメータ:
~
n
~
~
’
を出力する.
n
を指定すると
n
個のチルダを出力する. 評価式 | 戻り値 |
(format nil "~10~")
|
"~~~~~~~~~~" |
~A
—
Aesthetic
arg の消費: 1個
パラメータ:
~
mincol
,
colinc
,
minpad
,
padchar
A
princ
関数の方法で出力する.たとえば文字列の場合,前後のダブルクォートは出力されない.
:
’
をつけておくと
“
()
”
と出力されるようになる. 評価式 | 戻り値 |
(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
個の
padchar
(デフォルトは空白)
を表示する. 評価式 | 戻り値 |
(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)" |
~S
—
Standard
arg の消費: 1個
パラメータ:
~
mincol
,
colinc
,
minpad
,
padchar
S
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\")" |
~W
—
Write
arg の消費: 1個
パラメータ: なし
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)" |
~C
—
Character
arg の消費: 1個
パラメータ: なし
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" |
~D
—
Decimal
arg の消費: 1個
パラメータ:
~
mincol
,
padchar
,
commachar
,
comma-interval
D
@
’
で修飾すると,
‘
+
’
や
‘
-
’
の符号が必ず出力される.
:
’
で修飾すると,下から
comma-interval
桁ごとに
commachar
で指定された文字で数字を区切る
(
comma-interval
のデフォルトは3,
commachar
のデフォルトは
‘
,
’
)
. 評価式 | 戻り値 |
(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" |
~B
—
Binary
arg の消費: 1個
パラメータ:
~
mincol
,
padchar
,
commachar
,
comma-interval
B
~D
と同じ. 評価式 | 戻り値 |
(let ((x 65535)) (format nil "~D(10進) = ~:,,' ,4B(2進)" x x))
|
"65535(10進) = 1,111,111,111,111,111(2進)" |
~O
—
Octal
arg の消費: 1個
パラメータ:
~
mincol
,
padchar
,
commachar
,
comma-interval
O
~D
と同じ. 評価式 | 戻り値 |
(let ((x 65535)) (format nil "~D(10進) = ~:,,' ,3O(8進)" x x))
|
"65535(10進) = 177,777(8進)" |
~X
—
heXadecimal
arg の消費: 1個
パラメータ:
~
mincol
,
padchar
,
commachar
,
comma-interval
X
~D
と同じ. 評価式 | 戻り値 |
(let ((x 65535)) (format nil "~D(10進) = ~:,,' ,2X(16進)" x x))
|
"65535(10進) = f,fff(16進)" |
~R
—
Radix
arg の消費: 1個
パラメータ:
~
radix
,
mincol
,
padchar
,
commachar
,
comma-interval
R
~D
,
~B
,
~O
,
~X
と同じである. 評価式 | 戻り値 |
(let ((x 65535) (r 7)) (format nil "~D(10進) = ~vR(~D進)" x r x r))
|
"65535(10進) = 362031(7進)" |
:
’
修飾子のみ: 整数を英語の綴り(序数)で出力.
@
’
修飾子のみ: 整数をローマ数字で出力.
:
’
修飾子と
‘
@
’
修飾子の両方: 整数を旧ローマ数字で出力. 評価式 | 戻り値 |
(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" |
~F
—
Fixed-Format Floating-Point
arg の消費: 1個
パラメータ:
~
w
,
d
,
k
,
overflowchar
,
padchar
F
@
’
を指定すると,
+
や
-
の符号を必ず出力する. 評価式 | 戻り値 |
(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" |
評価式 | 戻り値 |
(format nil "pi = ~9,3,,,'_F" (* (atan 1.0) 4))
|
"pi = ____3.142" |
評価式 | 戻り値 |
(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" |
~E
—
Exponential Floating-Point
arg の消費: 1個
パラメータ:
~
w
,
d
,
e
,
k
,
overflowchar
,
padchar
,
exponentchar
E
@
’
修飾子の意味は
~F
と同じである.
0
)
で埋められる. 評価式 | 戻り値 |
(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" |
~G
—
General Floating-Point
arg の消費: 1個
パラメータ:
~
w
,
d
,
e
,
k
,
overflowchar
,
padchar
,
exponentchar
G
+e
”
,
“
-e
”
の2文字分)
nil
(
w
が省略された場合)
(max
〈
argの有効桁数
〉
(min
n
7
))
~
ww
,
d
,,
overflowchar
,
padchar
F~
ee
@T
,
~
w
,
d
,
e
,
k
,
overflowchar
,
padchar
,
exponentchar
E
として出力される.
~G
に
‘
@
’
修飾子が指定されていた場合は,そのまま
~F
や
~E
の処理に適用される.
~$
—
Monetary Floating-Point
arg の消費: 1個
パラメータ:
~
d
,
n
,
w
,
padchar
$
~F
,
~E
と同じ.
@
’
を指定すると,
+
や
-
の符号を必ず出力する.
:
’
を指定すると,符号を
padchar
による文字埋めより前に表示する. 評価式 | 戻り値 |
(format nil "$~$ $~$" 3.0 0.82)
|
"$3.00 $0.82" |
(format nil "$~@:,,10,'_$" 123.45)
|
"$+123.45" |
~T
—
Tabulate
arg の消費: なし
パラメータ:
~
colnum
,
colinc
T
@
’
で修飾すると相対タブになる.つまり,まず
colnum
個の空白を出力し,そこから一番近い
k
×
colinc
文字目の位置までカーソルを移動させる. 評価式 | 戻り値 |
(format nil "~5T0")
|
" 0" |
(format nil "title:~6,3T1~6,3T23~6,3T456~6,3T7890")
|
"title: 1 23 456 7890" |
~< ... ~>
—
Justification
arg の消費: なし
パラメータ: なし
~;
を書くと,文字列がそこでセグメントに分割される.
~<
を
‘
:
’
で修飾すると左端,
‘
@
’
で修飾すると右端にも等間隔のスペースが空けられる. 評価式 | 戻り値 |
(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
文字より少なかったとき」
になる.
~
n
,
m
:;
として,1行の流さを
m
の値で明示的に設定することもできる.
~*
—
Go-To
arg の消費: パラメータ,修飾子による
パラメータ:
~
n
*
:
’
だけで修飾すると,逆に,消費した
arg
を
n
個分戻す.
@
’
だけで修飾すると,次に消費する
arg
が
n
番目の
arg
になる.
~[ ... ~]
—
Conditional Expression
arg の消費: 0または1個
パラメータ:
~
n
[
~<...~>
と同様
~;
でセグメントに分割された
control-seqence
を記述する.
~:;
で区切っておくと,範囲外のセグメント番号が指定された場合に,
~:;
の後のセグメントが出力されるようになる.
~:;
がない場合,範囲外のセグメント番号を与えるとなにも出力されない.
~:[
のように
~[
に
‘
:
’
修飾子をつけると少し意味が変わる.セグメントは
~:[
falsestr
~;
truestr
~]
のように2つに区切る.このとき,
arg
が1つ消費され,それが
nil
であれば
falsestr
が,非
nil
であれば
truestr
が出力される.
~@[
と
‘
@
’
で修飾するとまた別の意味になる.この場合,
~@[
truestr
~]
とセグメントは1つだけ用意する.このとき,まず次に消費しようとしている
arg
の値が参照される.もしその値が非
nil
であれば,
arg
は消費されず,
truestr
が出力される.値が
nil
のときは
arg
が消費され,
truestr
は出力されない
(どちらの場合でも消費される
arg
の数を同じにするため,
truestr
の中で
arg
が1つ消費されるようにしておくのが普通)
.
~{ ... ~}
—
Iteration
arg の消費: 修飾子による
パラメータ:
~
n
{
format
関数の処理を上記のリストを引数として,その要素を使いきるまで繰り返し実行し,結果を順に出力する.
~{
を
‘
:
’
で修飾すると再帰的な
format
への引数の与え方が変わる.この場合も
arg
を1つ消費するが,これはリストを要素とするリストでなければならない.そして
k
回目のループでは
arg
中の
k
番目のリストの要素を引数として
format
の処理を実行する
(つまり,繰り返し回数は
arg
のリストの要素数となる)
.
@
’
で
~{
を修飾すると,今度は現在の
format
残りの
arg
を引数として再帰的な
format
の処理をそれらを使いきるまで繰り返す.
:
’
と
‘
@
’
の両方で修飾した場合,現在の
format
の残りの
arg
はすべてリストでなければならない.各ループでそれらを1つづつ引数として消費しながら再帰的な
format
の処理をを繰り返し実行する.
~}
を
‘
:
’
で修飾しておくと,引数が最初から空でも,最低1回はループ内の処理を行う
(ただし
n
が0だとそちらが優先される)
.
format
の
control-sequence
として使われる.
~^
が含まれていると,消費すべき
arg
が残っていない場合そこでループ処理を中断する
(ただし
‘
:
’
修飾子つきのループ内の場合は,ループから完全に脱出するのではなく,次のループ処理に移る.このとき,完全に脱出したい場合は
~:^
を使う.この場合
「
arg
が残っていない」
とは,
~^
ではsublist内の要素を使い切ったことであり,
~:^
ではsublistを使い切ったことであることに注意)
.
~^
へのパラメータで変えることもできる.詳しくは後の
~^
の項を参照.
~?
—
Recursive Processing
arg の消費: 1または2個
パラメータ: なし
format
関数の処理を行い,結果を出力する.
@
’
をつけると,1つめの
arg
を消費し,これを
control-sequence
とするところまでは上と同じだが,引数は現在実行中の
format
の残りの
arg
から消費する
(
~@?
の位置に
control-sequence
が置き換わると考えればよい)
.
~( ... ~)
—
Case Conversion
arg の消費: なし
パラメータ: なし
~(
を
‘
:
’
で修飾すると単語の先頭のみを大文字,それ以外を小文字にする.
@
’
で修飾すると1つめの単語の先頭を大文字,それ以外を小文字にする.
~P
—
Plural
arg の消費: 0または1個
パラメータ: なし
:
’
で修飾しなかった場合は
arg
を1つ消費し,修飾した場合は最後に消費した
arg
を参照する.その値が1と
eql
の意味で等しければ
“
s
”
を出力する.等しくなければなにも出力しない.
@
’
をつけておくと,参照した値が1と
eql
のときは
“
y
”
,そうでないときに
“
ies
”
が出力される.
~^
—
Escape Upward
arg の消費: なし
パラメータ:
~
n
,
m
,
p
^
~<
...
~>
内で使用すると,消費できる
arg
が残っていなければ以降のセグメントの処理を中断する.
~{
...
~}
内で使用すると,同様の場合そこでループを抜ける.これらに特有の話はそれぞれの項を参照.
~#^
とすればデフォルトと同じ条件になる.)
~〈改行〉
—
Ignored Newline
arg の消費: なし
パラメータ: なし
~
の直後の改行を含め,空白文字以外の文字が現れるまでそれらの空白文字をすべて無視する.
:
’
で修飾すると直後の改行以外の空白は無視しない.
@
’
で修飾すると
~
の直後の改行は無視しない. 評価式 | 戻り値 |
(format nil "abc
|
"abc def12345 xyz foobar" |