2017-02-21 64 views
0

我想实现它映射了地图和更新值的序列的功能时谓词匹配更快/更习惯性的方式来实现“地图时”?

这是第一个工作草案:

(defn update-if 
    ([m k pred f] 
    (let [init (get m k)] 
    (if (and (not-nil? init) (pred init)) 
      (update m k f) 
      m))) 
    ([m bindings] 
    (reduce-kv 
    (fn [agg k v] 
     (let [[pred f] v] 
     (update-if agg k pred f))) 
    m bindings))) 

(update-if {:a 1 :b 2} {:a [even? inc] :b [even? dec]}) ;; ==> {:a 1 :b 1} 
(update-if {:a 1 :b 2} :b even? dec) ;; ==> {:a 1 :b 1} 

(defn map-when 
    "Walks a collection of associative collections 
    and applies functions based on predicates 
    Output : 
    (map-when {:a [even? inc] :b [nan? zero]} '({:a 1 :b NaN} {:a 2 :b 7} {:a 4 :b NaN})) 
    = 
    ({:a 1 :b 0} {:a 3 :b 7} {:a 5 :b 0})" 
    ([bindings data] 
    (reduce 
    (fn [acc row] 
     (conj acc (update-if row bindings))) 
    '() data)) 
    ([pred f data] 
    (map 
    (fn [x] 
     (if (and (not-nil? x) (pred x)) 
      (f x) 
      x)) 
    data))) 

不比零?检查很重要(在这里),因为这意味着数据丢失。 该功能需要大约2秒才能在100万个随机{:a :b}地图(包括随机发生)上执行此操作。

我觉得奇怪的是,在核心/核心相关的库中没有这个函数存在。 有没有一些性能提示来改善这一点?我试图短暂的,但它不会对空列表工作'()

感谢

回答

2

你应该look at the specter library。它可能有你在找什么。例如:

(def data {:a [{:aa 1 :bb 2} 
       {:cc 3}] 
      :b [{:dd 4}]}) 

;; Manual Clojure 
(defn map-vals [m afn] 
    (->> m (map (fn [[k v]] [k (afn v)])) (into {}))) 

(map-vals data 
    (fn [v] 
    (mapv 
     (fn [m] 
     (map-vals 
      m 
      (fn [v] (if (even? v) (inc v) v)))) 
     v))) 

;; Specter 
(transform [MAP-VALS ALL MAP-VALS even?] inc data) 
1

生成只是必要的拉姆达最大化重用性。

(defn cond-update-fn [clauses] 
    (fn [m] 
    (reduce (fn [m [k [pred f]]] 
       (cond-> m 
        (and (contains? m k) 
         (pred (get m k))) (update k f))) 
     m 
     clauses))) 

如果您preds和FNS是在编译时已知编写宏代替(左为读者做练习)给出因为没有预解码迭代开销更高的性能。


重用在任何上下文中:

(def input [{:a 42, :b 42} {:a 42,:b 43}]) 

(def cond-update 
    (cond-update-fn {:a [even? inc] 
        :b [odd? dec]})) 

(map cond-update input) 

;-> ({:a 43, :b 42} {:a 43, :b 42}) 

;; Transducer 
(into [] (map cond-update) input) 

;-> [{:a 43, :b 42} {:a 43, :b 42}] 

;; Standalone 

(cond-update {:a 32}) 
;-> {:a 33}