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

时间:2013-06-09 17:28:05

标签: clojure

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

(def lseq-in (lazy-seq (cons 1 (more-one))))

或者出去?

(def lseq-out (cons 1 (lazy-seq (more-one))))

我注意到了

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

clojuredocs.org上的所有示例都使用“out”。

涉及哪些权衡?

2 个答案:

答案 0 :(得分: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))))))

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

答案 1 :(得分:7)

conslazy-seq,对seq的第一个元素的表达式的求值被推迟;在外部使用cons,它立即完成,只延迟了seq的“rest”部分的构造。 (所以(rest lseq-out)将是一个懒惰的seq。)

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

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