关联或更新 Clojure 列表和惰性序列

mru*_*cci 3 list clojure lazy-sequences

如果我有一个向量(def v [1 2 3]),我可以用 替换第一个元素(assoc v 0 666),获得[666 2 3]

但是,如果我在对向量进行映射后尝试做同样的事情:

(def v (map inc [1 2 3]))
(assoc v 0 666)
Run Code Online (Sandbox Code Playgroud)

抛出以下异常:

ClassCastException clojure.lang.LazySeq cannot be cast to clojure.lang.Associative
Run Code Online (Sandbox Code Playgroud)

编辑或更新惰性序列的单个元素的最惯用的方法是什么?

我应该map-indexed只使用和更改索引 0 还是将惰性序列实现为向量,然后通过关联/更新对其进行编辑?第一个具有保持懒惰的优点,而第二个效率较低但可能更明显。

我想对于第一个元素,我也可以使用 drop 和 cons。还有其他方法吗?我无法在任何地方找到任何示例。

Tay*_*ood 5

编辑或更新惰性序列的单个元素的最惯用的方法是什么?

没有用于修改序列/列表的单个元素的内置函数,但map-indexed可能是最接近的。这不是列表的有效操作。假设您不需要懒惰,我会将序列倒入一个向量中,这就是mapvie 的作用(into [] (map f coll))。根据您使用修改后的序列的方式,对其进行矢量化和修改可能同样高效。

你可以写一个函数map-indexed来做一些类似和懒惰的事情:

(defn assoc-seq [s i v]
  (map-indexed (fn [j x] (if (= i j) v x)) s))
Run Code Online (Sandbox Code Playgroud)

或者,如果您想在不进行矢量化的情况下一次性完成这项工作,您也可以使用转换器:

(sequence
  (comp
    (map inc)
    (map-indexed (fn [j x] (if (= 0 j) 666 x))))
  [1 2 3])
Run Code Online (Sandbox Code Playgroud)

意识到您的用例是只修改惰性序列中的第一项,然后您可以在保持惰性的同时做一些更简单的事情:

(concat [666] (rest s))
Run Code Online (Sandbox Code Playgroud)

更新:关于优化的评论:assoc-at在更新 1,000,000 个元素懒惰序列中的第 500,000 个元素时,leetwinski 的函数快了约 8 毫秒,因此,如果您希望从固有的低效操作中挤出每一点性能,则应该使用他的回答:

(def big-lazy (range 1e6))

(crit/bench
  (last (assoc-at big-lazy 500000 666)))
Evaluation count : 1080 in 60 samples of 18 calls.
            Execution time mean : 51.567317 ms
    Execution time std-deviation : 4.947684 ms
  Execution time lower quantile : 47.038877 ms ( 2.5%)
  Execution time upper quantile : 65.604790 ms (97.5%)
                  Overhead used : 1.662189 ns

Found 6 outliers in 60 samples (10.0000 %)
  low-severe     4 (6.6667 %)
  low-mild   2 (3.3333 %)
Variance from outliers : 68.6139 % Variance is severely inflated by outliers
=> nil

(crit/bench
  (last (assoc-seq big-lazy 500000 666)))
Evaluation count : 1140 in 60 samples of 19 calls.
            Execution time mean : 59.553335 ms
    Execution time std-deviation : 4.507430 ms
  Execution time lower quantile : 54.450115 ms ( 2.5%)
  Execution time upper quantile : 69.288104 ms (97.5%)
                  Overhead used : 1.662189 ns

Found 4 outliers in 60 samples (6.6667 %)
  low-severe     4 (6.6667 %)
Variance from outliers : 56.7865 % Variance is severely inflated by outliers
=> nil
Run Code Online (Sandbox Code Playgroud)

assoc-at在更新大型惰性序列中的第一个项目时,该版本快 2-3 倍,但不比(last (concat [666] (rest big-lazy))).