我需要动态修改此结构的数据:
var safari = Application('Safari');
safari.includeStandardAdditions = true;
safari.quit(); // Avoid having multiple Safari windows open
safari.activate(); // Start Safari and bring to front
var document = safari.documents[0];
document.url = 'http://stackoverflow.com';
以下内容:
[:db/id
:list/title
:list/type
{:list/items [... lots of nested data ...]}]
由于我正在处理几个不同的查询,我可以确定连接将是向量中的第四个项目。但我需要用[:db/id
:list/title
:list/type
{(default :list/items []) [... lots of nested data ...]}]
替换:list/items
的每个实例。
我知道这样做的唯一方法是使用(default :list/items [])
。但是,它会导致无限递归:
clojure.walk/prewalk
一旦步行找到(clojure.walk/prewalk #(if (= :list/items %)
'(default :list/items [])
%)
query)
并将其替换为:list/items
,它就会在替换后的值中找到'(default :list/items [])
,然后替换它。依此类推。
我可以使用一个原子来确保该值只被替换一次,但这就像是作弊。
还有其他方法吗?
答案 0 :(得分:1)
在这种情况下你可能需要使用postwalk:
user>
(def query [:db/id
:list/title
:list/type
{:list/items [:db/id
:list/title
:list/type
{:list/items []}]}])
#'user/query
user> (clojure.walk/postwalk #(if (= :list/items %)
'(default :list/items [])
%)
query)
[:db/id :list/title :list/type
{(default :list/items []) [:db/id :list/title :list/type {(default :list/items []) []}]}]
即使已经用新集合替换了叶子,postwalk也不会更深入内容:
user> (clojure.walk/prewalk #(do (println %)
(if (= % 1) [10] %))
[[1 2 3 [1 2]] [1 2]])
[[1 2 3 [1 2]] [1 2]]
[1 2 3 [1 2]]
1
10 ;; goes deeper
2
3
[1 2]
1
10 ;; and here
2
[1 2]
1
10 ;; and here
2
[[[10] 2 3 [[10] 2]] [[10] 2]]
user> (clojure.walk/postwalk #(do (println %)
(if (= % 1) [10] %))
[[1 2 3 [1 2]] [1 2]])
1
2
3
1
2
[[10] 2]
[[10] 2 3 [[10] 2]]
1
2
[[10] 2]
[[[10] 2 3 [[10] 2]] [[10] 2]]
[[[10] 2 3 [[10] 2]] [[10] 2]]
顺便说一句,对于你的确切情况,有一个很好的函数prewalk-replace
/ postwalk-replace
:
user> (clojure.walk/postwalk-replace
{:list/items '(default :list/items [])} query)
[:db/id :list/title :list/type
{(default :list/items []) [:db/id :list/title :list/type {(default :list/items []) []}]}]
评论后更新: 更多控制替换的一些(合成)示例。假设您想要替换嵌套向量的任意集合中的特定项目,但只更换项目一次(第一次看到它),并保持其余部分不变:
user> (require '[clojure.zip :as z])
user>
(defn replace-once [rep coll]
(loop [curr (z/vector-zip coll) rep rep]
(if (empty? rep) (z/root curr)
(let [n (z/node curr) r (rep n)]
(cond (z/end? curr) (z/root curr)
r (recur (z/replace curr r) (dissoc rep n))
:else (recur (z/next curr) rep))))))
#'user/replace-once
user> (replace-once {1 100 2 200} [[4 3 2] [10 1 2] 1 2 [5 3 2]])
[[4 3 200] [10 100 2] 1 2 [5 3 2]]
(这里你只是从替换候选人地图(rep
)中删除被替换的项目,并通过递归进一步传递,直到它为空)