diff options
Diffstat (limited to 'src/clojure/contrib/accumulators.clj')
-rw-r--r-- | src/clojure/contrib/accumulators.clj | 324 |
1 files changed, 0 insertions, 324 deletions
diff --git a/src/clojure/contrib/accumulators.clj b/src/clojure/contrib/accumulators.clj deleted file mode 100644 index dcd03dd1..00000000 --- a/src/clojure/contrib/accumulators.clj +++ /dev/null @@ -1,324 +0,0 @@ -;; Accumulators - -;; by Konrad Hinsen -;; last updated May 19, 2009 - -;; This module defines various accumulators (list, vector, map, -;; sum, product, counter, and combinations thereof) with a common -;; interface defined by the multimethods add and combine. -;; For each accumulator type, its empty value is defined in this module. -;; Applications typically use this as a starting value and add data -;; using the add multimethod. - -;; Copyright (c) Konrad Hinsen, 2009. All rights reserved. The use -;; and distribution terms for this software are covered by the Eclipse -;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) -;; which can be found in the file epl-v10.html at the root of this -;; distribution. By using this software in any fashion, you are -;; agreeing to be bound by the terms of this license. You must not -;; remove this notice, or any other, from this software. - -(ns - #^{:author "Konrad Hinsen" - :doc "A generic accumulator interface and implementations of various - accumulators."} - clojure.contrib.accumulators - (:refer-clojure :exclude (deftype)) - (:use [clojure.contrib.types :only (deftype)]) - (:use [clojure.contrib.def :only (defvar defvar- defmacro-)]) - (:require [clojure.contrib.generic.arithmetic :as ga])) - -(defmulti add - "Add item to the accumulator acc. The exact meaning of adding an - an item depends on the type of the accumulator." - {:arglists '([acc item])} - (fn [acc item] (type acc))) - -(defn add-items - "Add all elements of a collection coll to the accumulator acc." - [acc items] - (reduce add acc items)) - -(defmulti combine - "Combine the values of the accumulators acc1 and acc2 into a - single accumulator of the same type." - {:arglists '([& accs])} - (fn [& accs] (type (first accs)))) - -; -; An ::accumulator type tag is attached to tbe built-in types -; when used as accumulators, and new types are derived from it. -; Multimethods add and combine for ::accumulator sub-dispatch on class. -; We also define generic addition as the combine operation. -; -(let [meta-map {:type ::accumulator}] - (defn- with-acc-tag - [x] - (with-meta x meta-map))) - -(defmethod add ::accumulator - [a e] - ((get-method add (class a)) a e)) - -(defmethod combine ::accumulator - [& as] - (apply (get-method combine (class (first as))) as)) - -(defmethod ga/+ ::accumulator - [x y] - (combine x y)) - -; -; Vector accumulator -; -(defvar empty-vector (with-acc-tag []) - "An empty vector accumulator. Adding an item appends it at the end.") - -(defmethod combine clojure.lang.IPersistentVector - [& vs] - (with-acc-tag (vec (apply concat vs)))) - -(defmethod add clojure.lang.IPersistentVector - [v e] - (with-acc-tag (conj v e))) - -; -; List accumulator -; -(defvar empty-list (with-acc-tag '()) - "An empty list accumulator. Adding an item appends it at the beginning.") - -(defmethod combine clojure.lang.IPersistentList - [& vs] - (with-acc-tag (apply concat vs))) - -(defmethod add clojure.lang.IPersistentList - [v e] - (with-acc-tag (conj v e))) - -; -; Queue accumulator -; -(defvar empty-queue (with-acc-tag clojure.lang.PersistentQueue/EMPTY) - "An empty queue accumulator. Adding an item appends it at the end.") - -(defmethod combine clojure.lang.PersistentQueue - [& vs] - (add-items (first vs) (apply concat (rest vs)))) - -(defmethod add clojure.lang.PersistentQueue - [v e] - (with-acc-tag (conj v e))) - -; -; Set accumulator -; -(defvar empty-set (with-acc-tag #{}) - "An empty set accumulator.") - -(defmethod combine (class empty-set) - [& vs] - (with-acc-tag (apply clojure.set/union vs))) - -(defmethod add (class empty-set) - [v e] - (with-acc-tag (conj v e))) - -; -; String accumulator -; -(defvar empty-string "" - "An empty string accumulator. Adding an item (string or character) - appends it at the end.") - -(defmethod combine java.lang.String - [& vs] - (apply str vs)) - -(defmethod add java.lang.String - [v e] - (str v e)) - -; -; Map accumulator -; -(defvar empty-map (with-acc-tag {}) - "An empty map accumulator. Items to be added must be [key value] pairs.") - -(defmethod combine clojure.lang.IPersistentMap - [& vs] - (with-acc-tag (apply merge vs))) - -(defmethod add clojure.lang.IPersistentMap - [v e] - (with-acc-tag (conj v e))) - -; -; Numerical accumulators: sum, product, minimum, maximum -; -(defmacro- defacc - [name op empty doc-string] - (let [type-tag (keyword (str *ns*) (str name)) - empty-symbol (symbol (str "empty-" name))] - `(let [op# ~op] - (deftype ~type-tag ~name - (fn [~'x] {:value ~'x}) - (fn [~'x] (list (:value ~'x)))) - (derive ~type-tag ::accumulator) - (defvar ~empty-symbol (~name ~empty) ~doc-string) - (defmethod combine ~type-tag [& vs#] - (~name (apply op# (map :value vs#)))) - (defmethod add ~type-tag [v# e#] - (~name (op# (:value v#) e#)))))) - -(defacc sum + 0 - "An empty sum accumulator. Only numbers can be added.") - -(defacc product * 1 - "An empty sum accumulator. Only numbers can be added.") - -; The empty maximum accumulator should have value -infinity. -; This is represented by nil and taken into account in an -; adapted max function. In the minimum accumulator, nil is -; similarly used to represent +infinity. - -(defacc maximum (fn [& xs] - (when-let [xs (seq (filter identity xs))] - (apply max xs))) - nil - "An empty maximum accumulator. Only numbers can be added.") - -(defacc minimum (fn [& xs] - (when-let [xs (seq (filter identity xs))] - (apply min xs))) - nil - "An empty minimum accumulator. Only numbers can be added.") - -; -; Numeric min-max accumulator -; (combination of minimum and maximum) -; -(deftype ::min-max min-max - (fn [min max] {:min min :max max}) - (fn [mm] (list (:min mm) (:max mm)))) - -(derive ::min-max ::accumulator) - -(defvar empty-min-max (min-max nil nil) - "An empty min-max accumulator, combining minimum and maximum. - Only numbers can be added.") - -(defmethod combine ::min-max - [& vs] - (let [total-min (apply min (map :min vs)) - total-max (apply max (map :max vs))] - (min-max total-min total-max))) - -(defmethod add ::min-max - [v e] - (let [min-v (:min v) - max-v (:max v) - new-min (if (nil? min-v) e (min min-v e)) - new-max (if (nil? max-v) e (max max-v e))] - (min-max new-min new-max))) - -; -; Mean and variance accumulator -; -(deftype ::mean-variance mean-variance) - -(derive ::mean-variance ::accumulator) - -(defvar empty-mean-variance (mean-variance {:n 0 :mean 0 :variance 0}) - "An empty mean-variance accumulator, combining sample mean and - sample variance. Only numbers can be added.") - -(defmethod combine ::mean-variance - ([mv] - mv) - - ([mv1 mv2] - (let [{n1 :n mean1 :mean var1 :variance} mv1 - {n2 :n mean2 :mean var2 :variance} mv2 - n (+ n1 n2) - mean (/ (+ (* n1 mean1) (* n2 mean2)) n) - sq #(* % %) - c (+ (* n1 (sq (- mean mean1))) (* n2 (sq (- mean mean2)))) - var (if (< n 2) - 0 - (/ (+ c (* (dec n1) var1) (* (dec n2) var2)) (dec n)))] - (mean-variance {:n n :mean mean :variance var}))) - - ([mv1 mv2 & mvs] - (reduce combine (combine mv1 mv2) mvs))) - -(defmethod add ::mean-variance - [mv x] - (let [{n :n mean :mean var :variance} mv - n1 (inc n) - d (- x mean) - new-mean (+ mean (/ d n1)) - new-var (if (zero? n) 0 (/ (+ (* (dec n) var) (* d (- x new-mean))) n))] - (mean-variance {:n n1 :mean new-mean :variance new-var}))) - -; -; Counter accumulator -; -(deftype ::counter counter) - -(derive ::counter ::accumulator) - -(defvar empty-counter (counter {}) - "An empty counter accumulator. Its value is a map that stores for - every item the number of times it was added.") - -(defmethod combine ::counter - [v & vs] - (letfn [(add-item [cntr [item n]] - (assoc cntr item (+ n (get cntr item 0)))) - (add-two [c1 c2] (reduce add-item c1 c2))] - (reduce add-two v vs))) - -(defmethod add ::counter - [v e] - (assoc v e (inc (get v e 0)))) - -; -; Counter accumulator with total count -; -(deftype ::counter-with-total counter-with-total) -(derive ::counter-with-total ::counter) - -(defvar empty-counter-with-total - (counter-with-total {:total 0}) - "An empty counter-with-total accumulator. It works like the counter - accumulator, except that the total number of items added is stored as the - value of the key :total.") - -(defmethod add ::counter-with-total - [v e] - (assoc v e (inc (get v e 0)) - :total (inc (:total v)))) - -; -; Accumulator n-tuple -; -(deftype ::tuple acc-tuple) - -(derive ::tuple ::accumulator) - -(defn empty-tuple - "Returns an accumulator tuple with the supplied empty-accumulators - as its value. Accumulator tuples consist of several accumulators that - work in parallel. Added items must be sequences whose number of elements - matches the number of sub-accumulators." - [empty-accumulators] - (acc-tuple (into [] empty-accumulators))) - -(defmethod combine ::tuple - [& vs] - (acc-tuple (vec (map combine vs)))) - -(defmethod add ::tuple - [v e] - (acc-tuple (vec (map add v e)))) |