Recursion 递归是一种气味(在习惯用语Clojure中)是因为拉链和HOFs吗?
这本经典的书()是建立在两大思想之上的Recursion 递归是一种气味(在习惯用语Clojure中)是因为拉链和HOFs吗?,recursion,clojure,higher-order-functions,idioms,zipper,Recursion,Clojure,Higher Order Functions,Idioms,Zipper,这本经典的书()是建立在两大思想之上的 您可以用递归的方式(而不是使用循环)解决大多数问题(假设您有尾部调用优化) Lisp很好,因为它本身很容易实现 现在有人可能认为这适用于所有Lispy语言(包括Clojure)。问题是,这本书是它的时代(1989年)的产物,很可能是在(霍夫斯)成为我们今天拥有的东西之前(或者至少被认为是大学生们喜欢的) 递归的好处(至少部分)是易于遍历嵌套数据结构,如('a'b('c('d'e))) 用于: 现在使用-我们有了一种非递归的方法来遍历嵌套数据结构,并且可以像
('a'b('c('d'e)))
用于:
现在使用-我们有了一种非递归的方法来遍历嵌套数据结构,并且可以像遍历任何惰性数据结构一样遍历它们。用于:
现在,您似乎可以通过以下任一方法解决任何嵌套列表遍历问题:
- 如上所述的
,或拉链
- 一个
,它遍历结构并返回一组键,允许您使用拉链
修改结构李>assoc
- 当然,我假设数据结构的大小是固定的,并且在遍历之前是完全已知的
- 我排除了流式数据源场景李>
我的问题是:递归(在惯用的Clojure中)是一种气味吗?因为Zipper和HOFs?Clojure惯用用法不鼓励显式递归,因为调用堆栈是有限的:通常是10K深。修改Halloway&Bedra的Clojure函数式编程六条规则中的第一条((第89页)) 避免无界递归。JVM无法优化递归调用和 无边界递归的Clojure程序将破坏其堆栈 有两种缓解措施:
处理尾部递归李>recur
- 惰性序列可以将深层调用堆栈转换为浅层调用堆栈
跨正在展开的数据结构。序列中的许多HOF
库(如
和map
)执行此操作filter
因为高阶函数已经为您实现了常见的递归模式,所以手动递归并不常见。然而,这肯定不是你永远不应该使用手动递归的情况:它只是一个警告信号,表明你可能可以做其他事情。我甚至记不起在我使用Clojure的四年中有一种情况,我实际上需要一个拉链,但我最终相当经常地使用递归。这可能是一种气味,但不是一种对手头的特定问题不愉快的气味。例如,处理非结构化(或模棱两可)输入以产生结构化和确定性的内容。您能给出一个答案并提供一个示例吗?这听起来像是诺维格的人工智能编程范例中的一些东西当然-我认为它背后的问题是“是否存在必须求助于递归的场景”?hawkeye,另一个值得问的问题:是否存在递归更容易理解的场景?(我发现递归定义比zipper函数更容易理解,但这就是我。其他人可能会发现zipper版本更清晰,这很好。)Clojure不支持全面的尾部调用优化这一事实在很大程度上是错误的。但请注意,在遍历树结构时,TCO不一定有帮助。@hawkeye我认为您可以通过1)在Clojure中将函数转换为和2)在Clojure中使用
trampoline
来展平相互的尾部调用,从而用闭包代替递归。这个过程将递归从堆栈移动到堆。@hawkeye在我之前的评论中有一个到维基百科关于继续传递样式的文章的链接。代码在计划中。我希望你能接受。关于trampoline
有很多很好的报道-我猜你知道trampoline
@Thumbnail-我在Clojure中寻找一个例子。
(def leftmost
(fn [l]
(println "(leftmost " l)
(println (non-atom? l))
(cond
(null? l) '()
(non-atom? (first l)) (leftmost (first l))
true (first l))))
(defn map-zipper [m]
(zip/zipper
(fn [x] (or (map? x) (map? (nth x 1))))
(fn [x] (seq (if (map? x) x (nth x 1))))
(fn [x children]
(if (map? x)
(into {} children)
(assoc x 1 (into {} children))))
m))
(def m {:a 3 :b {:x true :y false} :c 4})
(-> (map-zipper m) zip/down zip/right zip/node)
;;=> [:b {:y false, :x true}]