作为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倍。
答案 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堆栈函数 - peek
,pop
和conj
- 处理顺序集合的自然开放端。
但是没有一个正确的方法。
各种解决方案如何对空载体做出反应?
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除非您小心谨慎,始终将输入强制转换为向量,如示例所示。