(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的延迟序列?
答案 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评估序列。
(强调我的)
基本上Clojure总是严格评估。懒惰的seqs 基本上使用与python相同的技巧及其生成器等。 懒惰衣服严重侵犯。
换句话说,for
急切会返回 lazy 序列。在你提出要求之前不会对其进行评估,并且会被分块。