Shammer's Philosophy

My private adversaria

formatで~{を使う

format構文は非常に奥が深い・・・そう感じるきっかけになった使い方についてメモ。

formatでは、引数内部で~aとか~%とか、いくつか特殊な役割を持つ書き方がある。その中の一つに、~{というものがある。これが、Practical Common Lisp で使用されていた。

 Practical: A Simple Database

の、dump-dbという関数。

(defun dump-db ()
  (dolist (cd *db*)
    (format t "~{~a:~10t~a~%~}~%" cd)))

これだけだと、正直よくわからない。まず、dolist は Java でいうところの foreach に近いもの。*db*は、リストの変数であり、この中のリストの個々の要素を cd という変数に格納して、その cd の内容を画面に出力する、という内容になる。

この構文を見たときの最初の印象は、~a が2つあるのになぜ cd という変数を一つしか指定しないのだ?という内容だった。実際は上記のコードで何も問題なく動作する(この関数を定義している話の流れでは、cd 内の要素が偶数個、という前提がある)。

いくつか段階を追って考えると理解できた。そのために試したコードを以下に記載。

CL-USER> (setq x (list 10 20 30 40))

(10 20 30 40)
CL-USER> (format t "~{~a:~a~}~%" x)
10:2030:40
NIL
CL-USER> (format t "~{~a~}~%" x)
10203040
NIL
CL-USER> (format t "~a~%" x)
(10 20 30 40)
NIL
CL-USER> (format t "~{~a:~a~%~}~%" x)
10:20
30:40

NIL
CL-USER> 

~a とするだけだと、その後で指定された変数の値すべてが画面に出力される(上記例だと(10 20 30 40)というリスト)。でも、~a を ~{ と ~} で囲むと、~a で指定された変数(リスト)の中の個々の値が対象になる。~a が2つあると、cd 内の個々の値に対して順番に対象がずれていく。Practical Common Lisp のページでは、この cd というのはキーワードリストになっていて、2つセットで扱うと都合がいいデータ構造になっている。

JavaとかCだと、こういう場合はループを2重にして、一つのオブジェクト(構造体)のメンバをひと通り出力したあとで次のオブジェクトへ、という書き方をしないといけない。でも、Lisp だとこんな書き方ができる、ということを知った。~{ と ~} の書き方にはもっと慣れていきたい。