Clojure : 해시 맵에서 항목의 하위 집합에 함수를 어떻게 적용합니까?

StackOverflow https://stackoverflow.com/questions/1638854

  •  08-07-2019
  •  | 
  •  

문제

나는 clojure와 이것을하는 방법을 알아 내려고하지 않습니다.

해시 맵의 키의 하위 집합에 대한 새로운 해시 맵을 만들고 싶습니다. 이것을하는 가장 좋은 방법은 무엇입니까?

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

그런 다음 같은 맵이 생성됩니다

{:hello "WORLD" :try "This" :foo "BAR"}
도움이 되었습니까?

해결책

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

고장:

그것은 내부를 보는 데 도움이됩니다. Clojure에서 해시 맵은 기능처럼 작용합니다. 인수로 열쇠가있는 함수처럼 부르면 해당 키와 관련된 값이 반환됩니다. 따라서 단일 키가 주어지면 해당 키의 현재 값은 다음을 통해 얻을 수 있습니다.

(some-map some-key)

우리는 오래된 값을 취하고 일부 기능을 호출하여 새로운 값으로 변경하고 싶습니다. f 그들에게. 따라서 단일 키가 주어지면 새로운 값은 다음과 같습니다.

(f (some-map some-key))

우리는이 새로운 값을 해시 맵 에서이 키와 연관시켜 기존 값을 대체하고자합니다. 이것이 무엇입니다 assoc 하다:

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

( "교체"는 단일 해시 맵 객체를 변호하지 않기 때문에 겁에 질린다. assoc. 해시 맵이 지속적이고 공유 구조이기 때문에 이것은 Clojure에서 여전히 빠르고 효율적입니다. assoc 그들을.)

우리는 반복적으로 필요합니다 assoc 우리지도에 새로운 값, 한 번에 하나의 키. 따라서 우리는 일종의 루핑 구조가 필요합니다. 우리가 원하는 것은 원래 해시 맵과 단일 키부터 시작한 다음 해당 키의 값을 "업데이트"하는 것입니다. 그런 다음 새로운 해시 맵과 다음 키를 가져 와서 다음 키의 값을 "업데이트"합니다. 그리고 우리는 한 번에 하나씩 모든 키에 대해 이것을 반복하고 마침내 우리가 "누적 된"해시 맵을 반환합니다. 이것이 무엇입니다 reduce 하다.

  • 첫 번째 논쟁 reduce 두 가지 인수를 취하는 함수 : "축적기"값,이 값은 우리가 계속 "업데이트"하는 값입니다. 그리고 한 번의 반복에 사용 된 단일 논증은 축적을 수행합니다.
  • 두 번째 논쟁 reduce 초기 값은 이것에 대한 첫 번째 주장으로 전달 되는가 fn.
  • 세 번째 논쟁 reduce 이것에 대한 두 번째 논쟁으로 전달되는 주장의 모음입니다. fn, 한번에 한.

그래서:

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

fn-to-update-values-in-our-map 그냥입니다 assoc 위의 진술, 익명 기능으로 래핑 :

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

그래서 그것을 연결합니다 reduce:

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

Clojure에는 익명 기능을 작성할 수있는 속기가 있습니다. #(...) 익명입니다 fn 단일 형태로 구성됩니다 %1 익명 함수에 대한 첫 번째 인수에 구속됩니다. %2 두 번째로, 그래서 우리 fn 위에서부터 동등하게 다음과 같이 쓸 수 있습니다.

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

이것은 우리에게 제공합니다 :

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

다른 팁

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

예제 전화

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

이것이 도움이되기를 바랍니다.

다음은 작동하는 것 같습니다.

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

더 좋은 방법이있을 수 있습니다. 아마도 누군가가 멋진 원 라이너를 생각해 낼 수있을 것입니다 :)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top