2013-08-27 28 views
7

我目前正在读O'Reilly的Clojure的编程的书,它是说,在它下面是关于懒惰的序列部分:如何在不强制实现的情况下找到懒序列的长度?

这是可能的(虽然很罕见)于慵懒的序列以了解它的长度,因此在没有实现其内容的情况下归还它。

我的问题是,这是如何完成的,为什么这么罕见?

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

回答

7

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

我现在可以想到的唯一序列实现可能会做到这一点,是一种昂贵的函数/过程的地图,而不是一个已知大小的集合。

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

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

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

+1

这显然不是唯一可能的例子。那么'(重复1000次)'? '(范围100)'?有很多类似的*可以*知道它们的长度的懒惰序列,但是没有编程,因为它会花费工程时间,并且可能会带来运行时间上的开销,从而导致可能的小优势。 – amalloy

+0

@amalloy这就是为什么我说“我能想到”,也许我应该在最后添加“现在”:)仍然剩下的答案成立 - 创建它之前已知的懒惰序列的长度,所以没有在巧妙地实现序列本身的长度方面获得真正的收益。 – soulcheck

+0

这是否意味着不可能在没有实现的情况下计算一个由“过滤器”产生的惰性序列?说一个几百万个随机数的懒序列:'( - >> my-rand-int-lazy-seq(filter#(even?%))(count-without-realization))'? – adamneilson

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] 
+0

+1为示例实现 – soulcheck

相关问题