Common Lisp递归宏扩展

ale*_*rov 9 macros common-lisp

曾几何时我正在玩宏并想出了这个:

(defmacro my-recursive-fact (n)
  (if (= 0 n) '1
    (let ((m (1- n)))
      `(* ,n (my-recursive-fact ,m)))))
Run Code Online (Sandbox Code Playgroud)

它奏效了.

CL-USER> (my-recursive-fact 5)
120
Run Code Online (Sandbox Code Playgroud)

那么我认为如果我使用macroexpand以下方法扩展此宏,这可能是向学生展示递归示例的好方法:

CL-USER> (macroexpand '(my-recursive-fact 5))
(* 5 (MY-RECURSIVE-FACT 4))
T
Run Code Online (Sandbox Code Playgroud)

也就是说,在这种情况下macroexpand-1和之间没有区别macroexpand.我确信我在理解中缺少一些关键点macroexpand,HyperSpec对递归宏没有什么特别之处.

而且我仍然很想知道是否有办法将这种宏扩展到最后.

Lar*_*off 10

Slime有一个代码行走slime-macroexpand-all命令:http://common-lisp.net/project/slime/doc/html/Macro_002dexpansion.html

这可能没有文档和/或不受支持,但也许您可以从REPL调用它:

CL-USER> (swank-backend:macroexpand-all '(my-recursive-fact 5))
(* 5 (* 4 (* 3 (* 2 (* 1 1)))))
Run Code Online (Sandbox Code Playgroud)


Rai*_*wig 8

MACROEXPAND采取一种形式并扩展它.它会多次执行,直到表单不再是宏形式.

在您的示例中,顶级调用my-recursive-fact是一个宏窗体.前面乘法的结果形式不是宏形式,因为*它不是宏.这是一个功能.表单有一个参数,它是一个宏形式.但MACROEXPAND不看那些.

如果要在所有级别上扩展代码,则需要使用代码walker.有些Lisps可以在IDE中直接访问它,比如Lispworks.

  • 首先,我误解了宏观形式的含义.HypepSpec明确地说它是一个表单,第一个元素是宏名称.其次,事实证明,我选择的实现--SBCL - 拥有它自己的代码漫游器.所以我需要的是sb-walker:步行式设施,它给了我想要的输出:(*5(*4(*3(*2(*1 1))))) (2认同)