2013-04-13 17 views
6

Owijam głowę wokół stanu w Clojure. Pochodzę z języków, w których można zmutować państwo. Na przykład w Pythonie mogę utworzyć słownik, wstawić kilka ciągów => liczby całkowite wewnątrz, a następnie przejść przez słownik i zwiększyć wartości.Jak zwiększać wartości na mapie

Jak to zrobić w idiomatycznym Clojure?

Odpowiedz

1

Wystarczy produce nowa mapa i używać go:

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

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

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

Co powiesz na jedną wartość? A co jeśli chcę {a: 7: b 4}. Co zrobić, jeśli mam mapę map całkowitych i chcę zwiększyć wartość słowa klucz1 => podklucz1 => liczba całkowita ++? –

+1

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

+0

Rozumiem, to tworzy nową mapę, prawda? Myślę, że to utrudnia utrzymanie stanu. Na przykład, aby utworzyć klasyfikator typu Bayes, muszę stale aktualizować listę rzeczy w wielopoziomowej strukturze. key1 => podklucz1 => liczba całkowita. Czy powinienem wykonać som dosync i zamienić magię? –

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

Aby zaktualizować tylko jedną wartość przez klucz:

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

Od Clojure 1.7 możliwe jest również użycie update:

(update my-map :b inc) 
2

Aby zaktualizować wiele wartości, możesz również skorzystać z funkcji zmniejszania liczby pobranych już akumulatorów i zastosować do nich funkcję oraz każdy element poniższej kolekcji.

=> (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} 

Bądź świadomy klawiszy które muszą być zawarte w wektorach, ale nadal można zrobić wiele update-iny w zagnieżdżonych struktur podobnych do oryginalnej aktualizacji.

Jeśli udało ci się uogólnioną funkcję, mógłby automatycznie owinąć wektor nad kluczem sprawdzając je za pomocą pot ?:

(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)) 

która pozwoliłaby na jednopoziomowych/kLUCZ aktualizacji bez konieczności zawijania klawiszy w wektorach

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

ale nadal będą mogli zrobić zagnieżdżonych aktualizacji

(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

byłem bawiąc się tej samej idei, więc wymyśliłem:

(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} 

lub

(def inc-vals (remap inc)) 

(inc-vals {:foo 1}) 
;=> {:foo 2} 
Powiązane problemy