如何在不强制实现的情况下找到延迟序列的长度?

时间:2013-08-27 17:09:23

标签: clojure lazy-sequences

我正在阅读O'reilly Clojure编程书,它在其关于延迟序列的部分中说了以下内容:

  

延迟序列可能(尽管非常罕见)知道它的长度,因此在没有实现其内容的情况下将其作为计数结果返回。

我的问题是,这是怎么做的以及它为何如此罕见?

不幸的是,本书未在本节中指定这些内容。我个人认为,在实现之前知道延迟序列的长度是非常有用的,例如,在同一页面中是使用map的函数处理的延迟文件序列的示例。很高兴知道在实现序列之前可以处理多少文件。

2 个答案:

答案 0 :(得分:9)

受到soulcheck's answer的启发,这是一个懒惰但计算的固定大小集合上昂贵功能的地图。

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

答案 1 :(得分:7)

我想这是因为通常还有其他方法可以找出尺寸。

我现在能想到的唯一可能做到这一点的序列实现是某种已知大小集合上昂贵的函数/过程的映射。

一个简单的实现将返回底层集合的大小,同时推迟实现lazy-sequence的元素(因此执行昂贵的部分),直到必要。

在这种情况下,可以预先知道正在映射的集合的大小,并且可以使用它而不是lazy-seq大小。

有时它可能很方便,这就是为什么它不是不可能实现的,但我想很少有必要。