Mercurial > lasercutter
view src/clojure/contrib/accumulators.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line source
1 ;; Accumulators3 ;; by Konrad Hinsen4 ;; last updated May 19, 20096 ;; This module defines various accumulators (list, vector, map,7 ;; sum, product, counter, and combinations thereof) with a common8 ;; interface defined by the multimethods add and combine.9 ;; For each accumulator type, its empty value is defined in this module.10 ;; Applications typically use this as a starting value and add data11 ;; using the add multimethod.13 ;; Copyright (c) Konrad Hinsen, 2009. All rights reserved. The use14 ;; and distribution terms for this software are covered by the Eclipse15 ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)16 ;; which can be found in the file epl-v10.html at the root of this17 ;; distribution. By using this software in any fashion, you are18 ;; agreeing to be bound by the terms of this license. You must not19 ;; remove this notice, or any other, from this software.21 (ns22 ^{:author "Konrad Hinsen"23 :doc "A generic accumulator interface and implementations of various24 accumulators."}25 clojure.contrib.accumulators26 (:refer-clojure :exclude (deftype))27 (:use [clojure.contrib.types :only (deftype)])28 (:use [clojure.contrib.def :only (defvar defvar- defmacro-)])29 (:require [clojure.contrib.generic.arithmetic :as ga]))31 (defmulti add32 "Add item to the accumulator acc. The exact meaning of adding an33 an item depends on the type of the accumulator."34 {:arglists '([acc item])}35 (fn [acc item] (type acc)))37 (defn add-items38 "Add all elements of a collection coll to the accumulator acc."39 [acc items]40 (reduce add acc items))42 (defmulti combine43 "Combine the values of the accumulators acc1 and acc2 into a44 single accumulator of the same type."45 {:arglists '([& accs])}46 (fn [& accs] (type (first accs))))48 ;49 ; An ::accumulator type tag is attached to tbe built-in types50 ; when used as accumulators, and new types are derived from it.51 ; Multimethods add and combine for ::accumulator sub-dispatch on class.52 ; We also define generic addition as the combine operation.53 ;54 (let [meta-map {:type ::accumulator}]55 (defn- with-acc-tag56 [x]57 (with-meta x meta-map)))59 (defmethod add ::accumulator60 [a e]61 ((get-method add (class a)) a e))63 (defmethod combine ::accumulator64 [& as]65 (apply (get-method combine (class (first as))) as))67 (defmethod ga/+ ::accumulator68 [x y]69 (combine x y))71 ;72 ; Vector accumulator73 ;74 (defvar empty-vector (with-acc-tag [])75 "An empty vector accumulator. Adding an item appends it at the end.")77 (defmethod combine clojure.lang.IPersistentVector78 [& vs]79 (with-acc-tag (vec (apply concat vs))))81 (defmethod add clojure.lang.IPersistentVector82 [v e]83 (with-acc-tag (conj v e)))85 ;86 ; List accumulator87 ;88 (defvar empty-list (with-acc-tag '())89 "An empty list accumulator. Adding an item appends it at the beginning.")91 (defmethod combine clojure.lang.IPersistentList92 [& vs]93 (with-acc-tag (apply concat vs)))95 (defmethod add clojure.lang.IPersistentList96 [v e]97 (with-acc-tag (conj v e)))99 ;100 ; Queue accumulator101 ;102 (defvar empty-queue (with-acc-tag clojure.lang.PersistentQueue/EMPTY)103 "An empty queue accumulator. Adding an item appends it at the end.")105 (defmethod combine clojure.lang.PersistentQueue106 [& vs]107 (add-items (first vs) (apply concat (rest vs))))109 (defmethod add clojure.lang.PersistentQueue110 [v e]111 (with-acc-tag (conj v e)))113 ;114 ; Set accumulator115 ;116 (defvar empty-set (with-acc-tag #{})117 "An empty set accumulator.")119 (defmethod combine (class empty-set)120 [& vs]121 (with-acc-tag (apply clojure.set/union vs)))123 (defmethod add (class empty-set)124 [v e]125 (with-acc-tag (conj v e)))127 ;128 ; String accumulator129 ;130 (defvar empty-string ""131 "An empty string accumulator. Adding an item (string or character)132 appends it at the end.")134 (defmethod combine java.lang.String135 [& vs]136 (apply str vs))138 (defmethod add java.lang.String139 [v e]140 (str v e))142 ;143 ; Map accumulator144 ;145 (defvar empty-map (with-acc-tag {})146 "An empty map accumulator. Items to be added must be [key value] pairs.")148 (defmethod combine clojure.lang.IPersistentMap149 [& vs]150 (with-acc-tag (apply merge vs)))152 (defmethod add clojure.lang.IPersistentMap153 [v e]154 (with-acc-tag (conj v e)))156 ;157 ; Numerical accumulators: sum, product, minimum, maximum158 ;159 (defmacro- defacc160 [name op empty doc-string]161 (let [type-tag (keyword (str *ns*) (str name))162 empty-symbol (symbol (str "empty-" name))]163 `(let [op# ~op]164 (deftype ~type-tag ~name165 (fn [~'x] {:value ~'x})166 (fn [~'x] (list (:value ~'x))))167 (derive ~type-tag ::accumulator)168 (defvar ~empty-symbol (~name ~empty) ~doc-string)169 (defmethod combine ~type-tag [& vs#]170 (~name (apply op# (map :value vs#))))171 (defmethod add ~type-tag [v# e#]172 (~name (op# (:value v#) e#))))))174 (defacc sum + 0175 "An empty sum accumulator. Only numbers can be added.")177 (defacc product * 1178 "An empty sum accumulator. Only numbers can be added.")180 ; The empty maximum accumulator should have value -infinity.181 ; This is represented by nil and taken into account in an182 ; adapted max function. In the minimum accumulator, nil is183 ; similarly used to represent +infinity.185 (defacc maximum (fn [& xs]186 (when-let [xs (seq (filter identity xs))]187 (apply max xs)))188 nil189 "An empty maximum accumulator. Only numbers can be added.")191 (defacc minimum (fn [& xs]192 (when-let [xs (seq (filter identity xs))]193 (apply min xs)))194 nil195 "An empty minimum accumulator. Only numbers can be added.")197 ;198 ; Numeric min-max accumulator199 ; (combination of minimum and maximum)200 ;201 (deftype ::min-max min-max202 (fn [min max] {:min min :max max})203 (fn [mm] (list (:min mm) (:max mm))))205 (derive ::min-max ::accumulator)207 (defvar empty-min-max (min-max nil nil)208 "An empty min-max accumulator, combining minimum and maximum.209 Only numbers can be added.")211 (defmethod combine ::min-max212 [& vs]213 (let [total-min (apply min (map :min vs))214 total-max (apply max (map :max vs))]215 (min-max total-min total-max)))217 (defmethod add ::min-max218 [v e]219 (let [min-v (:min v)220 max-v (:max v)221 new-min (if (nil? min-v) e (min min-v e))222 new-max (if (nil? max-v) e (max max-v e))]223 (min-max new-min new-max)))225 ;226 ; Mean and variance accumulator227 ;228 (deftype ::mean-variance mean-variance)230 (derive ::mean-variance ::accumulator)232 (defvar empty-mean-variance (mean-variance {:n 0 :mean 0 :variance 0})233 "An empty mean-variance accumulator, combining sample mean and234 sample variance. Only numbers can be added.")236 (defmethod combine ::mean-variance237 ([mv]238 mv)240 ([mv1 mv2]241 (let [{n1 :n mean1 :mean var1 :variance} mv1242 {n2 :n mean2 :mean var2 :variance} mv2243 n (+ n1 n2)244 mean (/ (+ (* n1 mean1) (* n2 mean2)) n)245 sq #(* % %)246 c (+ (* n1 (sq (- mean mean1))) (* n2 (sq (- mean mean2))))247 var (if (< n 2)248 0249 (/ (+ c (* (dec n1) var1) (* (dec n2) var2)) (dec n)))]250 (mean-variance {:n n :mean mean :variance var})))252 ([mv1 mv2 & mvs]253 (reduce combine (combine mv1 mv2) mvs)))255 (defmethod add ::mean-variance256 [mv x]257 (let [{n :n mean :mean var :variance} mv258 n1 (inc n)259 d (- x mean)260 new-mean (+ mean (/ d n1))261 new-var (if (zero? n) 0 (/ (+ (* (dec n) var) (* d (- x new-mean))) n))]262 (mean-variance {:n n1 :mean new-mean :variance new-var})))264 ;265 ; Counter accumulator266 ;267 (deftype ::counter counter)269 (derive ::counter ::accumulator)271 (defvar empty-counter (counter {})272 "An empty counter accumulator. Its value is a map that stores for273 every item the number of times it was added.")275 (defmethod combine ::counter276 [v & vs]277 (letfn [(add-item [cntr [item n]]278 (assoc cntr item (+ n (get cntr item 0))))279 (add-two [c1 c2] (reduce add-item c1 c2))]280 (reduce add-two v vs)))282 (defmethod add ::counter283 [v e]284 (assoc v e (inc (get v e 0))))286 ;287 ; Counter accumulator with total count288 ;289 (deftype ::counter-with-total counter-with-total)290 (derive ::counter-with-total ::counter)292 (defvar empty-counter-with-total293 (counter-with-total {:total 0})294 "An empty counter-with-total accumulator. It works like the counter295 accumulator, except that the total number of items added is stored as the296 value of the key :total.")298 (defmethod add ::counter-with-total299 [v e]300 (assoc v e (inc (get v e 0))301 :total (inc (:total v))))303 ;304 ; Accumulator n-tuple305 ;306 (deftype ::tuple acc-tuple)308 (derive ::tuple ::accumulator)310 (defn empty-tuple311 "Returns an accumulator tuple with the supplied empty-accumulators312 as its value. Accumulator tuples consist of several accumulators that313 work in parallel. Added items must be sequences whose number of elements314 matches the number of sub-accumulators."315 [empty-accumulators]316 (acc-tuple (into [] empty-accumulators)))318 (defmethod combine ::tuple319 [& vs]320 (acc-tuple (vec (map combine vs))))322 (defmethod add ::tuple323 [v e]324 (acc-tuple (vec (map add v e))))