Подтвердить что ты не робот

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

Представьте, что у вас есть такая карта:

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

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

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

4b9b3361

Ответ 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 на вашей карте. В этом случае это примерно эквивалентно:

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 для получения подробной информации.