2017-02-15 8 views
2

はのは、以下のようなネストされたマップがあるとしましょう。(一部のみネストされた)Clojure:ネストされたマップのすべての値に関数を適用して更新するにはどうすればよいですか?

(def mymap {:a 10 
     :b {:ba 21, :bb 22 :bc 23} 
     :c 30 
     :d {:da 41, :db 42}}) 

どのように私は、関数を適用(*%2)と言う、このマップ内のすべての値を更新することができますか?それはキーを指定しないことです。結果は次のようになります。

{:a 20, 
:b {:ba 42, :bb 44, :bc 46}, 
:c 60, 
:d {:da 82, :db 84}} 

これまでのところ、私はこの独自の機能を思い付いた:

(defn map-kv [f coll] (reduce-kv (fn [m k v] (assoc m k (f v))) (empty coll) coll)) 

しかし、私はまだ最初のレベルのキーを指定する必要があり、すべてに適用することはできません第1レベルおよび第2レベルのキー値。

+2

であなたが接近していた実装することができます。条件と再帰呼び出しを追加するだけでいいです: '(defn map-kv [f coll](reduce-kv(fn [mkv] if(map?v)(assoc mk(map-kv fv)) (fv))))(空のcoll)coll)) '。しかし、@ alan-thompsonによる解決策は間違いなく簡単です/慣用的です。 – jsonmurphy

答えて

3

はpostwalkし、アランが述べたように、再帰的にマップを探索し、すべてのキーを更新することは簡単です。 Clojureには、マップ内のすべての値に関数を単に適用するfmapという関数が用意されています。使用するには:

をproject.cljでは、この依存関係を宣言:

[org.clojure/algo.generic "0.1.2"] 

そして、あなたのコード内に、そして必要:

(require '[clojure.algo.generic.functor :as f :only [fmap]]) 

は、その後、あなたのマップを再帰的に歩いていく機能を定義します。

(defn fmap* 
    [f m] 
    (f/fmap #(if (map? %) 
      (fmap* f %) 
      (f %)) 
      m)) 

(fmap* 
    (partial * 2) ;; double every number 
    {:a 21 :b {:x 11 :y 22 :z {:p 100 :q 200}}}) 
=> {:a 42, :b {:x 22, :y 44, :z {:p 200, :q 400}}} 

非コア関数を含める必要がない場合は、fmap (DEFNのために適合)clojure sourceから、地図上の使用:

(defn fmap [f m] 
    (into (empty m) (for [[k v] m] [k (f v)]))) 
6

あなたはpostwalk機能を確認したいことがあります。またhttps://clojuredocs.org/clojure.walk/postwalk

(def data 
    {:a 10 
    :b {:ba 21, :bb 22 :bc 23} 
    :c 30 
    :d {:da 41, :db 42}}) 

(defn tx-nums [x] 
    (if (number? x) 
    (* 2 x) 
    x)) 

(postwalk tx-nums data) => 
    {:a 20, 
    :b {:ba 42, :bb 44, :bc 46}, 
    :c 60, 
    :d {:da 82, :db 84}} 
+0

サンプルで入力データが正しくありません。私はあなたが誤って出力をコピーしたと思います。 – Scott

+0

あなたは正しかった...修正されました! –

+0

これは、キーをマップするための関数も適用します。データが{1 2、3 4}の場合、{1,4,28}の代わりに{2 4、6 8}が返されます。 – Porthos3

2

あなたはまさにトップ2のレベルを変更したい場合は、私は本当に亡霊のように、二回変換呼び出しは最も簡単で、https://github.com/nathanmarz/specter

を見ます

(->> mymap 
    (sp/transform [sp/MAP-VALS map? sp/MAP-VALS number?] #(* 2 %)) 
    (sp/transform [sp/MAP-VALS number?] #(* 2 %))) 

実際にすべてを再帰的に置き換えたい場合は、幽霊の中でウォークパートを実装することもできます。たとえば、任意の構造のすべての数値を浮動させたいと考えました。まず、walker(vector、seq、setsを扱う)を定義しなければなりませんでした。これは一般的なので、私はそれを再利用することができます。

(defprotocolpath WalkValues) 

(extend-protocolpath WalkValues 
       clojure.lang.IPersistentVector [ALL WalkValues] 
       clojure.lang.IPersistentMap [MAP-VALS WalkValues] 
       clojure.lang.IPersistentSet [ALL WalkValues] 
       clojure.lang.ISeq [ALL WalkValues] 
       Object STAY) 

が、私はそれをやったら、私はそれが

(sp/transform [sp/WalkValues integer?] float mymap) 

であるか、この例

(sp/transform [sp/WalkValues number?] #(* 2 %) mymap) 
+0

「CTRL-f」の幽霊「」うん!歩行者を実装する素晴らしい例 –

関連する問題