lazy-seq — cons outside or in
缺点应该在里面(lazy-seq ...)
1
| (def lseq-in (lazy-seq (cons 1 (more-one)))) |
还是出去?
1
| (def lseq-out (cons 1 (lazy-seq (more-one)))) |
我注意到
1 2 3 4 5 6
| (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"。
需要进行哪些权衡?
您绝对希望(lazy-seq (cons ...))作为默认值,仅当您有明确的理由时才偏离它。 clojuredocs.org很好,但是示例都是社区提供的,因此我不会将其称为"文档"。当然,其构建方式的结果是,这些示例往往是由刚学会如何使用所讨论的构造并希望提供帮助的人编写的,因此其中许多人都是穷人。我将改为引用clojure.core中的代码或其他已知良好的代码。
为什么要使用默认值?考虑map的以下两种实现:
1 2 3 4 5 6 7 8 9 10
| (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立即返回一个惰性序列,将其所有工作延迟到请求一个元素为止。
-
line-seq在外面有cons,已经引起了一些混乱stackoverflow.com/questions/15182702/
-
实际上,当没有人要求line-seq占用第一行不是bug吗?
-
我确实认为line-seq是"错误的",但是它不太重要,因为line-seq完全懒惰,因为它没有像map那样调用任意函数。我的猜测是line-seq太旧了,以至于当惰性序列的工作方式与现在不同时,它的代码会被写回。您可以在dev.clojure.org/jira/browse/CLJ-222中看到一些有趣的line-seq历史。
在lazy-seq中使用cons时,对seq第一个元素的表达式的求值被延迟;如果在外部使用cons,则立即完成,仅推迟seq的"其余"部分的构建。 (因此(rest lseq-out)将是一个懒惰序列。)
因此,如果计算第一个元素很昂贵并且可能根本不需要,则将cons放在lazy-seq内更有意义。如果将初始元素作为参数提供给惰性seq生产者,则在外部使用cons可能更有意义(clojure.core/iterate就是这种情况)。否则,它没有太大的区别。 (在开始时创建惰性seq对象的开销可以忽略不计。)
Clojure本身使用两种方法(尽管在大多数情况下,lazy-seq会包装整个seq生成表达式,但不一定以cons开头)。
-
关于何时将初始元素作为参数提供的要点;那是我第一次听说。
-
嗯我不确定初始元素提供的参数。不论是否在lazy-seq内,计算值的显示速度几乎都相同吗?实际上,iterate的文档说它将返回一个lazy-seq,但是当我在其上调用realized?时会引发异常。这实际上不是一个错误吗?
-
除非必要,否则您希望iterate不计算(f x)。因此,需要将x的锥体与f的调用通过lazy-seq层分开。因此,您可以将其定义为(cons x (lazy-seq ...))或(lazy-seq (cons x (lazy-seq ...)));但是在第二种情况下,您最终得到的lazy-seq层数是原来的两倍。至于realized?,那是一个好点-也许文档字符串可以使用改进。 (或者您可以说realized?应该只为非惰性序列返回true,尽管我的第一个直觉是那不是正确的方法……嗯。)
-
我仍然不明白第二种情况。就我所知,这可能是一个根本性的缺口,但是我正在玩(defn iterate2 [f x] (lazy-seq (cons x (do (println"... iterate called") (iterate2 f (f x)))))),而当我在懒惰序列上调用first时未调用(f x)。
-
我刚刚将您的定义粘贴到1.5.1 REPL中,并输入了(first (iterate2 inc 0))。结果,我得到了一个" ...称为迭代"的打印输出,并且返回值0符合预期。相反,(first (iterate #(do (println"...") (inc %)) 0))返回0而不打印任何内容。
-
我现在看到了。谢谢。