diff options
author | Stuart Sierra <mail@stuartsierra.com> | 2009-06-02 20:25:00 +0000 |
---|---|---|
committer | Stuart Sierra <mail@stuartsierra.com> | 2009-06-02 20:25:00 +0000 |
commit | 016af1ddecea945f0ae9e6354e2bd006efcd7a04 (patch) | |
tree | 4e91bbe3500231802b7ebf4dea6989937ce001dc /src/clojure/contrib | |
parent | 62976a684da2984bec184324dfb3269b7de916e5 (diff) |
fnmap.clj: added new library; maps with custom implementations
Diffstat (limited to 'src/clojure/contrib')
-rw-r--r-- | src/clojure/contrib/fnmap.clj | 36 | ||||
-rw-r--r-- | src/clojure/contrib/fnmap/PersistentFnMap.clj | 67 | ||||
-rw-r--r-- | src/clojure/contrib/test_contrib/fnmap.clj | 39 |
3 files changed, 142 insertions, 0 deletions
diff --git a/src/clojure/contrib/fnmap.clj b/src/clojure/contrib/fnmap.clj new file mode 100644 index 00000000..f9cfc7c5 --- /dev/null +++ b/src/clojure/contrib/fnmap.clj @@ -0,0 +1,36 @@ +;;; fnmap.clj: maps that dispatch get/assoc to functions + +;; Copyright (c) Stuart Sierra, 2008. 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 "Stuart Sierra" + :doc "Maps that dispatch get/assoc to user-defined functions. + + Note: requires AOT-compilation"} + clojure.contrib.fnmap + (:require clojure.contrib.fnmap.PersistentFnMap)) + +(defn fnmap + "Creates a fnmap, or functional map. A fnmap behaves like an + ordinary Clojure map, except that calls to get and assoc are + filtered through user-defined getter and setter functions, which + operate on an internal map. + + (getter m key) should return a value for key. + + (setter m key value) should assoc key with value and return a new + map for m. + + All other map operations are passed through to the internal map." + ([getter setter] (clojure.contrib.fnmap.PersistentFnMap/create getter setter)) + ([getter setter & keyvals] + (apply assoc + (clojure.contrib.fnmap.PersistentFnMap/create getter setter) + keyvals))) + diff --git a/src/clojure/contrib/fnmap/PersistentFnMap.clj b/src/clojure/contrib/fnmap/PersistentFnMap.clj new file mode 100644 index 00000000..c340c74e --- /dev/null +++ b/src/clojure/contrib/fnmap/PersistentFnMap.clj @@ -0,0 +1,67 @@ +;; PersistentFnMap.clj: implementation for clojure.contrib.fnmap + +;; Copyright (c) Stuart Sierra, 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. + + +;; Thanks to Meikel Brandmeyer for his work on lazymap, which made +;; this implementation easier. + + +(ns clojure.contrib.fnmap.PersistentFnMap + (:gen-class :extends clojure.lang.APersistentMap + :state state + :init init + :constructors {[clojure.lang.IPersistentMap] [], + [clojure.lang.IPersistentMap clojure.lang.IPersistentMap] [clojure.lang.IPersistentMap]})) + +(defn -init + ([theMap] [[] theMap]) + ([theMap metadata] [[metadata] theMap])) + +(defn create [getter setter] + (clojure.contrib.fnmap.PersistentFnMap. + {::getter getter ::setter setter})) + +;; IPersistentMap + +(defn -assoc [this key value] + (clojure.contrib.fnmap.PersistentFnMap. + ((::setter (. this state)) (. this state) key value))) + +;; Associative + +(defn- -containsKey [this key] + (not (nil? ((::getter (. this state)) this key)))) + +(defn- -entryAt [this key] + (clojure.lang.MapEntry. key ((::getter (. this state)) (. this state) key))) + + +(defn -valAt [this key] + ((::getter (. this state)) (. this state) key)) + +;; Iterable + +(defn -iterator [this] + (.. this state iterator)) + +;; IPersistentCollection + +(defn -count [this] + (count (. this state))) + +(defn -seq [this] + (seq (. this state))) + +(defn -cons [this that] + (.. this state (cons this that))) + +(defn -empty [this] + (.. this state empty)) + diff --git a/src/clojure/contrib/test_contrib/fnmap.clj b/src/clojure/contrib/test_contrib/fnmap.clj new file mode 100644 index 00000000..ccd7a54a --- /dev/null +++ b/src/clojure/contrib/test_contrib/fnmap.clj @@ -0,0 +1,39 @@ +(ns clojure.contrib.test-contrib.fnmap + (:use clojure.contrib.fnmap + clojure.contrib.test-is)) + +(deftest acts-like-map + (let [m1 (fnmap get assoc :key1 1 :key2 2)] + (are (= _2 (get m1 _1)) + :key1 1 + :key2 2 + :nonexistent-key nil) + (are (= _2 (_1 m1)) + :key1 1 + :key2 2 + :nonexistent-key nil) + (let [m2 (assoc m1 :key3 3 :key4 4)] + (are (= _2 (get m2 _1)) + :key1 1 + :key2 2 + :key3 3 + :key4 4 + :nonexistent-key nil)))) + +(defn assoc-validate [m key value] + (if (integer? value) + (assoc m key value) + (throw (Exception. "Only integers allowed in this map!")))) + +(deftest validators + (let [m (fnmap get assoc-validate)] + (is (= 2 (:key2 (assoc m :key2 2)))) + (is (thrown? Exception (assoc m :key3 3.14))))) + +(defn get-transform [m key] + (when-let [value (m key)] + (- value))) + +(deftest transforms + (let [m (fnmap get-transform assoc)] + (is (= -2 (:key2 (assoc m :key2 2)))))) |