HOMEUP

format - 俺式 - Common Lisp Memo

format destination control-string &rest args => result

Common Lispのformat関数は私の知っているプログラミング言語の中で最強です。他のプログラミング言語で何行か必要な出力もCommon Lispのformat関数一つだけで出力できたりします。

loopマクロと並んでCommon Lispユーザの間ではいろいろと意見が分かれるらしいですが私はloop、formatどちらも好きです。(だって便利じゃん^^v)

参考にしたページはカーネギーメロン大学のCommon Lisp the Language, 2nd Editionっていうページです。

例では漢字を使ったりしていますがemacs+slimeで漢字を使うときは.emacsなどに(setq slime-net-coding-system 'utf-8-unix)を追加しておく必要があります。

destination

出力先を指定します。出力先はストリーム、t、nil、文字列が指定できます。

destinationにnil以外を指定するとresultはnilとなります。nilを指定した場合はresultには結果の文字列が返ってきます。

ストリーム

指定されたストリームに出力します。

CL-USER> (with-open-file (s "/tmp/test.dat"
			    :direction :output
			    :if-exists :supersede)
	   (format s "formatテスト~%"))


NIL

上記のコードで/tmpにtest.datというファイルができます。

t

標準出力に出力します。

CL-USER> (format t "formatテスト~%")
formatテスト
NIL

nil

format関数の戻り値として結果を返します。

CL-USER> (format nil "formatテスト~%")
"formatテスト
"

文字列

destinationにフィルポインタ付きの文字列を指定することで文字列へ出力を行うことができます。

例では可変長の文字列を作ってそれに書き込んでいます。

CL-USER> (defparameter *o* (make-array 1
				       :fill-pointer 0
				       :adjustable t
				       :element-type 'character))
*O*
CL-USER> *o*
""
CL-USER> (format *o* "test")
NIL
CL-USER> (format *o* "漢字")

NIL
CL-USER> *o*
"test漢字"

control-string

control-stringは"~"(チルダ、でも普段は「にょろ」と読んでるにょろよ〜。)で始まります。チルダの後に続く文字は大文字でも小文字でも構いません。私は小文字が好きです。(Shiftキーを押さなくていいという理由だったりする^^)

format control-string 構文図

構文図だとこんな感じかな?

注:構文図を書いてからこの構文図、パラメータ省略できないじゃん。と、気づきました。そのうち修正します m(--)m

~~ "~"を出力する   ~% 改行   ~& これも改行(余計な行を出力したくないとき)   ~a 基本!普通(自然な形)に出力   ~s S式で出力(?)   ~| 改ページ…使ったこと無い   ~d 整数の出力   ~{ ... ~} 繰り返し  

~~

"~"を出力します。

CL-USER> (format nil "~~")
"~"
back

~%

改行(#\Newline)を出力します。"~5%"とかすると5個の#\Newlineを出力します。

CL-USER> (format nil "~%")
"
"
CL-USER> (format nil "~5%")
"




"
back

~&

~&は現在の出力が行の先頭じゃない時に改行します。以下の例では~%を使ったほうは空行が出力されていますが~&では空行が出力されていません。

CL-USER> (dolist (l '("a" "" "b" "c" "def"))
	   (format t "~a~%" l))
a

b
c
def
NIL
CL-USER> (dolist (l '("a" "" "b" "c" "def"))
	   (format t "~a~&" l))
a
b
c
def
NIL
back

~a

argsから一つ消費して自然な形で出力します。

自然な形っていうのは人間が見て自然な形らしい。

CL-USER> (format nil "this is a ~a" "テスト")

"this is a テスト"
CL-USER> (format nil "1 + 1 = ~a" (+ 1 1))
"1 + 1 = 2"
CL-USER> (format nil "(cdr '(a b c)) => ~a" (cdr '(a b c)))
"(cdr '(a b c)) => (B C)"

最初の文字列の出力、なぜか改行が入っていてちょっと悲しいけど気にしない。(slimeのバグかな?)

"~:a"は引数にnilが入ってくると()で表示します。

CL-USER> (format nil "~a ~:a" nil nil)
"NIL ()"

~aのパラメータ

~aには4個のパラメータが指定できます。

~mincol,colinc,minpad,padcharaのような形式になります。

mincolだけを指定したときは出力される文字数がmincol文字になるまでargの右側に空白が出力されます。@を指定するとargの左側に空白文字が出力されます。

私の使っている処理系ではちゃんと漢字も1文字としてカウントされるのでいわゆる半角/全角混在(こう書くといろいろとツッコミが発生します^^;見逃してくり〜)のデータだと正しく(?)カラムがずれます。

CL-USER> (format nil "~10,,,a" "test")
"test      "
CL-USER> (format nil "~10,,,@a" "test")
"      test"
CL-USER> (format nil "~10,,,a" "テスト")
"テスト       "
CL-USER> (format nil "~10,,,@a" "テスト")
"       テスト"

デフォルトではmincol指定時に詰められる文字padcharは空白です。padcharを指定するともちろん詰め文字を変更できます。

CL-USER> (format nil "~10,,,'*a" "test")
"test******"
CL-USER> (format nil "~10,,,'あ@a" "test")


"ああああああtest"

minpadは@をつけるかどうかによってargの右、または左にpadcharを出力します。(サンプルなし)

で、残ったcolincなんですがmincol文字を越えるまでpadcharをcolincづつ出力するとありますが…何に使うんだろ?

しばらく考えてみます。

back

~s

~aと似ているのですがS式で出力しようと頑張ってくれるらしい。(つまりreadで読み戻せる)

いい例が思いつかない。

CL-USER> (format nil "~a" #\newline)
"
"
CL-USER> (format nil "~s" #\newline)
"#\\Newline"
CL-USER> (format nil "~s" "test")
"\"test\""
back

~|

改ページとか最近した事がないですよね?

とりあえずemacs+slime+sbclでの実行結果。

CL-USER> (format t "~|")
^LNIL

コントロールLが出力されました。

back

~d

整数を出力します。試しに整数以外でやったところ整数以外では~aが使われるみたいです。(sbclで実験)

@をつけると符号を出力します。

CL-USER> (format nil "~@d,~@d" 123 -123)
"+123,-123"

~dのパラメータ

~mincol,padchar,commachar,commacharintervald

mincolは出力する時の桁数を指定します。padcharは出力する桁数がmincolに満たない場合に詰められる文字を指定します。デフォルトは空白。

CL-USER> (dotimes (i 6) (format t "~5,'*@d~%" (expt 10 i)))
***+1
**+10
*+100
+1000
+10000
+100000
NIL

:を指定するとcommacharinterval毎にcommacharが出力されます。

commacharのデフォルトは","、commaintervalのデフォルトは3です。

CL-USER> (dotimes (i 6) (format t "~5,'*,' ,1:@d~%" (expt 10 i)))
***+1
*+1 0
+1 0 0
+1 0 0 0
+1 0 0 0 0
+1 0 0 0 0 0
NIL
CL-USER> (dotimes (i 6) (format t "~5,'*:@d~%" (expt 10 i)))
***+1
**+10
*+100
+1,000
+10,000
+100,000
NIL
back

~{ ... ~} 繰り返し

配列(とかリスト)を出力するとき、Cとかだとループで出力しますがCommon Lispだとformat関数だけで処理できてしまいます。

CL-USER> (defvar a '(1 2 3 4))
A
CL-USER> a
(1 2 3 4)
CL-USER> (format t "~{~d,~}~%" a)
1,2,3,4,
NIL

最後のカンマ(,)が気になります。Cなら最後の要素かどうか調べてカンマ(,)を出力しない処理が必要です。スクリプト系の言語なら、例えばRubyだと

a=[1,2,3,4]
p a.join(",")

のように文字列にして出力すれば面倒なカウンタやフラグが必要なくなります。Common Lispだと

CL-USER> (format t "~{~d~^,~}~%" a)
1,2,3,4
NIL

で、OK。formatだけですんじゃいます。

注意:このサンプルはubuntu + sbcl で emacsのslimeからいろいろとやっているのですが ~{ と ~}の間にリストを食べてくれる制御文字がないと無限ループにおちいります。~{~d,~}の例でdの前の~が抜けていて制御が戻らなくなり(一応受け付けるのですがむちゃくちゃ遅いので)プロセスをkillしました。体験談 (^^;)

back

~~

~を出力します。

back
HOMEUP