Clojure:如何替换嵌套列表中的元素? [英] Clojure: How to replace an element in a nested list?

查看:106
本文介绍了Clojure:如何替换嵌套列表中的元素?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个深层嵌套的列表(列表列表),我想替换列表中的一个任意元素。我如何做到这一点? (内置替换可能会替换许多出现,而我只需要替换一个元素。)

I have this deeply nested list (list of lists) and I want to replace a single arbitrary element in the list. How can I do this ? (The built-in replace might replace many occurrences while I need to replace only one element.)

推荐答案

,使用列表真的不是一个好主意,如果你需要做这种事情。随机访问是什么向量。 assoc-in 有效地执行此操作。使用列表,你不能逃避到递归到子列表中,并且用自己的改变版本替换大多数子列表,直到返回顶部。

As everyone else already said, using lists is really not a good idea if you need to do this kind of thing. Random access is what vectors are made for. assoc-in does this efficiently. With lists you can't get away from recursing down into the sublists and replacing most of them with altered versions of themselves all the way back up to the top.

此代码会做到,虽然效率低下和笨拙。借用来自dermatthias:

This code will do it though, albeit inefficiently and clumsily. Borrowing from dermatthias:

(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))

用法:

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)))

如果您给予 IndexOutOfBoundsException 任何 n 大于子列表的长度。它也不是尾递归。这也不是惯用的,因为好的Clojure代码避开使用列表的一切。这太糟糕了。我可能使用可变的Java数组之前,我使用这个。

You'll get IndexOutOfBoundsException if you give any n greater than the length of a sublist. It's also not tail-recursive. It's also not idiomatic because good Clojure code shies away from using lists for everything. It's horrible. I'd probably use mutable Java arrays before I used this. I think you get the idea.

在这种情况下列表比向量差的原因:

Reasons why lists are worse than vectors in this case:

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

您也不必自己写 assoc-in ,它已经存在。看一下 assoc-in 的实现;它是简单和直接(相对于列表版本)感谢向量提供高效和容易随机访问的索引,通过 get

You also don't have to write assoc-in yourself, it already exists. Look at the implementation for assoc-in sometime; it's simple and straightforward (compared to the list version) thanks to vectors giving efficient and easy random access by index, via get.

你也不需要引用像你这样的报价单。 Clojure中的列表强烈地暗示我在这里调用函数或宏。

You also don't have to quote vectors like you have to quote lists. Lists in Clojure strongly imply "I'm calling a function or macro here".

向量(和地图,集等)可以通过 seq s。你可以透明地使用向量列表的方式,所以为什么不使用向量和两个世界的最好的?

Vectors (and maps, sets etc.) can be traversed via seqs. You can transparently use vectors in list-like ways, so why not use vectors and have the best of both worlds?

向量也突出了视觉。由于广泛使用 [] {} ,Clojure代码比其他Lisps更少的大写。有些人发现这个烦人,我发现它使事情更容易阅读。 (我的编辑器语法高亮() [] {} 不同,这有助于更多。)

Vectors also stand out visually. Clojure code is less of a huge blob of parens than other Lisps thanks to widespread use of [] and {}. Some people find this annoying, I find it makes things easier to read. (My editor syntax-highlights (), [] and {} differently which helps even more.)

有些实例我会使用数据列表:

Some instances I'd use a list for data:


  1. 如果我有一个有序的数据结构需要从前面增长,我永远不需要随机访问

  2. 构建一个seq手动,如 lazy-seq

  3. 编写需要将数据作为数据返回的宏

这篇关于Clojure:如何替换嵌套列表中的元素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆