在Clojure中我希望在列表中更改特定项目(列表)与其他。 这是我的结构:
(def myLis '((3 3) (5 5) (5 5)))
(def som '(4 4))
我希望在myLis
中使用som
更改第二个元素。
myLis
中的结果为'((3 3) (4 4) (5 5))
这是基本的例子。我在myList中有几百个项目。
我尝试关联和更新,但这不适用于列表。
当我尝试使用assoc并更新时:
(update-in myLis [1] som)
(assoc myLis 1 som)
(assoc-in myLis [1] som)
有这样的错误:
clojure.lang.PersistentList cannot be cast to clojure.lang.Associative
如何快速更改此结构中的第n个元素(列表列表)。
答案 0 :(得分:2)
对于大多数用途,您通常应优先使用[1 2 3]
等向量,而不是像'(1 2 3)
这样的列表。在Clojure中,列表通常用于函数调用,如(+ 1 2)
,而数据文字向量通常用于[1 2 3]
。
以下代码显示了两个有用的选项。
主要代码:
(ns clj.core
(:require
[tupelo.core :as t]
))
(t/refer-tupelo)
(def myLis [ [3 3] [5 5] [5 5] ] )
(def som [4 4] )
(spyx (assoc myLis 1 som))
(spyx (assoc-in myLis [1] som))
(defn -main [& args]
(println "-main"))
结果:
~/clj > lein run
(assoc myLis 1 som) => [[3 3] [4 4] [5 5]]
(assoc-in myLis [1] som) => [[3 3] [4 4] [5 5]]
您需要在project.clj
中进行此操作才能使(spy ...)
正常工作:
:dependencies [
[tupelo "0.9.9"]
...
如果您确实要将所有内容保留在列表中,可以使用replace-at
from the Tupelo library。它的工作原理如下:
(def myLis '( (3 3) (5 5) (5 5) ) )
(def vec-1 [4 4] )
(def list-1 '(4 4) )
(spyx (t/replace-at myLis 1 vec-1 ))
(spyx (t/replace-at myLis 1 list-1))
(spyx (apply list (t/replace-at myLis 1 list-1)))
结果
> lein run
(t/replace-at myLis 1 vec-1) => [(3 3) [4 4] (5 5)]
(t/replace-at myLis 1 list-1) => [(3 3) (4 4) (5 5)]
(apply list (t/replace-at myLis 1 list-1)) => ((3 3) (4 4) (5 5))
前两个示例显示新元素可以是任何内容,例如向量[4 4]
或列表(4 4)
。另请注意,replace-at
始终返回矢量结果。如果您希望最终结果也是列表,则需要使用(apply list <some-collection>)
。
答案 1 :(得分:2)
正如clojure圣经(Clojure Programming)所指出的那样:
因为[lists]是链表,所以它们不支持高效随机 访问;因此,列表中的第n个将以线性时间运行(而不是 与矢量,数组等一起使用时的常数时间,并得到它 根本不支持列表,因为这样做不会与get一致 次线性效率的目标。
因此,为了替换列表中的元素,您必须遍历所有元素,因此项目在列表中的运行时间越长,并使用之前的元素重新构建列表,新项目以及之后的所有元素(休息)。或者,将列表转换为向量,如果绝对必须使用列表,请使用update-in并返回列表。
但是,如果可以,那么您是否可以在代码而不是列表中使用序列是值得的,因此您可以互换使用向您执行的处理更有效的向量或其他抽象。< / p>
但是,通过列表满足您的要求基础的一个简单的功能是:
(defn list-update-in [l i x]
(let [newlist (take i l)
newlist (concat newlist (list x))
newlist (concat newlist (drop (+ 1 i) l))]
newlist))
user> (list-update-in '((1 2) (2 3) (3 4)) 1 '(8 9))
((1 2) (8 9) (3 4))
此
没有越界检查答案 2 :(得分:1)
使用loop / recur添加一个额外的答案,以重新调整OP自己的解决方案,使其更像lisp。
(defn list-update-in-recur [l i x]
(loop [new-data [] old-list l]
(if (seq old-list)
(if (= (count new-data) i)
(recur (conj new-data x) (rest old-list))
(recur (conj new-data (first old-list)) (rest old-list)))
(apply list new-data))))
user> (list-update-in-recur '((1 2) (2 3) (3 4)) 1 '(8 9))
((1 2) (8 9) (3 4))
需要注意几点:
(apply list new-data)
old-list
值,每次迭代都会减小,并且退出条件只是在使用测试(seq old-list)
时还剩下任何元素时返回false / n为空时。conj
所有内容(添加到列表的开头)我们将其反转以返回输出。nth
替换为first
和rest
,效率更高,并且每次迭代都不必遍历整个列表。答案 3 :(得分:0)
我的解决方案包含列表:
(def newL '())
(def i 1)
(loop [k (- (count myLis) 1)]
(when (> k -1)
(cond
(= k i) (def newL (conj newL som))
:else (def newL (conj newL (nth myLis k)))
)
(recur (- k 1))
)
)