在Clojure中,在嵌套映射中关联多个键/值的惯用方法是什么?

在Clojure中,在嵌套映射中关联多个键/值的惯用方法是什么?,clojure,idioms,Clojure,Idioms,想象你有一张这样的地图: (def person { :name { :first-name "John" :middle-name "Michael" :last-name "Smith" }}) (reduce #(assoc-in %1 (first %2) (second %2)) person updates) 在一个表达式中更改与:first name和:last name关联的值的惯用方法是什么 (澄清:假设您想将:first name设置为“Bob

想象你有一张这样的地图:

(def person {
  :name {
    :first-name "John"
    :middle-name "Michael"
    :last-name "Smith" }})
(reduce #(assoc-in %1 (first %2) (second %2)) person updates)
在一个表达式中更改与:first name和:last name关联的值的惯用方法是什么


(澄清:假设您想将:first name设置为“Bob”,将:last name设置为“Doe”。还假设此地图中有一些我们想要保留的其他值,因此从头开始构建它不是一个选项)

以下是几种方法

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"}}
编辑
updatein
在地图上执行递归
assoc
s。在这种情况下,它大致相当于:

user> (assoc person :name 
             (assoc (:name person) 
                    :first-name "Bob" 
                    :last-name "Doe"))
随着对一系列嵌套贴图的深入,关键点的重复变得越来越乏味<在的递归中进行代码>更新可以避免反复重复键(例如,
:name
);中间结果存储在递归调用之间的堆栈上。请查看的源代码以了解它是如何完成的

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
是动态的(与
中的更新、
中的assoc以及大多数其他在Clojure数据结构上运行的Clojure函数一样)。如果将
assoc
关联到一个映射,它将返回一个映射。如果将
assoc
关联到一个向量,它将返回一个向量。查看源代码并查看Clojure源代码中的
RT.java
以了解详细信息。

我不确定我的情况是否完全相同,但我有一系列更改要应用:

(def updates [[[:name :first-name] "Bob"] 
              [[:name :last-name] "Doe"]])
在这种情况下,您可以使用assoc以如下方式减少该列表:

(def person {
  :name {
    :first-name "John"
    :middle-name "Michael"
    :last-name "Smith" }})
(reduce #(assoc-in %1 (first %2) (second %2)) person updates)

谢谢第一条语句的语法是如何工作的?assoc如何知道它是在通过“更新输入”传递到它的映射上运行的?它看起来很整洁,但是编译器如何不被弄糊涂呢?assoc不在乎是否只是得到了一个映射和一些参数(对),然后它就完成了它的工作。你得到的地图将通过语义更新放在正确的位置,然后作为孔地图返回。不确定你在问什么,但我添加了一些编辑来扩展答案,希望这有点帮助。再次感谢。我一直在努力解决“assoc:first name“Bob”:last name“Doe”部分,试图弄清楚assoc如何“知道”应该将键/值放入哪个映射(假定assoc的常用语法在键/值开始之前包括map变量,例如(assoc myMap:first name“Bob”:last name“Doe”)。我将进一步使用它。我这样做了:
(在[m&kvvp]中定义多个assoc](reduce#(在%1%2中应用assoc)m(分区2 kvvp))
其中kvvp是kevvector值对,与在中传递到assoc的值对相同。现在我可以执行
(swap!myatom多个assoc in[:a:b]“任意”[:c:d:e]“bla”)