Clojure的惰性序列是向量

12

我注意到在Clojure中,惰性序列似乎是以链表的形式表示的(或者至少被视为只能按顺序访问元素的序列)。即使被缓存到内存中,通过nth访问惰性序列的时间复杂度仍然是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个回答

21

是的,在Clojure中,序列被描述为具有三个操作(first、next和cons)的“逻辑列表”

序列本质上是Clojure版本的迭代器(尽管clojure.org坚称序列不是迭代器,因为它们不保存内部状态),只能以线性前向-后向方式移动到支持集合。

在Clojure中,懒惰的向量不存在。

如果您想要在索引范围内进行常数时间查找,并且不计算不需要的中间元素,则可以使用实时计算结果的函数。结合备忘录(或自己将结果缓存到arg-to-result哈希表中),您几乎可以获得与您从惰性向量中所期望的相同效果。

当存在比遍历所有先前的f(0)…f(n-1)更直接地计算f(n)的算法时,这显然只适用于。如果没有这样的算法,即每个元素的结果都取决于每个先前元素的结果,则在任何情况下都无法比序列迭代器更好。

编辑

顺便说一下,如果您只想让结果成为向量,以便随后快速查找,并且您不介意元素是按顺序创建的,那很简单。下面是使用向量实现的斐波那契:

(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返回一个惰性序列),但结果会被收集并以向量形式返回。

向量根据需要增长,只添加到最后一个被要求的元素,并且一旦计算完成,它是一个恒定时间查找。

你是否有类似这样的想法?


感谢您的回复!是的,您的斐波那契示例更接近我所需的:惰性创建向量。 - ivar
你也可以在惰性的 fib 序列上使用 nth :) (nth fib 10) - NikoNyrh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接