2015/01/23
入れ子のバッククオート
自分も良く迷うんだけど、一応簡単な指針がある。
まず、コンマのネストレベルとバッククオートのネストレベルを常に合わせるようにする。 次は良くない例:
``(list ,(+ 1 2))
これは(list ...)が2重のバッククオートの中にあるのに中でコンマを1重にしか使ってない。 規約上はバッククオートよりコンマのネストが少なければ動作はするけど、わかりづらくなるので避ける。
次に、「どのレベルのバッククオートで式を展開したいか」を考えて、 機械的に次の指針をあてはめる:
- 最初のレベル(外側)のバッククオートで式を展開したい場合はこうする:
,',式
- 最初のレベルでは展開せず、二番目(内側)のバッククオートで展開したい場合はこうする:
,,'式
ネストしたバッククオートが出てくる典型例はマクロ定義を生成するマクロだけれど、 その場合、最初のマクロ展開で埋め込みたい式は前者、生成されたマクロ定義の展開時に コンマとして機能させたい場合は後者を使う。
ちょっと手元に例として引けるコンパクトなコードがないので、わざとらしい例だけど、 例えば
(defmacro foo (x) `'(foo ,x))
みたいなマクロを名前を指定して生成するマクロ
(defmacro gen-defmacro (name) ...)
を考えよう。(gen-defmacro foo)
とすると上の(defmacro foo ...)
が生成される。(gen-defmacro bar)
とするとこれが生成される:
(defmacro bar (x) `'(bar ,x))
まず、ここまではすぐ書けるだろう:
(defmacro gen-defmacro (name) `(defmacro ,name (x) `'(...)))
ここのnameはまだバッククオートが1重なので迷わない。問題はバッククオートがネストする...の部分だ。
- nameはgen-defmacroの展開時に埋め込みたい。つまり外側のバッククオートで展開
- xは生成されたdefmacroの展開時に埋め込む、つまり内側のバッククオートで展開
上の指針をあてはめればこの通り。
(defmacro gen-defmacro (name) `(defmacro ,name (x) `'(,',name ,,'x)))
実行例
[1]> (defmacro gen-defmacro (name) `(defmacro ,name (x) `'(,',name ,,'x))) GEN-DEFMACRO [2]> (gen-defmacro foo) FOO [3]> (gen-defmacro bar) BAR [4]> (foo (a b c)) (FOO (A B C)) [5]> (bar (d e f) (BAR (D E F))
3重以上のネストも基本的に同じ。ポイントは comma-quoteが互いにキャンセルするということ。後は右側から展開されてく。
,',',式 ; 一番外側のバッククオートで展開 ,',,'式 ; 二番目のバッククオートで展開 ,,','式 ; 三番目(最も内側)のバッククオートで展開
なお、バッククオートのネストレベルはコンマがあると下がるので、次のようなコードは ネストとは考えない:
`(list ,(list `(list ,x)))
外側のバッククオートはすぐ次のコンマと対応するんで、2番めのlistの式はネストレベルが0になる。
Tags: Programming, CommonLisp, Macro
Post a comment