Clojure-宏观扩张如何在;“一些”;功能

Clojure-宏观扩张如何在;“一些”;功能,clojure,macros,lisp,Clojure,Macros,Lisp,就在我认为自己对宏有很好的掌握时,我偶然发现了一些的源代码,乍看起来有点奇怪 (defn some [pred coll] (when (seq coll) (or (pred (first coll)) (recur pred (next coll))))) 我的第一个直觉是,这看起来像是在消耗堆栈,但后来我想起了:“不,笨蛋,或是一个宏,因此它将简单地扩展成大量嵌套的ifs” 然而,再仔细考虑一下,我最终认为自己陷入了困境。在展开时,函数源如下所示: (defn s

就在我认为自己对宏有很好的掌握时,我偶然发现了一些的源代码,乍看起来有点奇怪

(defn some
  [pred coll]
    (when (seq coll)
      (or (pred (first coll)) (recur pred (next coll)))))
我的第一个直觉是,这看起来像是在消耗堆栈,但后来我想起了:“不,笨蛋,
是一个宏,因此它将简单地扩展成大量嵌套的
ifs

然而,再仔细考虑一下,我最终认为自己陷入了困境。在展开时,函数源如下所示:

(defn some
  [pred coll]
    (when (seq coll)
      (let [or__4469__auto__  (pred (first coll))]
         (if or__4469__auto__ 
             or__4469__auto__ 
             (recur pred (next coll))))))
现在让我困惑的是最后的
recur
call。我一直认为宏扩展发生在运行时之前,但在这里,您必须在运行时调用已经扩展的代码,以获得第二个宏扩展。。。。等一下,我想我已经明白了

没有第二个宏扩展,没有嵌套的
if
块,只有一个
if
块。对
recur
的调用只会继续重新绑定
pred
coll
,但上面的同一个块会继续测试真理,直到找到它,或者集合用完并返回
nil


有人能确认这是否是正确的解释吗?我最初认为宏扩展和运行时是交错的,在运行时调用recur会导致新的宏调用,这是没有意义的,因为宏扩展必须在运行时之前发生。现在我想我明白了我的困惑所在,只有一个宏扩展,结果代码在循环中反复使用

首先,请注意,任何函数都可以用作隐式
循环
表达式。另外,
recur
的工作原理与递归函数调用类似,只是它不会因为编译器的技巧而耗尽堆栈(这就是为什么
loop
&
recur
是“特殊形式”-它们不遵循正常函数的规则)

另外,请记住,
when
是一个宏,可扩展为
if
表达式


说了这么多,您确实得出了正确的结论。

这里有两种递归模式:

  • 宏是隐式递归的,由参数序列引起 生成一个
    if
    表单树
  • some
    函数是显式递归的,被激发为告诉单个 最后一个论点的顺序。这个递归是
    recur
    able与此无关
宏的第一个参数之外的每个参数都会生成嵌套的
if
表单。例如

=> (clojure.walk/macroexpand-all '(or a b c)) 
(let* [or__5501__auto__ a]
  (if or__5501__auto__ or__5501__auto__
    (let* [or__5501__auto__ b]
      (if or__5501__auto__ or__5501__auto__ c))))
有两个参数,因此如果表单有一个
。正如所指出的,当
打开时,周围的
会变成另一个
if
形式

您可以有任意多个嵌套的
if
表单,如果
if
树的叶子都位于尾部位置。因此,所有立即递归调用都是可重复的。如果没有这样的尾部递归,
recur
调用将无法编译