簡體   English   中英

Clojure:使用assoc-in的結果不一致

[英]Clojure: Inconsistent results using assoc-in

有人可以解釋使用(assoc-in)后面結果背后的原因是什么?

(assoc-in {:foo {:bar {:baz "hello"}}} [:foo :bar] "world")
=> {:foo {:bar "world"}}

(assoc-in {:foo {:bar nil}} [:foo :bar :baz] "world")
=> {:foo {:bar {:baz "world"}}}

(assoc-in {:foo {:bar "hello"}} [:foo :bar :baz] "world")
=> ClassCastException java.lang.String cannot be cast to clojure.lang.Associative  clojure.lang.RT.assoc (RT.java:702)

顯然我可以用另一種數據類型(例如字符串)替換地圖甚至nil但是我不能用地圖替換數據類型(例如String),因為它需要該數據類型已經是地圖。

一個人如何解決這個問題呢? 我想實現以下目標:

(assoc-in {:foo {:bar "hello"}} [:foo :bar :baz] "world")
=> {:foo {:bar {:baz "world"}}}

assoc-in assoc之上實現。 您可以替換maps和nil因為assoc對它們起作用:

(assoc {} :foo :bar)  ;=> {:foo :bar}
(assoc nil :foo :bar) ;=> {:foo :bar}

但是assoc不適用於字符串:

(assoc "string" :foo :bar) ;=> ClassCastException

另外, assoc-in 的定義非常優雅:

(defn assoc-in
  ;; metadata elided
  [m [k & ks] v]
  (if ks
    (assoc m k (assoc-in (get m k) ks v))
    (assoc m k v)))

如果你需要替換一個無法調用assoc的值,你需要在一個較淺的層次上操作並替換整個地圖而不僅僅是值:

(assoc-in {:foo {:bar "hello"}} [:foo :bar] {:baz "world"})
;=> {:foo {:bar {:baz "world"}}}

如果地圖中還有其他值,您不想通過替換整個內容而丟失,則可以使用帶有assoc update-in

(update-in {:foo {:bar "hello"}} [:foo] assoc :baz "hi")
;=> {:foo {:bar "hello", :baz "hi"}}

推理:問題是你的:bar指向字符串“hello”而不是指向地圖。 因為你可以使用七巧板的想法

基於@jbm的答案,我查看了源代碼並得出以下解決方案(包括重新實現(assoc-in)

(defn assoc-in' [m [k & ks] v]
  (if ks
    (let [v' (get m k)
          v'' (when (map? v') v')]
      (assoc m k (assoc-in' v'' ks v)))
    (assoc m k v)))

如果有人可以驗證這個解決方案,我會很高興。

這段代碼可以解決這個問題,但我不知道它是否足以滿足您的要求

(assoc-in {:foo {:bar "hello"}} [:foo :bar] {:baz "world"})
=> {:foo {:bar {:baz "world"}}}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM