4

を使用して、次の結果の背後にある理由を誰かが説明できますか(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を別のデータ型 (文字列など) に置き換えることもできますが、データ型 (文字列など) をマップに置き換えることはできません。データ型が既にマップである必要があるためです。

そして、これをどのように回避しますか?私は次のことを達成したいと思います:

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

4 に答える 4

6

assoc-inの上に実装されていassocます。マップを置き換えることができ、それらで動作する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は、代わりに 1 レベル浅く操作し、値だけではなくマップ全体を置き換える必要があります。

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

全体を置き換えて失いたくないマップ内に他の値がある場合は、次のように使用できupdate-inますassoc

(update-in {:foo {:bar "hello"}} [:foo] assoc :baz "hi")
;=> {:foo {:bar "hello", :baz "hi"}}
于 2013-10-30T22:01:21.300 に答える