在 Clojure 的嵌套映射中关联多个键/值的惯用方法是啥?
Posted
技术标签:
【中文标题】在 Clojure 的嵌套映射中关联多个键/值的惯用方法是啥?【英文标题】:What is the idiomatic way to assoc several keys/values in a nested map in Clojure?在 Clojure 的嵌套映射中关联多个键/值的惯用方法是什么? 【发布时间】:2011-05-28 13:51:07 【问题描述】:想象一下你有一张这样的地图:
(def person
:name
:first-name "John"
:middle-name "Michael"
:last-name "Smith" )
在一个表达式中更改与 :first-name 和 :last-name 相关的值的惯用方法是什么?
(澄清:假设您要将 :first-name 设置为“Bob”,将 :last-name 设置为“Doe”。假设此映射中还有一些我们想要保留的其他值,因此构造从头开始不是一种选择)
【问题讨论】:
【参考方案1】:这里有几种方法。
user> (update-in person [:name] assoc :first-name "Bob" :last-name "Doe")
:name :middle-name "Michael", :last-name "Doe", :first-name "Bob"
user> (update-in person [:name] merge :first-name "Bob" :last-name "Doe")
:name :middle-name "Michael", :last-name "Doe", :first-name "Bob"
user> (update-in person [:name] into :first-name "Bob" :last-name "Doe")
:name :middle-name "Michael", :last-name "Doe", :first-name "Bob"
user> (-> person
(assoc-in [:name :first-name] "Bob")
(assoc-in [:name :last-name] "Doe"))
:name :middle-name "Michael", :last-name "Doe", :first-name "Bob"
编辑
update-in
在你的地图上递归 assoc
s。在这种情况下,它大致相当于:
user> (assoc person :name
(assoc (:name person)
:first-name "Bob"
:last-name "Doe"))
随着您深入一系列嵌套映射,键的重复变得越来越乏味。 update-in
的递归可让您避免一遍又一遍地重复键(例如 :name
);中间结果存储在递归调用之间的堆栈中。查看update-in 的源代码,了解它是如何完成的。
user> (def foo :bar :baz :quux 123)
#'user/foo
user> (assoc foo :bar
(assoc (:bar foo) :baz
(assoc (:baz (:bar foo)) :quux
(inc (:quux (:baz (:bar foo)))))))
:bar :baz :quux 124
user> (update-in foo [:bar :baz :quux] inc)
:bar :baz :quux 124
assoc
是动态的(update-in
、assoc-in
和大多数其他对 Clojure 数据结构进行操作的 Clojure 函数也是如此)。如果assoc
到地图上,则返回地图。如果你assoc
到一个向量上,它会返回一个向量。查看 assoc 的源代码并查看 Clojure 源代码中的 RT.java
以了解详细信息。
【讨论】:
谢谢!第一条语句的语法是如何工作的? assoc 怎么知道它在通过“update-in”传递给它的地图上运行?看起来很整洁,但是编译器怎么不糊涂呢? assoc 不在乎是否只获取地图和一些 args(成对)然后它会做它的事情。您返回的地图将通过更新语义放入正确的位置,然后作为孔地图返回。 不确定您在问什么,但我添加了一些编辑以扩展答案,希望对您有所帮助。 再次感谢。我在 'assoc :first-name "Bob" :last-name "Doe"' 部分苦苦挣扎,试图弄清楚 assoc 如何“知道”它应该将键/值放入哪个映射(鉴于通常的语法for assoc 在键/值开始之前包含 map 变量,例如 (assoc myMap :first-name "Bob" :last-name "Doe")。我会再玩这个。 我这样做了:(defn multi-assoc-in [m & kvvp] (reduce #(apply assoc-in %1 %2) m (partition 2 kvvp)))
其中 kvvp 是 kevvector-value 对,与您传递给 assoc-in 的相同。现在我可以做(swap! myatom multi-assoc-in [:a :b] "whatever" [:c :d :e] "blabla")
【参考方案2】:
我不确定我的情况是否完全一样,但我有要应用的更改列表:
(def updates [[[:name :first-name] "Bob"]
[[:name :last-name] "Doe"]])
在这种情况下,您可以像这样使用 assoc-in 减少该列表:
(reduce #(assoc-in %1 (first %2) (second %2)) person updates)
【讨论】:
以上是关于在 Clojure 的嵌套映射中关联多个键/值的惯用方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章