Какой идиоматический способ связать несколько ключей / значений во вложенной карте в Clojure? - PullRequest
30 голосов
/ 21 декабря 2010

Представьте, что у вас есть карта, подобная этой:

(def person {
  :name {
    :first-name "John"
    :middle-name "Michael"
    :last-name "Smith" }})

Каков идиоматический способ изменения значений, связанных с обоими: именем и фамилией в одном выражении?

(Пояснение: допустим, вы хотите установить: first-name в "Bob" и: last-name в "Doe". Также допустим, что в этой карте есть некоторые другие значения, которые мы хотим сохранить, поэтому создаем ее с нуля.не вариант)

1 Ответ

44 голосов
/ 21 декабря 2010

Вот несколько способов.

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 и посмотрите в RT.java в источнике Clojure для деталей.

...