Archive for 2010年7月9日|Daily archive page

newLISP で On Lisp する...第7章(番外編)

(この blog は、“short short story または 晴耕雨読な日々”からの引越してきたもの。スクリプトは、newLISP V10.2.1 以降で動作するように書き直しています。
defun 等の newLISP組込関数に無い関数は、特に断らない限り、newlisp-utility.lsp に定義してあります。)

 前回、newLISP では、次のようなスタイルになると書きました。

(define-macro (macro-name)
  (letex (vars)
     (body))

 ここで、body が letex で一つの式にまとめられる時は、どうなるでしょう?
 簡単なマクロで試して見ます。

(define-macro (test)
  (letex (x (args 0))
    x))

 動作はというと、

> test
(lambda-macro () 
 (letex (x (args 0)) x))
> (test '(a b))
(a b)
> (test (+ 1 2))
3

 このように、引数の式が評価されて返されています。
 実は、以前の newLISP では、展開はされても評価されなかったのです。
 そのため、例えば、こうする必要がありました。

(define-macro (test)
  (letex (x (args 0))
    (begin  x)))

 しかし、現在のバージョンでは、上の例で見たように、必要ありません。begin を付けても、実害はありませんけどね(笑)。
 さて、冒頭のスタイルで body が括弧で括られていることにお気付きでしょうか?
 実は、現在の newLISP では、

(define-macro (macro-name)
  (letex (vars)
    body)

 で良かったのです(汗)。
 さて、letex で定義する変数によく _(アンダースコア)を付けています。

(define-macro (test)
  (letex (_x (args 0))
    _x))

 しかし、動作に要注意です。

> test
(lambda-macro () 
 (letex (_x (args 0)) _x))
> (test (+ 1 2))
3
> (let (x 1) (test (+ x 2)))
3
> (let (_x 1) (test (+ _x 2)))

ERR: value expected in function + : _x
called from user defined function test

 このように、letex で定義された変数名と同じに変数名は参照できず、エラーになります。
 _(アンダースコア)を付けているのは、通常、_(アンダースコア)を付けた変数を使わないという前提で、このエラーを避けているのです。
 このエラーが、“On Lisp” 本書でこれから繰り返し強調される変数捕捉の問題です。
 ダイナミック・スコープの newLISP では、Common Lisp 以上に厄介かもしれません。“On Lisp” では問題となりそうな変数を gensym で作って回避しているのですが、newLISP では、もっと簡単に解決できます。

(context 'MAIN:test)
(define-macro (test:test)
  (letex (_x (args 0))
    _x))
(context MAIN)

 context を使うことで、そのマクロ専用の名前空間に変数捕捉を封じ込めることが出来ます。しかも default factor なので global 設定しなくても、任意の context 上でマクロ名だけ使えるすぐれものです。

> test
test
> test:test
(lambda-macro () 
 (letex (test:_x (args 0)) test:_x))
> (test (+ 1 2))
3
> (let (x 2) (test (+ x 1)))
3
> (let (_x 2) (test (+ _x 1)))
3

 newLISP の組込に gensym が無いのは、このためだったりして(笑)。しかし、問題もあります。この方法で、マクロを定義すると、そのマクロ名は、変数名として使えません。関数の引数はおろか、内部変数にも使えなくなります。context の default factor の問題というよりも、newLISP の名前空間とスコープの問題ですけどね。安易なネーミングは、避けたほうが良いでしょう。また、私が、マクロで定義している変数に、_(アンダースコア)を付けているのは、変数捕捉を一時的に避けるためです。しかし、マクロをユーティリティ化する際は、必ず、context の default factor を使って定義します。
 そして、今回、マクロ番外編のまとめです。

 以上、如何でしょうか?

広告