次回は、ソースに入れるかも。
しかし、このブログは学習ブログなので「読者を念頭におかない!」ことが信条なのですが、だんだん、ゴミを撒き散らしているだけなのでは?という懸念が強くなってきました。WebのS/N比を低くしているだけだなぁ、と。まぁそんなこともぼちぼち考えながら、当面は続けていこうと思います。
こつこつ。
;;;; CLtL2におけるbackquoteの定義
;; Steeleの実装を読み解く前に、CLtL2のbackquoteの定
;; 義をおさらいしておこう。まず、
;; 「バッククォートは、テンプレートを用いることによっ
;; て複雑なデータ構造を構築するプログラムを簡単に書
;; けるようにする。」
;; という言葉が、アタリマエなのだが新鮮。そうか、バッ
;; ククォートはデータ構造のテンプレート記法なのだと。
;; さて前回見たとおり、文字apostropheはマクロ文字としては、
;; (quote foo)の略記にすぎない。'fooは、fooが何かに
;; 関わらず、(quote foo)として読み込まれる。
;; うむ。これはわかる。大丈夫。進もう。
;; 次はバッククォート。まず、バッククォートという言
;; 葉の使い方を確認しよう。通常の会話において、バッ
;; ククォートと言うとき、それは単体のマクロ文字を指
;; していることもあるし、バッククォート構文を指して
;; いることもある。大抵の場合、どちらを指しているか
;; は文脈によってあきらかである。なので、この文章で
;; も文脈を利用して「バッククォート」という単語でこ
;; の2つのものを指していくことも可能である。しかし、
;; ちょっと考えてみると、ここではどちらであるか明示
;; した方が書きやすいし、わかりやすそうだ。そこで、
;; この文章では「バッククォート」というように独立し
;; た単語としては使わずに、「マクロ文字バッククォー
;; ト」か「バッククォート構文」のいずれかを使うこと
;; にする。あとひとつ、先頭文字がバッククォートで始
;; まるLispフォームのことを「バッククォートフォーム」
;; と呼ぶことがある。これも必要に応じて利用する。
;; バッククォート構文の説明に移ろう。
;; バッククォート構文は、リーダマクロとして定義され
;; ている。複雑なデータ構造を簡便に構築するためのテ
;; ンプレート記法を目指したものである。
;; バッククォート構文で使われるマクロ文字は次の通り
;; である。
;; ` , # @ .
;; ここで、# は`の直後にあらわれたときのみバック
;; クォート構文のマクロ文字として機能し、@ と .
;; は、, の直後にあらわれたときのみそうである。
;; さて、一般的に、リーダマクロの定義は、マクロ文字
;; を含む印字表現をリーダが読んだときに、リーダの返
;; り値として、どのようなLisp objectを返すのか、と
;; いう変換として記述される。すなわち、定義がリーダ
;; で閉じているのだ。図示すると次のとおり。
;; <印字表現> -(reader)-> <Lispオブジェクト>
;; 例えば、
;; 'foo -(reader)-> (quote foo)を読み込んだときに返すLispオブジェクトとまったく同一。
;; である。すなわち、'fooをreaderが(quote foo)と読
;; み替えると考えてもまったく支障も懸念も無い。すな
;; わち、readerが返すLispオブジェクトの印字表現は、
;; (quote foo)であると言ってしまってよいのだ。
;; しかしバッククォート構文のマクロ文字については、
;; 少々事情が異なる。
;; バッククォート構文では、readerが返却する<Lisp オ
;; ブジェクト>が何であるかは実装依存としてしまって
;; いる。では、バッククォート構文の意味はどう定義す
;; るかというと、その実装依存な<Lisp オブジェクト>
;; をevaluatorが評価した結果得られる<Lispオブジェク
;; ト>が何かという、もう一段先で記述しているのだ。
;; <印字表現> -(reader)-> <実装依存> -(evaluator)-> <Lispオブジェクト>
;; これは後で再度説明する。
;; 準備は整った。ここからは、バッククォート構文の意
;; 味を形式的に定義しよう。
;; 用語1. form は、断りがない限り、任意のLispフォー
;; ムを表すものとする。
;; 用語2. basic は、form からリストと一般のベクタを
;; 除外したものである。
;; 用語3. 2つのLispフォームがあり、それらを評価した
;; 結果がequalにて等しいことを、評価等価と呼ぶ。こ
;; のとき、評価する回数は複数回も考えられる。A と
;; B が一回の評価において評価等価なとき、A = Bと書
;; く。二回の評価において評価等価なときは、A == B
;; と書く。
;; 1. `basic = 'basic.
;; 2. `,form = form.
;; ただし、ここでの form は、@ または . で始まる
;; Lispフォームを除外している。
;; 3. `,@form は誤りである。
;; 4. `(x1 x2 x3 ... xn . atom) = (append [x1] [x2] [x3] ... [xn] (quote atom))
;; ここで角括弧は次のようなものである。(上から順に判定)
;; - xjが,@で始まるとき、xjを,@formと書けば、[xj] = form。
;; - xjが,で始まるとき、xjを,formと書けば、 [xj] = (list form)。
;; - それ以外のとき、xjをformと書けば、 [xj] = (list `form)。
;; ここで、`formはバッククォートフォームである。
;; このフォームも再帰的に解釈をすすめる。
;; 5. `(x1 x2 x3 ... xn) = `(x1 x2 x3 ... xn . nil)
;; 6. `(x1 x2 x3 ... xn . ,form) = (append [x1] [x2] [x3] ... [xn] form)
;; 7. `(x1 x2 x3 ... xn . ,@form) は誤りである。
;; 8. `#(x1 x2 x3 ... xn) = (apply #'vector `(x1 x2 x3 ... xn))
;; 9. ,@ が使われているところはどこでも、,@ の代わ
;; りに ,. を使うことができる。この両者の違いは、,.
;; の場合は、後続のformが生成するリストを破壊しても
;; よいということである。
;; 10. バッククォート構文が入れ子になっている場合、
;; もっとも内側のマクロ文字バッククォートに属する
;; フォームが最初に展開される。
;; さて、先送りした話に戻ろう。
;; `((,a b) ,c ,@d)
;; を上記ルールに基づいて解釈(展開)すると、
;; (append (list (append (list a) (list 'b) 'nil)) (list ) d 'nil)
;; になる。しかし、バッククォート構文のルールが述べ
;; ているのは、readerがまさにこのLispオブジェクトを
;; 返さなければいけないということではなく、これと評
;; 価等価なLispオブジェクトを返せばよいということだ。
;; それは無数に存在する。例は次のとおり。
;; (append (list (cons a '(b))) (list c d)
;; (list* (cons a '(b)) c d)
;; readerが返すものは、これらのうちどれでもよいのだ。
;; さて、これでCLtL2のバッククォートの定義のおさら
;; いができた。次回は、ついにSteeleの実装をみていこ
;; う。