iza*_*ban 8 clojure lazy-sequences
我正在阅读O'reilly Clojure编程书,它在其关于延迟序列的部分中说了以下内容:
延迟序列可以(尽管非常罕见)知道它的长度,因此在没有实现其内容的情况下将其作为计数结果返回.
我的问题是,这是如何完成的以及它为何如此罕见?
遗憾的是,本书未在本节中指定这些内容.我个人认为,在实现之前知道延迟序列的长度是非常有用的,例如,在同一页面中是使用函数处理的延迟文件序列的示例map
.很高兴知道在实现序列之前可以处理多少文件.
灵魂调查的答案灵感来自这里,这是一个懒惰但计算在固定大小的集合上的昂贵功能的地图.
(defn foo [s f]
(let [c (count s), res (map f s)]
(reify
clojure.lang.ISeq
(seq [_] res)
clojure.lang.Counted
(count [_] c)
clojure.lang.IPending
(isRealized [_] (realized? res)))))
(def bar (foo (range 5) (fn [x] (Thread/sleep 1000) (inc x))))
(time (count bar))
;=> "Elapsed time: 0.016848 msecs"
; 5
(realized? bar)
;=> false
(time (into [] bar))
;=> "Elapsed time: 4996.398302 msecs"
; [1 2 3 4 5]
(realized? bar)
;=> true
(time (into [] bar))
;=> "Elapsed time: 0.042735 msecs"
; [1 2 3 4 5]
Run Code Online (Sandbox Code Playgroud)
我想这是因为通常还有其他方法可以找出尺寸.
我现在能想到的唯一可能做到这一点的序列实现是某种已知大小集合上昂贵的函数/过程的映射.
一个简单的实现将返回底层集合的大小,同时推迟实现惰性序列的元素(因此执行昂贵的部分)直到必要.
在这种情况下,可以预先知道正在映射的集合的大小,并且可以使用它而不是lazy-seq大小.
它有时可能很方便,这就是为什么它不是不可能实现的,但我想很少有必要.