lazy-seq - 外部或内部的缺点

eve*_*_jr 6 clojure

应该在里面(lazy-seq ......)

(def lseq-in (lazy-seq (cons 1 (more-one))))
Run Code Online (Sandbox Code Playgroud)

还是出去?

(def lseq-out (cons 1 (lazy-seq (more-one))))
Run Code Online (Sandbox Code Playgroud)

我注意到

(realized? lseq-in)
        ;;; ? false

(realized? lseq-out)
        ;;; ? <err>
        ;;;   ClassCastException clojure.lang.Cons cannot be cast to clojure.lang.IPending  clojure.core/realized? (core.clj:6773)
Run Code Online (Sandbox Code Playgroud)

clojuredocs.org上的所有示例使用"out".

涉及的权衡是什么?

ama*_*loy 8

你肯定想要(lazy-seq (cons ...))作为你的默认,只有你有明确的理由才会有偏差.clojuredocs.org很好,但这些例子都是社区提供的,我不会称之为"文档".当然,它是如何建立一个后果是例子倾向于是人谁写的只是学会了如何使用结构的问题,希望提供帮助,所以很多人都是穷人.我会转而使用clojure.core中的代码或其他已知良好的代码.

为什么这是默认值?考虑以下两种实现map:

(defn map1 [f coll]
  (when-let [s (seq coll)]
    (cons (f (first s))
          (lazy-seq (map1 f (rest coll))))))

(defn map2 [f coll]
  (lazy-seq
    (when-let [s (seq coll)]
      (cons (f (first s))
            (map2 f (rest coll))))))
Run Code Online (Sandbox Code Playgroud)

如果你打电话(map1 prn xs),那么xs的元素将立即被实现和打印,即使你从未有意识地实现了生成的映射序列的元素.map2另一方面,立即返回一个惰性序列,延迟其所有工作,直到请求一个元素.

  • 我确实认为`line-seq`是"错误的",但是'line-seq`完全懒惰并不是那么重要,因为它不会像`map`那样调用任意函数.我的猜测是`line-seq`太老了,当懒惰的序列工作方式与现在不同时,它的代码被写回来了.您可以在http://dev.clojure.org/jira/browse/CLJ-222上看到line-seq的一些有趣历史. (2认同)

Mic*_*zyk 7

对于consinside lazy-seq,对seq的第一个元素的表达式的求值将被推迟; 与cons在外面,它马上做,只有序列的"休息"部分的建设被推迟.(那(rest lseq-out)将是一个懒惰的seq.)

因此,如果计算第一个元素是昂贵的并且可能根本不需要,那么放入cons内部lazy-seq更有意义.如果将初始元素作为参数提供给惰性seq生成器,则cons在外部使用可能更有意义(这是这种情况clojure.core/iterate).否则它没有那么大的差别.(在开始时创建一个懒惰的seq对象的开销可以忽略不计.)

Clojure本身使用两种方法(尽管在大多数情况下lazy-seq包装了整个seq生成表达式,这可能不一定从一开始cons).