2

数値nを取り、本体をn回評価する再帰的な Lisp マクロを作成しています(ANSI Lisp の演習)。私はこれを行うための 2 つの方法を試しました。展開時にマクロ自体を呼び出す方法と、マクロをローカル再帰関数に展開する方法です。どちらも私が望むようには機能しません。

これが最初のものです。スタック オーバーフローがありますが、macroexpand-1 を使用してその展開を見ると、問題ないように思えます。

(defmacro n-times (n &rest body)
  (let ((i (gensym)))
    `(let ((,i ,n))
     (if (zerop ,i) nil
         (progn
           ,@body
           (n-times (- ,i 1) ,@body))))))

これが 2 番目のものです。「未定義の関数 #xxxx が引数 (z) で呼び出されました」というエラーが発生します。ここで #xxxx は gensym の名前で、z は呼び出した数値よりも 1 小さい値です。gensyms と flet を一緒に使用する方法に問題があると思いますが、これを正しく行う方法がわかりません。

(defmacro n-times (n &rest body)
  (let ((g (gensym)))
    `(flet ((,g (x)
              (if (zerop x) nil
                  (progn
                    ,@body
                    (,g (- x 1))))))
       (,g ,n))))
4

2 に答える 2

9

最初の質問に答えるために、再帰を停止しない再帰マクロ展開があります。マクロ展開はコンパイル時に発生し、実行時に発生するため、 の存在はif再帰的なマクロ展開を停止しません。if

flet2 番目の質問に答えるには、使用して再帰関数を指定することはできませんlabels。代わりに使用する必要があります。

于 2013-05-09T03:54:15.800 に答える
7

Common Lisp でのマクロ展開は実行前に行われるため、これは少し注意が必要です。

マクロはソース コードを認識します。これの意味は:

  • マクロを使用する場合、数値 n は変数ではなく数値として渡す必要があります。したがって、マクロ展開時にその数はわかっています。そのようなマクロの場合、マクロでそれを確認します-そうしないと、常に次のようなものを書きたくなります(let ((n 10)) (n-times n ...))-これは機能しません。

  • マクロは再帰的反復を計算する必要があります。したがって、ロジックはマクロにあり、生成されたコードにはありません。各マクロは、コードを生成する必要があります。これは、基本的なケースに到達するまで、マクロ展開時に 1 段階単純化されます。

于 2013-05-09T08:49:11.813 に答える