sno*_*ape 9 lisp macros recursion common-lisp
这个宏的递归定义做了它应该做的事情(从1到n的总和):
(defmacro sum-int-seq (n)
`(cond
((equal 0 ,n) 0)
(t (+ ,n (sum-int-seq (- ,n 1))))))
Run Code Online (Sandbox Code Playgroud)
例如(sum-int-seq 5)给出15.
但为什么它有效呢?当宏扩展时,我得到这个:
(macroexpand '(sum-int-seq 5))
(IF (EQUAL 0 5) 0 (+ 5 (SUM-INT-SEQ (- 5 1))))
Run Code Online (Sandbox Code Playgroud)
但是因为sum-int-seq是一个宏,所以宏评估应该变成一个无限循环.编译器是否创建了递归函数?如果这个定义创建了一个递归函数,有没有办法以递归方式定义宏?
(为简洁起见,这是一个愚蠢的例子,一个函数当然会更好地工作)
Rai*_*wig 14
你的例子不起作用.
它可能在翻译中起作用.但是使用编译器,您将在编译期间看到无限循环.
CL-USER 23 > (defun test (foo)
(sum-int-seq 5))
TEST
Run Code Online (Sandbox Code Playgroud)
让我们使用LispWorks解释器:
CL-USER 24 > (test :foo)
15
Run Code Online (Sandbox Code Playgroud)
让我们尝试编译函数:
CL-USER 25 > (compile 'test)
Stack overflow (stack size 15997).
1 (continue) Extend stack by 50%.
2 Extend stack by 300%.
3 (abort) Return to level 0.
4 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
Run Code Online (Sandbox Code Playgroud)
那么,现在接下来的问题是:为什么它在解释器中起作用,但是编译器无法编译它?
好的,我会解释一下.
我们先来看看口译员.
(sum-int-seq 5).(COND ((EQUAL 0 5) 0) (T (+ 5 (SUM-INT-SEQ (- 5 1))))).(+ 5 (SUM-INT-SEQ (- 5 1))).为此,它需要宏扩展(SUM-INT-SEQ (- 5 1)).(cond ((EQUAL 0 (- (- (- (- (- 5 1) 1) 1) 1) 1)) 0) ....然后返回0并且计算可以使用此结果并将其他术语添加到其中.解释器获取代码,评估它的内容,并在必要时进行宏扩展.然后评估生成的代码或宏扩展.等等.
现在让我们看一下编译器.
(COND ((EQUAL 0 5) 0) (T (+ 5 (SUM-INT-SEQ (- 5 1))))).(SUM-INT-SEQ (- 5 1)).请注意,代码永远不会被评估,只会扩展.(SUM-INT-SEQ (- (- 5 1) 1))等等.最后你会看到堆栈溢出.编译器遍历(递归编译/扩展)代码.它可能不会执行代码(除非它进行优化或宏实际上明确地评估它).
对于递归宏,您需要实际倒计时.如果你在宏内部进行评估,那么类似的东西(sum-int-seq 5)可以起作用.但(defun foo (n) (sum-int-seq n))由于编译器不知道n的值是什么,因此这是没有希望的.