Clojure Удалить элемент из вектора в указанном месте

Есть ли способ удалить элемент из вектора на основе индекса, на данный момент я использую subvec для разделения вектора и воссоздания его снова. Я ищу реверс assoc для векторов?


person Hamza Yerlikaya    schedule 08.09.2009    source источник


Ответы (7)


subvec, вероятно, лучший способ. В документах Clojure говорится, что subvec — это «O(1) и очень быстро, так как результирующий вектор имеет общую структуру с оригиналом и обрезка не выполняется». Альтернативой может быть обход вектора и построение нового с пропуском определенных элементов, что будет медленнее.

Удаление элементов из середины вектора — это не то, в чем векторы хороши. Если вам приходится делать это часто, рассмотрите возможность использования хэш-карты, чтобы вы могли использовать dissoc.

Видеть:

  • subvec на clojuredocs.org
  • subvec в clojure.github.io, куда указывает официальный сайт к.
person Brian Carper    schedule 08.09.2009

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 самый быстрый

person Timothy Pratley    schedule 11.09.2009

Библиотека векторов clojure.core.rrb-vector обеспечивает логарифмическую конкатенацию времени и срезы. Предполагая, что вам нужна настойчивость, и учитывая то, о чем вы просите, логарифмическое решение времени является настолько быстрым, насколько это теоретически возможно. В частности, это намного быстрее, чем любое решение, использующее собственный subvec clojure, поскольку шаг concat помещает любое такое решение в линейное время.

(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]
person djhaskin987    schedule 29.08.2014

Вот решение, которое оказалось хорошим:

(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)
person narkisr    schedule 28.08.2012
comment
Для удаления одного элемента это медленнее, чем использование subvec, но для удаления нескольких индексов это довольно удобно. - person Torbjørn; 09.09.2012

subvec быстро; в сочетании с переходными процессами это дает еще лучшие результаты.

Использование критерия для оценки:

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 ms, 953,565863 µs, 314,387437 µs.

person omiel    schedule 27.06.2014
comment
Какую версию clojure вы использовали для версии subvec w/transients? Возвращаясь на 4 года назад, мы обнаружили открытую ошибку, связанную с несовместимостью subvec с переходный, потому что APersistentVector$SubVector does not implement IEditableCollection. Я предполагаю, что именно поэтому пример оборачивает вызов subvec в (vec (subvec ...)), хотя в моих тестах с clojure 1.7, vec в этом случае по-прежнему будет возвращать clojure.lang.APersistentVector$SubVector, предположительно в качестве оптимизации с тех пор, но я не копался в источнике clojure для этого. - person Ryan Wilson; 21.07.2015
comment
1.6.0 я думаю. Он был выпущен за несколько месяцев до того, как был написан этот ответ. - person omiel; 21.07.2015
comment
Не могли бы вы обновиться до Clojure 1.8? Как упомянул @RyanWilson, ваш переходный пример не работает с 1.8. - person Ahmed Fasih; 28.04.2016
comment
Замена vec в вашем последнем примере на into [] работает с Clojure 1.8, но это абсолютно убивает производительность :(. Самое быстрое, что я могу получить, это (into (subvec v 0 i) (subvec v j)), что примерно на 15% быстрее, чем ваша средняя версия using subvec на моем компьютере… - person Ahmed Fasih; 28.04.2016

Еще одна возможность, которая должна работать с любой последовательностью, а не бомбить, если индекс выходит за пределы допустимого диапазона...

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

Возможно, будет быстрее получить нужные вам индексы.

(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"
person ahhhhhhhhhhhhhhhhhhhhhhhhhhhhh    schedule 10.05.2020