在构建延迟序列时使用flatten时,我不确定我观察到的行为是什么。
查看clojure.core中的源代码我可以看到flatten函数调用filter,因此应该返回一个惰性序列 - 我想。但是下面的代码片段给了我一个stackoverflow错误。在使用对concat的调用替换对flatten的调用的片段中,它可以正常工作
(defn l-f [c]
(if (nil? c) []
(lazy-seq (flatten (cons [[ :h :j] :a :B] (l-f (rest c)))))))
(take 10 (l-f (repeat 2))) is how I invoke it.
这是一个相当人为的例子。我也知道flatten和concat会给我一些嵌套级别不同的序列。
我试图弄清楚为什么flatten似乎打破了懒惰,尽管我对clojure.core中的代码的理解(另外的)有其他建议。
答案 0 :(得分:5)
懒惰只会带你到目前为止 - 懒惰只是意味着序列在创建时并未完全实现,但是从另一个构建一个懒惰序列有时会涉及向前看几个值。在这种情况下,flatten
的实现与您调用它的递归方式不一致。
首先,flatten
函数调用tree-seq
来深度优先遍历集合的内容。反过来,tree-seq
使用提供的序列调用mapcat
,该序列委托给apply
,它实现序列中的前几个项目以确定要调用的函数的arity。实现序列中的前几个项会导致对l-f
的递归调用,该调用会对其余参数调用flatten
,并陷入无限循环。
在这种特殊情况下,不需要递归调用flatten
,因为在第一次调用之后的任何调用都不起作用。因此,可以通过将惰性序列的生成与其展平分开来修复您的函数:
(defn l-f [c]
(letfn [(l-f-seq [x] (if-let [s (seq x)]
(lazy-seq (cons [[:h :j] :a :B] (l-f-seq (rest s))))
[]))]
(flatten (l-f-seq c))))