如何让Lisp for循环宏返回体中最后表达式的值?
让你的
for宏返回循环体最后一次的结果 你的问题很典型——递归实现的循环宏默认返回递归终止时的nil,因为最后一次调用递归函数时不满足when条件,直接返回nil。要让它返回循环体最后一个表达式的最后一次执行的值,我们需要在递归过程中跟踪这个值,直到循环结束时返回它。
修改思路
核心逻辑很简单:
- 每次执行循环体时,保存其最后一个表达式的结果;
- 如果还有下一次循环,就递归并返回下一次循环的结果;
- 如果是最后一次循环,直接返回当前保存的结果。
修改后的宏代码
(defmacro for ((parameter start-value end-value &optional (step 1)) &body e) (let ((func-name (gensym)) (step-name (gensym)) (end (gensym)) (next-param (gensym))) `(labels ((,func-name (,parameter ,end ,step-name) (when (<= ,parameter ,end) ;; 保存循环体最后一个表达式的执行结果 (let ((result (progn ,@e))) (setf ,next-param (+ ,parameter ,step-name)) ;; 判断是否还有下一次循环 (if (<= ,next-param ,end) ;; 还有循环:递归并返回下一次的结果 (,func-name ,next-param ,end ,step-name) ;; 最后一次循环:返回当前结果 result))))) (,func-name ,start-value ,end-value ,step))))
为什么这样有效?
(progn ,@e)会执行循环体的所有表达式,并自动返回最后一个表达式的值,我们把这个值存在result变量里;- 每次递归时,如果下一个参数仍满足循环条件,就继续递归,此时当前的
result会被下一次递归的结果覆盖; - 当最后一次执行循环体后,下一个参数超出了结束值,我们直接返回当前的
result,这就是整个宏的最终返回值。
测试示例
比如执行这段代码:
(for (i 1 3) (* i 2))
循环会依次计算2、4、6,最终宏会返回6,而不是原来的nil。
内容的提问来源于stack exchange,提问作者syd




