2

我正在编写一个递归 Lisp 宏,取一个数字n并评估主体n次(来自 ANSI Lisp 的练习)。我尝试了两种方法——让宏在其扩展中调用自身,以及让宏扩展为本地递归函数。两者都不像我想要的那样工作。

这是第一个——它有一个堆栈溢出,但是当我使用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))))))

这是第二个 - 它出错,“未定义的函数 #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发生在运行时。

要回答您的第二个问题,您不能使用flet指定递归函数,而是必须使用labels

于 2013-05-09T03:54:15.800 回答
7

由于 Common Lisp 中的宏扩展发生在运行时之前,这有点棘手。

请记住,宏查看源代码。这表示:

  • 使用宏时,数字 n 必须作为数字而不是变量传递。因此,在宏扩展时,该数字是已知的。对于这样的宏,我会在宏中进行检查——否则你总是很想写类似的东西(let ((n 10)) (n-times n ...))——这是行不通的。

  • the macro needs to compute the recursive iteration. Thus the logic is in the macro, and not in the generated code. Each macro needs to generate code, which is one step simpler at macro expansion time - until the basic case is reached.

于 2013-05-09T08:49:11.813 回答