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
able与此无关recur
或
宏的第一个参数之外的每个参数都会生成嵌套的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
调用将无法编译