Предварительно агрегированная структура данных в clojure

В OLAP-кубах можно очень быстро просматривать большие объемы агрегированных данных. Основная причина этого заключается в том, что данные предварительно агрегируются в операциях, которые легко комбинировать вверх (в основном +, -, среднее, стандартное, максимальное, минимальное и некоторые другие).

Как получить это «антиленивое» поведение в clojure?

Я думаю о чем-то вроде

(def world-population {:africa 4e8            ;;this is an aggregation!
                       :africa/liberia 3.4e6
                       :africa/ethiopia 7.4e7
                       ...})

Как обновить подобную структуру данных и убедиться, что родители объекта также обновлены? Нужно ли сворачивать собственную реф-реализацию?


person claj    schedule 12.03.2012    source источник


Ответы (2)


Вы можете написать рекурсивную функцию свертки как функцию более высокого порядка, например:

(defn rollup 
  ([data heirarchy func]
    (loop [top (second (first heirarchy))]
      (if (nil? (heirarchy top))
        (rollup data heirarchy func top)
        (recur (heirarchy top)))))
  ([data heirarchy func root]
    (let [children (reduce (fn [l [k v]] (if (= v root) (cons k l) l)) '() heirarchy)
          data (reduce (fn [d c] (if (d c) d (rollup d heirarchy func c))) data children)
          child-values (map data children)]
      (assoc data root (apply func child-values)))))

Который затем можно использовать с любой конкретной операцией свертки или иерархией, которая вам нравится:

(def populations { :africa/liberia 3.4e6
                   :africa/ethiopia 7.4e7})

(def geography {:africa/liberia :africa 
                :africa/ethiopia :africa
                :africa :world})

(rollup populations geography +)
=> {:africa           7.74E7, 
    :world            7.74E7, 
    :africa/ethiopia  7.4E7, 
    :africa/liberia   3400000.0}

Очевидно, что это становится более сложным, если у вас очень большие наборы данных или несколько иерархий и т. д., но этого должно быть достаточно для многих простых случаев.

person mikera    schedule 13.03.2012
comment
Это круто! Умный способ использования функций высшего порядка! География, вероятно, будет хорошим дополнением для деривата, постараюсь больше с этим. - person claj; 14.03.2012

Сохраняя свои данные в атоме, вы можете добавлять часы — по сути, обратные вызовы при обновлении атома.

Что-то вроде этого:

(def world-population (atom {:africa 4e8
                             :africa/liberia 3.4e6
                             ...}))

(add-watch word-population :population-change-key
      (fn [key ref old new]
         (prn "population change")))

Вы можете построить некоторую логику распространения событий поверх этого.

person sw1nn    schedule 12.03.2012
comment
add-watch — умный способ синхронизировать структуру! Спасибо за это! - person claj; 14.03.2012