2

関数の本体にマクロが含まれている関数を定義しましょう。マクロは不特定の時点で展開され、*test*このプロセス中に のグローバルな動的値を使用します。

> (defvar *test* nil)
> (defmacro body ()
    `(print ,*test*))
> (defun test ()
    (body))
> (test)
NIL

しかし、関数の定義中にバインド*test*したい場合はどうすればよいでしょうか。1test1NIL

ラップするだけdefunletは機能しません:

> (let ((*test* 1))
    (defun test ()
      (body)))
> (test)
NIL

おそらく、Hyperspec の次の行に関連しています。

コンパイル時の副作用を実行するためにdefunは必要ありません

しかし、それを行う他の方法はありますか?

4

4 に答える 4

3

あなた自身が書いているように、マクロは不特定の時間に展開されます。私のSBCLでは、フォーム全体が評価される前、つまりLETバインディングが有効になる前に、マクロが展開されます。一部のインタプリタでは、バインディングの有効期限が切れた後、関数の実行時にマクロが展開される場合があります。

Common Lispになった初期のバージョンには、COMPILER-LETを介したそのようなメカニズムが含まれていましたが、削除されました。詳細については、COMPILER-LET-CONFUSIONの問題を参照してください。字句的には、MACROLET/SYMBOL-MACROLETを使用していくつかの効果を実現できます。動的にこれを正常に機能させることは困難であり、実際の動的バインディングを使用する必要があると思われる場合は、アプローチを再考することをお勧めします。

于 2012-01-04T16:16:48.433 に答える
1

let次のように導入できます。

(defvar *test* nil)

(defmacro foo ()
  (let ((test (gensym)))
    `(let ((,test *test*))
       (print ,test))))

(defun test-foo ()
  (foo))

(test-foo) => print and returns NIL
(let ((*test* 1))
  (test-foo)) => print and returns 1
于 2012-01-04T17:02:50.857 に答える
1

let の代わりにマクロを使用して評価時間を制御するのはどうでしょうか (ここでは、既知の変数に既知の値を代入していますが、動的変数で遊んでいるので、より多くの変数を処理するように簡単に拡張できます)。

(defmacro letter (&body body)
  (let ((old-test *test*))
    (set '*test* 1)
    `(progn
       ,@body
       (set '*test* ,old-test))))

定義test:

(letter (defun test () (body)))

使用test:

CL-USER> (test)

1 
1

これは SBCL で期待どおりに動作するようです。他の実装で試す前に、ある程度スリープする必要があります。

letterうーん、マクロ展開すると、マクロ展開されて評価された場合にのみ正しく機能することが明らかになります。単純にマクロを展開しても、元*test*の値には戻りません (doh)。したがって、これは適切な「バインディング エミュレーター」ではありません。

于 2012-01-04T21:08:53.890 に答える
0

*test*変数はの本体内で有効であるためだと思いますlet

于 2012-01-04T14:21:45.163 に答える