Clojure压扁和懒惰

时间:2012-09-27 16:54:36

标签: clojure lazy-evaluation flatten

在构建延迟序列时使用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中的代码的理解(另外的)有其他建议。

1 个答案:

答案 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))))