たくさん寝太郎の寝床

料理とITと皿回しが好きなオタクのブログ

Lispの話(format)

こんにちは、たくさん寝太郎です。

Lispの記事を書くのは約3ヶ月振りっぽいです...。
前回はLand of Lispの10章(loop)までやってたっぽいので今回は11章のお勉強をしようと思います。



今までにも出てきた気がしますが、Lispではテキスト表示によくformatを使います。

(format t "hello world")

=>

hello world
NIL

第一引数には次の3つがあります。

  • nil: 出力の代わりに生成されたテキストを文字列をして返す

(format nil "hello world") => "hello world"
  • t: 結果をコンソールに出力する。(返り値はnilになる)
  • stream: データを出力ストリームに書き出す。

ストリームとは

ストリームには「流れ」といった意味があります。プログラムは内部だけで完結するのではなく外側から情報を受け取りたい/情報を渡したい時がありますが、その時に用いられるデータ型がストリームです。

代表的なストリームの型は以下の通りです。

  • コンソールストリーム
  • ファイルストリーム
  • ソケットストリーム
  • 文字列ストリーム

また、ストリームを向きで分類すると次の2つになります。

  • 出力ストリーム
  • 入力ストリーム


ストリームについて詳しくは12章に載っているので次回の記事でまとめようと思います。


話が飛びましたが、format関数の第2引数は制御文字列と呼ばれます。
最初の例では"hello world"が制御文字列です。

制御文字列の中には以下のような制御シーケンスを含めることができます。

制御シーケンス

  • ~s
  • ~a

(format t "She is ~20s." "minase inori")
(format t "I am ~20@a." "netarou")

=>

She is "minase inori"      .
I am              netarou.

~sはprin1のような出力、~aはprincのような出力をします。
例にあるようにa,sの前に整数を置くと出力の最低幅の指定が出来ます。
更に、整数の後に@を加えると値が右詰めで表示されます。

シーケンスへパラメータを渡す場合は,(コンマ)で区切ります。
例えば第3パラメータに整数を渡せば文字列の後に指定した数の空白を置くことができたり、第4パラメータに文字を指定すれば文字列の後に指定した文字を置くことができます。

(format t "feel so ~,,5,'!a" "good")

=>

feel so good!!!!!
数値に関する制御シーケンス


進数変換したい場合は以下のシーケンスを使います。

  • x: 16進数(hexadecimal)表示
  • b: 2進数(binary)表示
  • d: 10進数(decimal)表示
(format t "~b" 1202)

=>

10010110010

16進数って英語でhexadecimalって言うんですね。
8進数は英語でoctalだったので~oを使ってみたら8進数表示になりました。


浮動小数点数には~fを使います。

(format t "~5f" 3.14159265)

=>

3.142

(format t "~,5f" 3.14159265)

=>

3.14159

(format t "~,,4f" 0.1202)

=>

1202.0

第一引数に整数を指定すると小数点を含めて指定された表示幅に、第二引数に整数を指定すると小数点以下が指定された表示幅に丸められます。
また、第三引数に整数nを渡すと表示する数を10^n倍することができます。

Land of Lispには「価格を表示する時には~$を使うと良い」とありますが、~$は小数点第2位まで表示するシーケンスのようです。

(format t "~$" 109.655)
=>
109.65

(format t "~$" 109.656)
=>
109.66

四捨五入ではなく五捨六入なんですね。


複数行出力に関する制御シーケンス

Common Lispには複数行出力するのにterprifresh-lineを使いますが、formatの制御シーケンスでは~%と~&がそれに相当します。

(progn (format t "I have a pen ~5&")
       (format t "~%I have a pineapple "))

=>

I have a pen





I have a pineapple


(progn (format t "I have a pen ~%")
       (format t "~&I have a pineapple "))

=>

I have a pen
I have a pineapple

terpri(~%)は現在の行を終了して改行するのに対し、fresh-line(~&)はカーソルが行頭にない場合に限って改行します。

これらはパラメータに整数を渡すことで改行数を指定できます。



長くなったので今回はこのあたりで終わります。
format関数はかなり複雑な組み込み関数で他にも様々な制御シーケンスが存在します。LoL11章後半では文字列の整列や繰り返しの制御シーケンスについて触れているのですが、それらについては使うことがあったら別の記事でまとめようと思います。