【发布时间】:2019-03-31 13:34:31
【问题描述】:
为了更好地学习宏,我一直在玩一些简单的例子,包括重新创建一个简化的后线程。我很难理解为什么下面的一个版本会导致堆栈溢出而另一个不会。
;; version one - blows up w/ stack overflow
(defmacro ->>> [e1 & exprs]
`(if ~exprs
(->>> ~(concat (first exprs) (list e1)) ~@(rest exprs))
~e1))
;; version two, works just fine
(defmacro ->>> [e1 & exprs]
(if exprs
`(->>> ~(concat (first exprs) (list e1)) ~@(rest exprs))
e1))
我最初的反应是,这一定是因为在第一个示例中,虽然生成的扩展看起来如果它是普通代码就可以运行得很好,因为它是一个宏,递归调用不断扩展并且如果测试永远不会发生。在第二个版本中,if 测试发生在返回任何列表以进行运行时评估之前,从而有机会突破。
但是,我不确定这种心智模型是否正确,因为以下示例 (Clojure Brave & True) 看起来与上面的第一个版本非常相似,并且工作正常:
(defmacro our-and
([] true)
([x] x)
([x & next]
`(if ~x (our-and ~@next) ~x)))
编辑:澄清一下,我的意思是上面的our-and 在结构上(不是语义上)相似,因为它返回一个包含对宏的递归调用的列表,类似于我的版本一上面的线程最后一个副本。
【问题讨论】: