マクロの中でローカル変数を使いたい場合。例えば2つの変数の値を入れ替えるマクロを、単純にletを使って書くとこうなる。
(define-macroこれを
swap (lambda (a b)
`(let ((tmp ,a)) (set! ,a ,b) (set! ,b tmp))
)
)
(define x 10)このように使用すれば、期待通りxとyが入れ替わってくれるのだが、
(define y 20)
(swap x y)
(print "x=" x ", y=" y)
(define foo 100)このように使用すると、fooとtmpが入れ替わってくれない。何が起こったかは、マクロを手動で展開してみると一目瞭然。
(define tmp 200)
(swap foo tmp)
(print "foo=" foo ", tmp=" tmp)
(let ((tmp foo)) (set! foo tmp) (set! tmp foo))fooはローカル変数tmpに保存した値を書き戻しただけだし、letの外のtmpは参照すらされていない。
要するに、マクロの引数とローカル変数の名前がかぶると、引数が内側から見えなくなる問題が起こる。ならば、引数とは絶対にかぶらない名前をローカル変数に付けてやればよい。そこで出てくるのがgensym。gensymは評価するたびにユニークなシンボルを返すので、これをローカル変数名として使ってやればよい。
(define-macroぱっと見、最初のswapと似てはいるが、ローカル変数tmpの役割がまるで違うことに注意。gensym使用版swap2のtmpは、マクロ展開時に使用するローカル変数。そしてtmpが保持するのはaの値ではなく、aの値を保持する変数の名前。tmpはunquoteされているので、マクロの展開結果にtmpは現れない。
swap2 (lambda (a b)
(let ((tmp (gensym)))
`(let ((,tmp ,a)) (set! ,a ,b) (set! ,b ,tmp))
)
)
)
0 件のコメント:
コメントを投稿