[英]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.