我目前正在阅读 O'reilly Clojure 编程书,它在关于惰性序列的部分中有以下内容:
惰性序列有可能(尽管非常罕见)知道它的长度,因此将它作为 count 的结果返回,而不知道它的内容。
我的问题是,这是如何做到的,为什么它如此罕见?
不幸的是,本书在本节中没有具体说明这些内容。我个人认为在实现之前知道惰性序列的长度非常有用,例如,在同一页面中是使用函数处理的惰性文件序列的示例map
。在实现序列之前知道可以处理多少个文件会很好。
我目前正在阅读 O'reilly Clojure 编程书,它在关于惰性序列的部分中有以下内容:
惰性序列有可能(尽管非常罕见)知道它的长度,因此将它作为 count 的结果返回,而不知道它的内容。
我的问题是,这是如何做到的,为什么它如此罕见?
不幸的是,本书在本节中没有具体说明这些内容。我个人认为在实现之前知道惰性序列的长度非常有用,例如,在同一页面中是使用函数处理的惰性文件序列的示例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]
我想这是因为通常有其他方法可以找出大小。
我现在能想到的唯一可能做到这一点的序列实现,是某种昂贵的函数/过程在已知大小的集合上的映射。
一个简单的实现将返回底层集合的大小,同时将延迟序列的元素的实现(因此执行昂贵的部分)推迟到必要时。
在这种情况下,人们事先知道要映射的集合的大小,并且可以使用它来代替惰性序列大小。
有时它可能很方便,这就是为什么它不是不可能实现的原因,但我想很少有必要。