【问题标题】:Clojure Lazy Sequences that are Vectors作为向量的 Clojure 惰性序列
【发布时间】:2011-03-06 05:47:16
【问题描述】:

我注意到 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 sn-p: Designing a lazy vector: problem with const

【问题讨论】:

    标签: clojure list vector lazy-sequences


    【解决方案1】:

    是的,Clojure 中的序列被描述为"logical lists",具有三个操作(first、next 和 cons)。

    序列本质上是迭代器的 Clojure 版本(尽管 clojure.org 坚持认为序列不是迭代器,因为它们不保持内部状态),并且只能以线性从前到-结束时尚。

    惰性向量不存在,至少在 Clojure 中不存在。

    如果您希望在一系列索引上进行恒定时间查找,而不计算不需要的中间元素,您可以使用动态计算结果的函数。结合 memoization(或将结果缓存在您自己的 arg-to-result 哈希中),您将获得与我假设您希望从惰性向量中获得的几乎相同的效果。

    这显然只适用于计算 f(n) 的算法比通过所有前面的 f(0)...f(n-1) 更直接的算法。如果没有这样的算法,当每个元素的结果都依赖于前一个元素的结果时,无论如何你都不能比序列迭代器做得更好。

    编辑

    顺便说一句,如果您只希望结果是一个向量,以便之后快速查找,并且您不介意第一次按顺序创建元素,那很简单。

    这是一个使用向量的斐波那契实现:

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

    向量根据需要增长,我们只添加直到最后一个请求的元素,一旦计算出来,它就是一个常数时间查找。

    你有这样的想法吗?

    【讨论】:

    • 感谢您的精彩回复!是的,您的斐波那契示例更类似于我正在寻找的东西:惰性创建向量。
    • 您也可以在懒惰的fib 序列上使用nth :) (nth fib 10)
    猜你喜欢
    • 2011-06-26
    • 2010-12-08
    • 2014-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-12
    相关资源
    最近更新 更多