2

我试图想出一种方法来通过将另一个映射中的函数应用于每个值来转换 clojure 哈希映射。这是我到目前为止所拥有的:

(defn transform-map [m fm]
  (into {} (for [[k v] m]
    (let [mfn (get fm k identity)] [k (mfn v)])))

(def m {:a 0 :b 0 :c 0 :d 0})
(def fm {:a inc :b dec :c identity})
(transform-map m fm)  ;=> {:a 1, :b -1, :c 0, :d 0}

这很好用,但前提是每个函数都采用一个参数,即键的当前值。如果我想在我的函数映射中放置一个函数,该函数使用的值不是同一个键中的值,该怎么办?例如,假设我想将键的总和:a放入:b键中:d

我可以尝试类似:

(assoc fm :d (fn[a b] (+ a b)))

但是有没有办法可以改变我的transform-map函数,以便在该函数调用中使用适当的参数?

4

2 回答 2

4

我建议分解函数以及它们如何应用于转换映射。下面是一个例子来说明:

;; functions which take some params and return a result
(defn sub [a b] (- a b))
(defn add [a b] (+ a b))

;; data map
(def data {:a 5 :b 4 :c 3})

;; transformation map key => output key, value is vector of function
;; to apply and the params that function will take from the data map
(def transformations {:a [inc :a]
                      :b [dec :b]
                      :c [add :a :b]
                      :d [sub :b :c]})

; The transformation function
(defn transform-map [m fm]
  (into {} (map (fn [[k v]]
                  [k (apply (first v)
                            ((apply juxt (rest v)) m))])
                fm)))

(transform-map data transformations)
于 2013-03-26T04:39:48.713 回答
0

一种更通用的构建方法是从初始映射开始,然后减少其上的转换函数集合:

“假设我想将键 :a 和 :b 的总和放入键 :d 中?”

user> (def m {:a 0 :b 0 :c 0 :d 0}
#'user/m 
user> (reduce #(%2 %1) m [#(assoc % :a (inc (:a %))) 
                          #(assoc % :b (inc (:b %))) 
                          #(assoc % :d (+ (:a %) (:b %)))]) 
{:a 1, :c 0, :b 1, :d 2} 

虽然这更冗长,因为您必须为每个指定操作 ( assoc) 和目标 ( :betc. ),但它可以让您表达任何转换并为您提供提供额外参数的地方。也许更重要的是,它使排序明确,而不依赖于地图的内部排序。

您可以将输入构造为 [target action] 对,以使其稍微不那么冗长:

user> (reduce (fn [result [target action]] 
                  (assoc result target (action result))) 
                 m 
                 [[:a #(inc (:a %))] 
                  [:b #(inc (:b %))] 
                  [:d #(+ (:a %) (:b %))]])
{:a 1, :c 0, :b 1, :d 2}
于 2013-03-25T23:24:57.137 回答