マクロの話その1。
Schemeのマクロはややこしい。しかも、ややこしい点が複数あるから、余計にややこしい。しかしまあ、ややこしいと繰り返してもややこしいだけなので、1つずつややこしい点を解消していこう。
まずはマクロの定義。
(define-macro マクロ名 プロシージャ)これだけ見れば、別にグローバルな名前付きプロシージャの定義とほとんど同じように見えるが、問題はその展開。
C言語のマクロは、マクロの定義に従って参照個所を機械的に置換するだけだが、Schemeのマクロはdefine-macroで定義したプロシージャの中身で置換する、…わけではない。プロシージャの実行結果で置換するのだ!
例えば、以下のコードについて考えてみよう。
(define foo ‘bar)foo-macro1のプロシージャの実行結果はfooというシンボル。foo-macro2のプロシージャの実行結果はfooの評価値、すなわちbarというシンボルである。これをふまえて手動でマクロ展開すると
(define bar “bar is bar”)
(define-macro foo-macro1 (lambda() ‘foo))
(define-macro foo-macro2 (lambda() foo))
(print “foo-macro1 = ” (foo-macro1))
(print “foo-macro2 = ” (foo-macro2))
(print “foo-macro1 = “ foo)となる。
(print “foo-macro2 = “ bar)
…と、ここまではあくまで前処理で、この展開結果を実行するのが本番。したがって、最終的には
foo-macro1 = barと、fooの評価値とbarの評価値が表示される。
foo-macro2 = bar is bar
次に引数付きマクロの例。
(define-macro p (lambda (x) (list ‘print x)))マクロが展開されるとき、マクロの仮引数の値は(quote 実引数)である模様。なので、上記の例を手動で展開すると
(let loop ((i 1))
(when (<= i 10)
(p i)
(loop (+ i 1))
)
)
(let loop ((i 1))となり、めでたく1〜10が表示される。ここでもし、マクロ定義のクォート等を怠るとどうなるか。
(when (<= i 10)
(print i)
(loop (+ i 1))
)
)
(define-macro p2 (lambda (x) (print x)))このマクロp2は(print ‘i)の実行結果、すなわち#<undef>に展開されるので、実行しても10回#<undef>が評価されるだけ。こうならないためにも、マクロは基本的に実行結果がS式になるように定義するのだ。
(let loop ((i 1))
(when (<= i 10)
(p2 i)
(loop (+ i 1))
)
)
…とはいえ、listやquoteを使って巧い具合にS式になるようにマクロを定義するのは、結構面倒。そこで出てくるのがquasiquoteなのだが、長くなってきたので次回に続く。
0 件のコメント:
コメントを投稿