You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何让Lisp for循环宏返回体中最后表达式的值?

让你的for宏返回循环体最后一次的结果

你的问题很典型——递归实现的循环宏默认返回递归终止时的nil,因为最后一次调用递归函数时不满足when条件,直接返回nil。要让它返回循环体最后一个表达式的最后一次执行的值,我们需要在递归过程中跟踪这个值,直到循环结束时返回它。

修改思路

核心逻辑很简单:

  1. 每次执行循环体时,保存其最后一个表达式的结果;
  2. 如果还有下一次循环,就递归并返回下一次循环的结果;
  3. 如果是最后一次循环,直接返回当前保存的结果。

修改后的宏代码

(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))

循环会依次计算246,最终宏会返回6,而不是原来的nil

内容的提问来源于stack exchange,提问作者syd

火山引擎 最新活动