formatで~{を使う
format構文は非常に奥が深い・・・そう感じるきっかけになった使い方についてメモ。
formatでは、引数内部で~aとか~%とか、いくつか特殊な役割を持つ書き方がある。その中の一つに、~{というものがある。これが、Practical Common Lisp で使用されていた。
の、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 だとこんな書き方ができる、ということを知った。~{ と ~} の書き方にはもっと慣れていきたい。