2017-02-21 63 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}