8

应该在里面(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”。

涉及哪些权衡?

4

2 回答 2

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另一方面,它立即返回一个惰性序列,延迟它的所有工作,直到请求一个元素。

于 2013-06-09T20:14:49.827 回答
7

使用consinside lazy-seq,对 seq 的第一个元素的表达式的评估被推迟;在cons外部,它立即完成,只有 seq 的“其余”部分的构建被推迟。(所以(rest lseq-out)将是一个惰性序列。)

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

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

于 2013-06-09T20:14:20.700 回答