Clojure:如何替换嵌套列表中的元素?

Gab*_*iMe 7 clojure

我有这个深层嵌套的列表(列表列表),我想替换列表中的单个任意元素.我怎样才能做到这一点 ?(内置替换可能会替换多次出现,而我只需要替换一个元素.)

Bri*_*per 9

正如其他人已经说过的那样,如果你需要做这种事情,使用列表真的不是一个好主意.随机访问是为什么做的向量. assoc-in这样做有效.使用列表,您无法远离递归到子列表,并将其中的大多数替换为自身的更改版本,一直回到顶部.

这段代码虽然效率低下且笨拙,但仍会这样做.从皮癣中借来:

(defn replace-in-list [coll n x]
  (concat (take n coll) (list x) (nthnext coll (inc n))))

(defn replace-in-sublist [coll ns x]
  (if (seq ns)
    (let [sublist (nth coll (first ns))]
      (replace-in-list coll
                       (first ns)
                       (replace-in-sublist sublist (rest ns) x)))
    x))
Run Code Online (Sandbox Code Playgroud)

用法:

user> (def x '(0 1 2 (0 1 (0 1 2) 3 4 (0 1 2))))
#'user/x
user> (replace-in-sublist x [3 2 0] :foo) 
(0 1 2 (0 1 (:foo 1 2) 3 4 (0 1 2)))
user> (replace-in-sublist x [3 2] :foo) 
(0 1 2 (0 1 :foo 3 4 (0 1 2)))
user> (replace-in-sublist x [3 5 1] '(:foo :bar)) 
(0 1 2 (0 1 (0 1 2) 3 4 (0 (:foo :bar) 2)))
Run Code Online (Sandbox Code Playgroud)

IndexOutOfBoundsException如果您提供的任何n大于子列表的长度,您将获得.它也不是尾递归的.它也不是惯用语,因为好的Clojure代码不会使用列表来处理所有事情.这太糟糕了.在使用它之前,我可能会使用可变的Java数组.我想你应该已经明白了.

编辑

在这种情况下,列表比向量更糟的原因:

user> (time
       (let [x '(0 1 2 (0 1 (0 1 2) 3 4 (0 1 2)))]               ;'
         (dotimes [_ 1e6] (replace-in-sublist x [3 2 0] :foo))))
"Elapsed time: 5201.110134 msecs"
nil
user> (time
       (let [x [0 1 2 [0 1 [0 1 2] 3 4 [0 1 2]]]]
         (dotimes [_ 1e6] (assoc-in x [3 2 0] :foo))))
"Elapsed time: 2925.318122 msecs"
nil
Run Code Online (Sandbox Code Playgroud)

你也不必assoc-in自己写,它已经存在.assoc-in一段时间看一下实现; 由于矢量通过索引提供有效且容易的随机访问,因此它简单明了(与列表版本相比)get.

您也不必引用像您必须引用列表的向量.Clojure中的列表强烈暗示"我在这里调用函数或宏".

矢量(以及地图,集合等)可以通过seqs 遍历.您可以透明地以列表方式使用向量,那么为什么不使用向量并且两全其美?

矢量也在视觉上突出.由于广泛使用[]和,所以Clojure代码不像其他Lisp那样庞大的parens {}.有些人觉得这很烦人,我觉得它让事情更容易阅读.(我的编辑器语法 - 突出显示(),[]以及{}不同的帮助甚至更多.)

在某些情况下,我会使用数据列表:

  1. 如果我有一个需要从前面增长的有序数据结构,我将永远不需要随机访问
  2. 通过"手动"构建seq lazy-seq
  3. 编写一个宏,需要将代码作为数据返回


Art*_*ldt 6

对于简单的情况,递归替换函数将为您提供您需要的更多复杂性.当事情变得复杂一点时,破解打开clojure构建拉链功能的时间:"Clojure包括纯功能,通用的树行走和编辑,使用称为拉链的技术(在命名空间zip中)."

改编自http://clojure.org/other_libraries中的示例

(defn randomly-replace [replace-with in-tree]
    (loop [loc dz]
      (if (zip/end? loc)
      (zip/root loc)
     (recur
      (zip/next
       (if (= 0 (get-random-int 10))
         (zip/replace loc replace-with)
         loc)))))
Run Code Online (Sandbox Code Playgroud)

这些将适用于嵌套的任何东西(seq'able)甚至xmls


kot*_*rak 5

它有点不回答你的问题,但如果你有向量而不是列表:

user=> (update-in [1 [2 3] 4 5] [1 1] inc)
[1 [2 4] 4 5]
user=> (assoc-in [1 [2 3] 4 5] [1 1] 6)
[1 [2 6] 4 5]
Run Code Online (Sandbox Code Playgroud)

因此,如果可能的话,请避免列表支持向量以获得更好的访问行为.如果你必须从各种来源使用lazy-seq,这当然不是一个建议......