読者です 読者をやめる 読者になる 読者になる

レガシーコード生産ガイド

私に教えられることなら

Common Lispでのバッククォートの入れ子の謎

M.Hiroi's Home Page / Common Lisp 入門:番外編 を見ながら、 phaendal/a-piece-of-lisp · GitHubLisp処理系を作っていってるんだけど、Common Lispでのバッククォートの入れ子の処理が大分謎だったのでわかった部分だけ書いておく

最初に謎だったのは、sbcl

``,(car (list 1 2)) ; => (CAR (LIST 1 2))

これが`,(car (list 1 2))を返すと思ってたんだけど、何故か(car (list 1 2)と入れ子になったバッククォートも消えてしまった。

いろいろ試した結果、

`,`(car (list 1 2)) ; => (CAR (LIST 1 2))

と同じだとわかった。つまりバッククォートが入れ子になっていた場合、出てくるカンマは最も、又はひとつ外側のバッククォートに対応する?

次のコードを評価してみると

`(1 2 `(3 ,4)) ; => (1 2 '(3 4)

一番内側に対応するなら、(1 2 `(3 ,4))が返ってきそう。 しかしquoteに置き換えられてるのを見ると、単純な入れ子の処理でもないっぽい。

`(1 2 `(3 `(,4))) ; => (1 2 '(3 '(4)))

この結果を見ると一番外側みたいだ。

さらに、clispで以下のコードを評価した結果と

;; clisp
`,`(list 1 2 3) ; => (LIST 1 2 3)
``,(list 1 2 3) ; => (LIST 1 2 3)

sbclで評価した結果が違う。

;; sbcl
`,`(list 1 2 3) ; => (LIST 1 2 3)
``,(list 1 2 3) ; => `(,1 ,2 ,3)

評価の仕方とか、list関数の実装がそもそも違うんだろうか?

CLtL2を眺めてみると、

If the backquote syntax is nested, the innermost backquoted form should be expanded first. This means that if several commas occur in a row, the leftmost one belongs to the innermost backquote.

とある。

several commas occur in a rowって、,,3 なのか ,3 ,4 みたいなのかどっちだろう?意味的には前者だと思うけど…と思って次のコードを試してみたら

``(car (list ,x ,y)) ; => `(car (list ,x ,y))

``(car (list ,,x ,y))
; xが定義されてない場合はエラー
; (defvar x 1)しておくと、`(CAR (LIST ,1 ,Y))

なるほど、,,xの場合一番左のカンマは一番内側に対応してるから、まず,xが評価されて、その結果と一番左のカンマがその場に残るのか…

やや混乱してきたので今日はこのへんで中断する。

On Lisp、実用CommonLisp、実践CommonLispのどれかにこの説明あるかな? とりえあずこの動きがちゃんとわかるまでマクロを作るマクロは避けといた方が良さそうだ。

広告を非表示にする