2013-04-13 17 views
6

Sto avvolgendo la mia mente attorno allo stato in Clojure. Vengo da lingue in cui lo stato può essere mutato. Ad esempio, in Python, posso creare un dizionario, inserire alcune string => coppie di interi all'interno, quindi andare sul dizionario e incrementare i valori.Come aumentare i valori in una mappa

Come dovrei farlo in Clojure idiomatico?

risposta

1

Basta produce una nuova mappa e utilizzarla:

(def m {:a 3 :b 4}) 

(apply merge 
    (map (fn [[k v]] {k (inc v) }) m)) 

; {:b 5, :a 4} 
+0

Che ne dici di un valore? Quindi cosa succede se voglio {a: 7: b 4}. Cosa succede se ho una mappa di mappe di interi e voglio incrementare il valore di say key1 => subkey1 => integer ++? –

+1

@DavidWilliams (update-in my-map [: b] inc) ;; => {: a 1,: b 3} –

+0

Ottenuto, questo crea una nuova mappa, corretta? Penso che questo renda difficile mantenere lo stato. Ad esempio, per creare un classificatore bayes, ho bisogno di tenere costantemente aggiornati i racconti delle cose in una struttura multilivello. key1 => subkey1 => intero. Avrei bisogno di fare qualche dosync e scambiare la magia? –

7
(def my-map {:a 1 :b 2}) 
(zipmap (keys my-map) (map inc (vals my-map))) 
;;=> {:b 3, :a 2} 

Per aggiornare un solo valore per chiave:

(update-in my-map [:b] inc) ;;=> {:a 1, :b 3} 

Dal Clojure 1.7 è anche possibile utilizzare update:

(update my-map :b inc) 
2

Per aggiornare più valori, è anche possibile sfruttare la possibilità di ridurre l'acquisizione di un accumulatore già pieno e l'applicazione di una funzione su questo e su tutti i membri della raccolta seguente.

=> (reduce (fn [a k] (update-in a k inc)) {:a 1 :b 2 :c 3 :d 4} [[:a] [:c]]) 
{:a 2, :c 4, :b 2, :d 4} 

essere a conoscenza delle chiavi che devono essere racchiusi in vettori, ma si può ancora fare più update-in in strutture nidificate come l'aggiornamento originale.

Se hai fatto una funzione generalizzata, è potrebbe avvolgere automaticamente un vettore su una chiave provandola con coll ?:

(defn multi-update-in 
    [m v f & args] 
     (reduce 
     (fn [acc p] (apply 
         (partial update-in acc (if (coll? p) p (vector p)) f) 
         args)) m v)) 

che consentirebbe a livello singolo/aggiornamenti chiave senza la necessità di avvolgere le chiavi vettori

=> (multi-update-in {:a 1 :b 2 :c 3 :d 4} [:a :c] inc) 
{:a 2, :c 4, :b 2, :d 4} 

, ma ancora in grado di fare gli aggiornamenti nidificati

(def people 
    {"keith" {:age 27 :hobby "needlefelting"} 
    "penelope" {:age 39 :hobby "thaiboxing"} 
    "brian" {:age 12 :hobby "rocket science"}}) 

=> (multi-update-in people [["keith" :age] ["brian" :age]] inc) 
    {"keith" {:age 28, :hobby "needlefelting"}, 
    "penelope" {:age 39, :hobby "thaiboxing"}, 
    "brian" {:age 13, :hobby "rocket science"}} 
0

ho accarezzato la stessa idea, così mi si avvicinò con:

(defn remap 
    "returns a function which takes a map as argument 
    and applies f to each value in the map" 
    [f] 
    #(into {} (map (fn [[k v]] [k (f v)]) %))) 

((remap inc) {:foo 1}) 
;=> {:foo 2} 

o

(def inc-vals (remap inc)) 

(inc-vals {:foo 1}) 
;=> {:foo 2} 
Problemi correlati