2009-10-28 8 views
5

Nie jestem do Clojure i próbuje dowiedzieć się, jak to zrobić.Clojure: Jak zastosować funkcję do podzestawu wpisów w haszującej mapie?

Chcę utworzyć nową mapę skrótu, która dla podzbioru kluczy w haszującej mapie stosuje funkcję do elementów. Jaki jest najlepszy sposób na zrobienie tego?

(let 
    [my-map {:hello "World" :try "This" :foo "bar"}] 
    (println (doToMap my-map [:hello :foo] (fn [k] (.toUpperCase k))) 

ta powinna następnie doprowadzić mapę z czymś jak

{:hello "WORLD" :try "This" :foo "BAR"} 

Odpowiedz

24
(defn do-to-map [amap keyseq f] 
    (reduce #(assoc %1 %2 (f (%1 %2))) amap keyseq))

Podział:

Pomaga to patrzeć inside-out. W Clojure mapy hash działają jak funkcje; jeśli wywołasz je jako funkcję z kluczem jako argumentem, zwracana jest wartość skojarzona z tym kluczem. Tak więc biorąc pod uwagę jednego klawisza, aktualna wartość dla tego klucza można uzyskać poprzez:

(some-map some-key) 

Chcemy wziąć stare wartości i zmieniać je do nowych wartości poprzez wywołanie niektórych funkcji f na nich. Tak więc biorąc pod uwagę jednego klawisza, nowa wartość będzie:

(f (some-map some-key)) 

Chcemy powiązać tę nową wartość z tego klucza w naszym hash-map „zastępując” starą wartość. To, co robi assoc:

(assoc some-map some-key (f (some-map some-key))) 

(„Replace” jest w cudzysłowie, ponieważ zastraszania-nie jesteśmy mutacji pojedynczego obiektu hash-map; wracamy nowy, niezmienny, zmieniany hash-mapy obiektów za każdym razem nazywamy assoc. to jest nadal szybki i skuteczny w Clojure ponieważ hash-mapy są trwałe i struktura akcji podczas assoc im.)

Musimy wielokrotnie assoc nowe wartości na naszej mapie, jeden klawisz naraz. Więc potrzebujemy jakiegoś rodzaju konstrukcji pętlowej. Chcemy zacząć od naszej oryginalnej mapy skrótów i pojedynczego klucza, a następnie "zaktualizować" wartość tego klucza. Następnie przechodzimy do tej nowej tablicy skrótów i następnego klucza, i "aktualizujemy" wartość tego następnego klucza. Powtarzamy to dla każdego klucza, po jednym na raz, i ostatecznie zwracamy mapę hash, którą "zgromadziliśmy". Tak właśnie robi reduce.

  • Pierwszym argumentem reduce to funkcja, która przyjmuje dwa argumenty: wartość „akumulator”, która jest wartością trzymamy „odnowienia” kółko; i pojedynczy argument użyty w jednej iteracji do zrobienia części akumulacji.
  • Drugi argument do reduce jest wartością początkową przekazaną jako pierwszy argument do tego fn.
  • Trzeci argument do reduce jest zbiorem argumentów, które należy przekazać jako drugi argument do tego fn, po jednym na raz.

Więc:

(reduce fn-to-update-values-in-our-map 
     initial-value-of-our-map 
     collection-of-keys) 

fn-to-update-values-in-our-map jest tylko oświadczenie assoc z góry, owinięte w anonimowej funkcji:

(fn [map-so-far some-key] (assoc map-so-far some-key (f (map-so-far some-key)))) 

więc podłączając go do reduce:

(reduce (fn [map-so-far some-key] (assoc map-so-far some-key (f (map-so-far some-key)))) 
     amap 
     keyseq) 

W Clojure, istnieje skrót do pisania anonimowych funkcji: #(...) to anonimowy fn składający się z pojedynczej formy, w której %1 jest związany z pierwszym argumentem funkcji anonimowej, %2 z drugim, itd. Więc nasz fn z góry może być zapisany równoważnie jako:

#(assoc %1 %2 (f (%1 %2))) 

To daje nam:

(reduce #(assoc %1 %2 (f (%1 %2))) amap keyseq) 
+1

Czy mógłbyś przekazać oświadczenie z opisem oświadczenia dla nas nowego w Clojure? –

+1

Nadzieję, że pomaga. –

+0

Ta notacja # (...) jest niesamowita. Świetna odpowiedź! –

1

Poniższy wydaje się działać:

(defn doto-map [ks f amap] 
    (into amap 
    (map (fn [[k v]] [k (f v)]) 
     (filter (fn [[k v]] (ks k)) amap)))) 

user=> (doto-map #{:hello :foo} (fn [k] (.toUpperCase k)) {:hello "World" :try "This" :foo "bar"}) 
{:hello "WORLD", :try "This", :foo "BAR"} 

Nie może być lepszy sposób to zrobić. Może ktoś może pochodzić z ładnym jednego-liner :)

4
(defn doto-map [m ks f & args] 
    (reduce #(apply update-in %1 [%2] f args) m ks)) 

Przykład połączenia

user=> (doto-map {:a 1 :b 2 :c 3} [:a :c] + 2) 
{:a 3, :b 2, :c 5} 

Nadzieja, że ​​to pomaga.

Powiązane problemy