Recursion Clojure中的递归循环通过宏抛出错误

Recursion Clojure中的递归循环通过宏抛出错误,recursion,clojure,macros,Recursion,Clojure,Macros,我一直在尝试用clojure编写一个递归循环,它将打印出列表中的最后一个数字。重点不是我需要得到最后一个数字(我确信有一个内置的函数用于此),而是我想更好地理解clojure中的递归和宏。所以我有这个宏 (defmacro loop-do [the-list] `(if (= (count '~the-list) 1) (println (first '~the-list)) (loop-do (rest '~the-list)))) 但是我得到了一个堆栈溢出错

我一直在尝试用clojure编写一个递归循环,它将打印出列表中的最后一个数字。重点不是我需要得到最后一个数字(我确信有一个内置的函数用于此),而是我想更好地理解clojure中的递归和宏。所以我有这个宏

(defmacro loop-do [the-list]
  `(if (= (count '~the-list) 1)
       (println (first '~the-list))
       (loop-do (rest '~the-list))))
但是我得到了一个堆栈溢出错误。我做错了什么?

人们将如何使用你的宏? 在某个地方,有人会打电话:

(loop-do list)
作为一段代码,这些只是列表中的两个符号。第一个被识别为您的宏,第二个,
list
,是表示将在运行时绑定的变量的符号。但您的宏只知道这是一个符号

这同样适用于:

(loop-do (compute-something))
参数是一个表单,但您不希望获取该表单的最后一个元素,而只获取在计算代码后获得的列表的最后一个元素

因此:您只知道在宏中,
列表将绑定到一个表达式,在运行时,该表达式必须是一个列表。您不能将列表
当作一个列表来使用:无论是
(count'list)
还是
(count'(compute something))
都不能满足您的需要

您可以扩展到
(计数列表)
(计数(计算某物))
,但结果只能在运行时计算。宏的工作只是生成代码

递归宏 宏不是递归的:它们扩展为递归调用

可能会扩展为:

(let [a0 a] (if a0 a0 (and b c)))
宏扩展过程是一个应该终止的固定点,但宏不会调用自身(这意味着什么,在定义宏时是否会扩展代码?)。“展开为递归调用”中的“递归”宏应该有一个基本情况,即它不会展开为自身(独立于运行时会发生什么或不会发生什么)

。。。将替换为:

(loop-do (rest 'x))
。。。这将再次扩大。 这就是为什么评论说大小实际上在增长,这就是为什么会出现stackoverflow错误:macroexpansion从未找到固定点

调试宏 您有一个堆栈溢出错误。如何调试它

使用,它只执行一次宏扩展:

(macroexpand-1 '(loop-do x))
=> (if (clojure.core/= (clojure.core/count (quote x)) 1)
       (clojure.core/println (clojure.core/first (quote x)))
       (user/loop-do (clojure.core/rest (quote x))))

您可以看到生成的代码仍然包含对
usr/loop do
的调用,但参数是
(clojure.core/rest(quote x))
。这就是您应该寻找的症状。

看看循环/重现函数。在这一行:
(循环执行(rest'~列表))
您将以下列表按字面意思传递给宏:
(rest…
,这样您就不会在每一步上减小数据大小。相反,您放大了列表),但rest如何放大列表?我想,如果我传递
(123445)
,它只会返回除第一个元素之外的所有元素,并将其传递到
循环do
?我确信这有一个内置函数:是的,它会放大一个leetwinski,因为当您以
(循环do(rest'~列表))的方式调用宏时,您正在将未计算符号的列表(
(rest'~列表)
)传递回宏<代码>(rest'~列表)
在被赋予宏之前不会被计算。如果您想实践宏,您应该尝试执行实际需要宏的任务。试着重写核心宏。啊,我明白了!这是非常清楚的!谢谢你简洁而详细的解释!:)
(loop-do (rest 'x))
(macroexpand-1 '(loop-do x))
=> (if (clojure.core/= (clojure.core/count (quote x)) 1)
       (clojure.core/println (clojure.core/first (quote x)))
       (user/loop-do (clojure.core/rest (quote x))))