15
(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 块而不是 32 块的惰性序列?

4

2 回答 2

13

Michaes Fogus写了一篇关于通过提供自定义 ISeq 实现来禁用此行为的博客条目

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

Clojure 的乐趣中给出了一种更简单的方法:

(defn seq1 [s]
  (lazy-seq
    (when-let [[x] (seq s)]
       (cons x (seq1 (rest s))))))
于 2012-05-11T18:19:59.097 回答
3

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

接受一个或多个 binding-form/collection-expr 对的向量,每个对后跟零个或多个修饰符,并产生 expr 评估的惰性序列。

(强调我的)

那么这是怎么回事

基本上 Clojure 总是严格评估。懒惰的 seqs 基本上使用与 python 相同的技巧和它们的生成器等。懒惰的衣服中的严格评估。

换句话说,for 急切地返回一个惰性序列。在您要求之前不会对其进行评估,并且会被分块。

于 2012-05-16T00:34:39.587 回答