2

不确定在构建惰性序列时使用 flatten 时观察到的行为是什么。

查看 clojure.core 中的源代码,我可以看到 flatten 函数调用了过滤器,因此应该返回一个惰性序列 - 我认为。然而下面的代码片段给了我一个 stackoverflow 错误。在对 flatten 的调用替换为对 concat 的调用的代码段中,它工作得很好

(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 中代码的(有限)理解表明并非如此。

4

1 回答 1

5

惰性只带你到这么远——惰性只是意味着序列在创建时还没有完全实现,但是从另一个构建一个惰性序列有时涉及向前看一些值。在这种情况下, 的实现flatten不能很好地与您调用它的递归方式配合使用。

首先,该flatten函数调用tree-seq对集合内容进行深度优先遍历。反过来,使用提供的序列进行tree-seq调用mapcat,该序列委托给apply,它实现序列中的前几项以确定要调用的函数的数量。实现序列中的前几项会导致对 的递归调用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))))
于 2012-09-27T18:21:42.727 回答