“for”在clojure中实际上是不是很懒惰?

时间:2012-05-11 18:05:48

标签: clojure list-comprehension lazy-evaluation

(take 2 (for [x (range 10)
              :let [_ (println x)]
              :when (even? x)] x))
>> (* 0
* 1
* 2
* 3
* 4
* 5
* 6
* 7
* 8
* 9
0 2)

我以为我只是非常密集。但不是,事实证明Clojure实际上评估了任何延迟序列的前32个元素(如果可用)。哎哟。

我在for中有一个:let递归调用。我很好奇为什么计算似乎是先进行广泛而不是深度优先。似乎计算(尽管,公平,不是内存)正在爆炸,因为我一直在递归树的所有上部分支。尽管代码的逻辑意图是深度优先的,但Clojure的32分块正在强制进行广度优先评估。

无论如何,是否有任何简单的方法可以强制进行1-chunking而不是32-launking的延迟序列?

2 个答案:

答案 0 :(得分:13)

Michaes Fogus has written a blog entry on disabling this behavior by providing a custom ISeq implementation

the modified version by Colin Jones无耻地窃取:

(defn seq1 [#^clojure.lang.ISeq s]
  (reify clojure.lang.ISeq
    (first [_] (.first s))
    (more [_] (seq1 (.more s)))
    (next [_] (let [sn (.next s)] (and sn (seq1 sn))))
    (seq [_] (let [ss (.seq s)] (and ss (seq1 ss))))
    (count [_] (.count s))
    (cons [_ o] (.cons s o))
    (empty [_] (.empty s))
    (equiv [_ o] (.equiv s o))))

给出了一种更简单的方法in The Joy of Clojure

(defn seq1 [s]
  (lazy-seq
    (when-let [[x] (seq s)]
       (cons x (seq1 (rest s))))))

答案 1 :(得分:3)

要回答标题中的问题,不,for并非懒惰。 However,它:

  

采用一个或多个向量   binding-form / collection-expr对,每个对后跟零或更多   修饰符,产生一个懒惰的expr评估序列。

(强调我的)

所以what's going on

  

基本上Clojure总是严格评估。懒惰的seqs   基本上使用与python相同的技巧及其生成器等。   懒惰衣服严重侵犯。

换句话说,for 急切会返回 lazy 序列。在你提出要求之前不会对其进行评估,并且会被分块。