2017-02-21 31 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 секунд, чтобы выполнить это на 1 миллион случайных карт {: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}