如何在Clojure中替换向量中的最后一个元素

时间:2017-09-21 19:51:23

标签: clojure

作为Clojure的新手,我经常难以表达最简单的事情。例如,替换向量中的最后一个元素,即

v[-1]=new_value

在python中,我最终得到了Clojure中的以下变体:

(assoc v (dec (count v)) new_value)

这至少可以说是非常长而且无法解释,或者

(conj (vec (butlast v)) new_value) 

更糟糕的是,它的运行时间为O(n)

这让我觉得很傻,就像一个试图用俱乐部修理瑞士手表的穴居人。

用Clojure方法替换向量中的最后一个元素是什么?

支持我的O(n) - butlast版本 - 版本(Clojure 1.8):

(def v (vec (range 1e6)))
#'user/v
user=> (time (first (conj (vec (butlast v)) 55)))
"Elapsed time: 232.686159 msecs"
0
(def v (vec (range 1e7)))
#'user/v
user=> (time (first (conj (vec (butlast v)) 55)))
"Elapsed time: 2423.828127 msecs"
0

所以基本上10次元素的数量要慢10倍。

2 个答案:

答案 0 :(得分:10)

我用

(defn set-top [coll x]
  (conj (pop coll) x))

例如,

(set-top [1 2 3] :a)
=> [1 2 :a]

但它也适用于列表的前面

(set-top '(1 2 3) :a)
=> (:a 2 3)

Clojure堆栈函数 - peekpopconj - 处理顺序集合的自然开放端。

但是没有一个正确的方法。

各种解决方案如何对空载体做出反应?

  • 您的Python v[-1]=new_value会引发异常,您的(assoc v (dec (count v)) new_value)和我的(defn set-top [coll x] (conj (pop coll) x))也是如此。
  • 您的(conj (vec (butlast v)) new_value)返回[new_value]butlast无效。

答案 1 :(得分:1)

如果您坚持“纯粹”,那么您的第二或第三种解决方案将起作用。我更喜欢更简单&更明确地使用辅助函数from the Tupelo library

(s/defn replace-at :- ts/List
  "Replaces an element in a collection at the specified index."
  [coll     :- ts/List
   index    :- s/Int
   elem     :- s/Any]
   ...)

(is (= [9 1 2] (replace-at (range 3) 0 9)))
(is (= [0 9 2] (replace-at (range 3) 1 9)))
(is (= [0 1 9] (replace-at (range 3) 2 9)))
As with drop-at, replace-at will throw an exception for invalid values of index.

存在类似的辅助函数
  • 嵌件在
  • 落在
  • 前置
  • 追加

请注意,对于Clojure列表(渴望或懒惰)或Clojure向量,上述所有工作同样适用。 The conj solution will fail除非您小心谨慎,始终将输入强制转换为向量,如示例所示。