我的Clojure函数在使用列表或向量时是否很慢?

4

我正在学习Clojure,我刚写了这个函数:

(defn replace-last [coll value]
    (assoc coll (dec (count coll)) value))

我有点担心在列表和/或向量方面速度可能会非常慢 - 我还不太理解它们。

谢谢!

2个回答

11

对于向量,这将非常快 - 向量的承诺之一是快速创建略微修改的副本。

对于列表,这根本行不通 - 它们不是可关联的(clojure.lang.Associative),因此不是assoc的有效参数。至于其他可能的方法,如果你需要获取一个实际的列表(而不是可序列化的seq),那么你就没有办法了 - 访问/替换列表的最后一个元素基本上是一个线性时间操作。然而,如果你可以接受一个类似于列表但最后一个元素不同的惰性序列,你可以实现一个半惰性的butlast变体(clojure.core中的不惰性)并将其与lazy-cat一起使用:

(defn semi-lazy-butlast [s]
  (cond (empty? s) s
        (empty? (next s)) nil ; or perhaps ()
        :else (lazy-seq (cons (first s)
                              (semi-lazy-butlast (next s))))))

(defn replace-last [s x]
  (if (empty? s) ; do nothing if there is no last element to replace
    s
    (lazy-cat (semi-lazy-butlast s) [x])))
这将推迟替换最后一个元素,直到你在使用序列时接近它。
一个示例交互:
user=> (take 10 (replace-last (range) :foo))
(0 1 2 3 4 5 6 7 8 9)
user=> (take 10 (replace-last (range 10) :foo))
(0 1 2 3 4 5 6 7 8 :foo)
user=> (replace-last () :foo)
()

2
我认为butlast应该被视为已弃用。 clojure.core还包含drop-last,除了惰性求值外,它还有一个优点,即能够从序列中删除任意数量的元素,而不仅仅是一个。 - Daniel Janus
@DanielJanus:哇,我完全错过了 drop-last。谢谢! - Michał Marczyk

3
这对于向量来说将会像其他任何东西一样快速,但对于列表则完全不起作用。

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