作为向量的Clojure Lazy序列

时间:2010-06-21 09:42:29

标签: clojure list vector lazy-sequences

我注意到Clojure中的延迟序列似乎在内部表示为链表(或者至少它们被视为仅具有对元素的顺序访问的序列)。即使被缓存到内存中,使用nth的lazy-seq上的访问时间也是O(n),而不是像向量那样的常量时间。

;; ...created my-lazy-seq here and used the first 50,000 items

(time (nth my-lazy-seq 10000))
"Elapsed time: 1.081325 msecs"

(time (nth my-lazy-seq 20000))
"Elapsed time: 2.554563 msecs"

如何在Clojure中以增量方式获取常量查找或创建延迟向量?

想象一下,在生成惰性向量期间,每个元素都是之前所有元素的函数,因此遍历列表所花费的时间成为一个重要因素。

相关问题仅发现了这个不完整的Java代码段: Designing a lazy vector: problem with const

1 个答案:

答案 0 :(得分:19)

是的,Clojure中的序列被描述为"logical lists",有三个操作(第一个,下一个和缺点)。

序列本质上是迭代器的Clojure版本(虽然clojure.org坚持认为序列不是迭代器,因为它们不具有iternal状态),并且只能通过线性前端到后台集合中移动。结束时尚。

懒惰的载体不存在,至少在Clojure中不存在。

如果希望在一系列索引上进行恒定时间查找,而不计算不需要的中间元素,则可以使用一个动态计算结果的函数。结合memoization(或者在你自己的arg-to-result哈希中缓存结果),你得到的结果和我想要的懒惰向量几乎相同。

这显然只有当算法能够比通过前面的所有f(0)... f(n-1)更直接地计算f(n)时才有效。如果没有这样的算法,当每个元素的结果取决于每个前一个元素的结果时,在任何情况下都不能比序列迭代器做得更好。

修改

顺便说一句,如果您想要的结果是一个向量,那么之后您就可以快速查找,而且您不介意第一次按顺序创建元素,这很简单。

这是使用向量的Fibonacci实现:

(defn vector-fib [v]
  (let [a (v (- (count v) 2)) ; next-to-last element
        b (peek v)]   ; last element
    (conj v (+ a b))))

(def fib (iterate vector-fib [1 1]))

(first (drop 10 fib))
  => [1 1 2 3 5 8 13 21 34 55 89 144]

这里我们使用一个延迟序列来推迟函数调用,直到被要求(iterate返回一个惰性序列),但结果被收集并在向量中返回。

矢量根据需要增长,我们只添加要求的最后一个元素,并且一旦计算它就是一个恒定的时间查找。

你有这样的想法吗?