Является ли (def m (update-in m ks f & args)) хорошей практикой?

Я новичок в мире clojure и у меня есть сомнения. У меня есть вложенная карта, такая как

(def accounts (hash-map :XYZ (hash-map :balance (hash-map 171000 0 :171018 500 :171025 200)
                                   :statement (hash-map :171018 [{:desc "purchase" :amount 200}
                                                                 {:desc "deposit" :amount 700}]
                                                        :171025 [{:desc "purchase" :amount 300}]))

И я хочу обновить утверждения, поэтому я написал простую функцию:

(defn add-statement
[account date desc amount]
(def accounts (update-in accounts [account :statement date] conj {:desc desc :amount amount}))

Но у меня такое чувство, что я делаю это неправильно...

1 ответ

Решение

Вам нужно будет изменить accounts быть изменчивым, если вы хотите обновить их. Обычный способ сделать это, делая учетные записи atom, Тогда ваша функция может выглядеть так:

(defn add-statement! [account date desc amount]
  (swap! accounts update-in [account :statement date]
         (fn [line-items] 
           (conj (or line-items []) {:desc desc :amount amount}))))

Это добавит позицию выписки на новую или существующую дату. update-in такой же, как у вас, за исключением того, что позиция для новой даты будет помещена в вектор, а не в список. (conj сохраняет тип, но он должен знать тип: (conj nil :a) дает (:a)).

Чтобы превратить счета в атом:

(def accounts (atom (hash-map ...)))

Я замечаю, что ваши остатки не верны в любом случае. Но если вы обновляете их, обязательно сделайте это в том же swap! функция.

Чтобы ответить на ваш вопрос "Есть (def m (update-in m ...)) хорошая практика?". Определенно не внутри defn, Если вы думаете поставить def внутри defn использовать let вместо. Вне defn было бы хорошо иметь def что обновляет другой def это имеет другое имя, так что (def m2 (update-in m1 ...)),

Другие вопросы по тегам