Clojure从指定位置的Vector中删除项目

Clojure Remove item from Vector at a Specified Location

截至目前,有没有一种方法可以从基于索引的向量中删除项目,我正在使用subvec拆分向量并再次重新创建它。 我在寻找assoc的反向矢量吗?


subvec可能是最好的方法。 Clojure文档说subvec是" O(1)且非常快,因为生成的向量与原始向量共享结构,并且不进行修剪"。另一种选择是遍历向量并在跳过某些元素的同时建立一个新的向量,这会比较慢。

从向量中间删除元素不一定是向量所擅长的。如果必须经常执行此操作,请考虑使用哈希映射,以便可以使用dissoc

看到:

  • clojuredocs.org处的subvec
  • 官方网站指向的clojure.github.io上的subvec。

1
2
3
4
(defn vec-remove
 "remove elem in coll"
  [pos coll]
  (vec (concat (subvec coll 0 pos) (subvec coll (inc pos)))))

1
2
3
4
5
user=> (def a [1 2 3 4 5])
user=> (time (dotimes [n 100000] (vec (concat (take 2 a) (drop 3 a)))))
"Elapsed time: 1185.539413 msecs"
user=> (time (dotimes [n 100000] (vec (concat (subvec a 0 2) (subvec a 3 5)))))
"Elapsed time: 760.072048 msecs"

是的-Subvec最快


向量库clojure.core.rrb-vector提供对数时间级联和切片。假设您需要持久性,并考虑您的要求,对数时间解在理论上是最快的。特别是,它比使用clojure的本机subvec的任何解决方案都快得多,因为concat步骤将任何此类解决方案置于线性时间。

1
2
3
4
(require '[clojure.core.rrb-vector :as fv])
(let [s (vec [0 1 2 3 4])]
  (fv/catvec (fv/subvec s 0 2) (fv/subvec s 3 5)))
; => [0 1 3 4]


这是一个很好的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
(defn index-exclude [r ex]
  "Take all indices execpted ex"
    (filter #(not (ex %)) (range r)))


(defn dissoc-idx [v & ds]
   (map v (index-exclude (count v) (into #{} ds))))

(dissoc-idx [1 2 3] 1 2)


'(1)


subvec是快速的;与瞬变相结合,可以提供更好的结果。

使用标准进行基准测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
user=> (def len 5)
user=> (def v (vec (range 0 5))
user=> (def i (quot len 2))
user=> (def j (inc i))

; using take/drop
user=> (bench
         (vec (concat (take i v) (drop j v))))
;              Execution time mean : 817,618757 ns
;     Execution time std-deviation : 9,371922 ns

; using subvec
user=> (bench
         (vec (concat (subvec v 0 i) (subvec v j len))))
;              Execution time mean : 604,501041 ns
;     Execution time std-deviation : 8,163552 ns

; using subvec and transients
user=> (bench
         (persistent!
          (reduce conj! (transient (vec (subvec v 0 i))) (subvec v j len))))
;              Execution time mean : 307,819500 ns
;     Execution time std-deviation : 4,359432 ns

长度越大,加速越大; len等于10000的同一工作台给出平均值:1,368250 ms953,565863 μs314,387437 μs


获得所需的索引可能会更快。

1
2
3
4
5
6
7
8
9
(def a [1 2 3 4 5])

(def indexes [0 1 3 4])

(time (dotimes [n 100000] (vec (concat (subvec a 0 2) (subvec a 3 5)))))
"Elapsed time: 69.401787 msecs"

(time (dotimes [n 100000] (mapv #(a %) indexes)))
"Elapsed time: 28.18766 msecs"

如果索引超出范围,则另一种可能性应该适用于任何序列,而不是炸弹...

1
2
(defn drop-index [col idx]
  (filter identity (map-indexed #(if (not= %1 idx) %2) col)))