【问题标题】:Is 'for' not actually lazy in clojure?在clojure中'for'实际上不是懒惰的吗?
【发布时间】:2012-05-20 08:22:35
【问题描述】:
(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 个元素(如果可用)。哎哟。

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

无论如何,有没有简单的方法来强制 1-chunking 而不是 32-chunking 的惰性序列?

【问题讨论】:

    标签: clojure list-comprehension lazy-evaluation


    【解决方案1】:

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

    【讨论】:

    • 谢谢!这让我难过了两个小时。我不知道为什么 (take 1 (map some-func [1 2 3 4])) 评估所有 4 个元素的 some-func ......我必须说,从阅读中看,为什么会发生这种情况并不明显“map”和“take”的文档
    【解决方案2】:

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

    采用一个或多个向量 binding-form/collection-expr 对,每个后跟零个或多个 修饰符,并产生一个惰性的 expr 求值序列。

    (强调我的)

    那么what's going on?

    基本上,Clojure 总是严格评估。惰性序列 基本上使用与 python 相同的技巧和它们的生成器等。 穿着懒惰的衣服进行严格的评估。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-17
      • 1970-01-01
      • 2013-04-20
      • 2018-01-28
      • 1970-01-01
      • 2017-01-21
      • 2012-04-09
      • 1970-01-01
      相关资源
      最近更新 更多