更新或关联列表而不是向量

时间:2016-12-19 10:28:25

标签: clojure

更新矢量工作正常:

(update [{:idx :a} {:idx :b}] 1 (fn [_] {:idx "Hi"}))
;; => [{:idx :a} {:idx "Hi"}]

然而,尝试使用列表执行相同的操作不起作用:

(update '({:idx :a} {:idx :b}) 1 (fn [_] {:idx "Hi"}))
;; => ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Associative  clojure.lang.RT.assoc (RT.java:807)

assoc确实存在同样的问题。

我想对延迟类型而不是向量进行更新和覆盖操作。这里有什么根本问题,有没有办法解决它?

3 个答案:

答案 0 :(得分:3)

根本问题是update函数适用于关联结构,即向量和映射。列表不能将键作为查找值的函数。

user=> (associative? [])
true
user=> (associative? {})
true
user=> (associative? `())
false

update在幕后使用get进行随机访问工作。

  

我想对延迟类型执行更新和覆盖操作   而不是矢量

目前尚不清楚想要实现的目标。你是正确的,矢量不是懒惰的,但是如果你想对集合进行随机访问操作,那么矢量对于这种情况是理想的,而列表则不是。

  

有没有办法解决它?

是的,但您仍然无法使用update功能,在您的情况下看起来不会有任何好处。

使用列表,您必须遍历列表才能访问列表中的某个索引 - 所以在许多情况下,即使它是懒惰的,您也必须实现大量的序列。

答案 1 :(得分:2)

您可以使用takedrop

定义自己的功能
(defn lupdate [list n function]
  (let [[head & tail] (drop n list)]
    (concat (take n list)
            (cons (function head) tail))))

user=> (lupdate '(a b c d e f g h) 4 str)
(a b c d "e" f g h)

使用延迟序列,这意味着您将计算n个第一个值(但不会计算剩余的值,这毕竟是我们使用延迟序列的重要部分)。您还必须考虑空间和时间的复杂性(concat等)。但如果你真的需要对懒惰序列进行操作,那就是你要走的路。

答案 2 :(得分:1)

在你的问题背后找到你想要解决的问题:

您可以使用Clojure的序列函数构建一个简单的解决方案:

(defn elf [n]
  (loop [population (range 1 (inc n))]
    (if (<= (count population) 1)
      (first population)
      (let [survivors (->> population
                           (take-nth 2)
                           ((if (-> population count odd?) rest identity)))]
        (recur survivors)))))

例如,

(map (juxt identity elf) (range 1 8))
;([1 1] [2 1] [3 3] [4 1] [5 3] [6 5] [7 7])

这具有复杂性O(n)。您可以通过将人口计数作为循环中的冗余参数传递,或者将countpopulation转储到向量中来加快survivors。序列函数 - take-nthrest - 完全能够进行除草。

我希望我做对了!