lein-release plugin: preparing 0.1.0 release
This commit is contained in:
parent
b7e6c36d68
commit
bc1af9cdb0
23
README.md
23
README.md
|
@ -4,6 +4,8 @@ A Clojure library designed to manipulate sparse *arrays* - multi-dimensional spa
|
|||
|
||||
Arbitrary numbers of dimensions are supported, up to limits imposed by the JVM stack.
|
||||
|
||||
[](https://clojars.org/sparse-array)
|
||||
|
||||
## Conventions:
|
||||
|
||||
### Sparse arrays
|
||||
|
@ -32,7 +34,26 @@ Thus an array with a single value 'hello' at coordinates x = 3, y = 4, z = 5 wou
|
|||
}
|
||||
```
|
||||
|
||||
At the present stage of development, where the expectations of an operation are violated, `nil` is returned and no exception is thrown. However, it's probable that later there will be at least the option of thowing specific exceptions, as otherwise debugging could be tricky.
|
||||
### Errors and error-reporting
|
||||
|
||||
A dynamic variable, `*safe-sparse-operations*`, is provided to handle behaviour in error conditions. If this is `false`, bad data will generally not cause an exception to be thrown, and corrupt structures may be returned, thus:
|
||||
|
||||
```clojure
|
||||
(put (make-sparse-array :x :y :z) "hello" 3) ;; insufficient coordinates specified
|
||||
|
||||
=> {:dimensions 3, :coord :x, :content (:y :z), 3 {:dimensions 2, :coord :y, :content (:z), nil {:dimensions 1, :coord :z, :content :data, nil nil}}}
|
||||
```
|
||||
|
||||
However, if `*safe-sparse-operations*` is bound to `true`, exceptions will be thrown instead:
|
||||
|
||||
```clojure
|
||||
(binding [*safe-sparse-operations* true]
|
||||
(put (make-sparse-array :x :y :z) "hello" 3))
|
||||
|
||||
ExceptionInfo Expected 3 coordinates; found 1 clojure.core/ex-info (core.clj:4617)
|
||||
```
|
||||
|
||||
Sanity checking data is potentially expensive; for this reason `*safe-sparse-operations*` defaults to `false`, but you make wish to bind it to `true` especially while debugging.
|
||||
|
||||
### Dense arrays
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
(defproject sparse-array "0.1.0-SNAPSHOT"
|
||||
(defproject sparse-array "0.1.0"
|
||||
:description "A Clojure library designed to manipulate sparse *arrays* - multi-dimensional spaces accessed by indices, but containing arbitrary values rather than just numbers. For sparse spaces which contain numbers only, you're better to use a *sparse matrix* library, for example [clojure.core.matrix](https://mikera.github.io/core.matrix/)."
|
||||
:url "http://example.com/FIXME"
|
||||
:license {:name "Eclipse Public License"
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
(ns sparse-array.core)
|
||||
|
||||
(declare put get)
|
||||
|
||||
(def ^:dynamic *safe-sparse-operations*
|
||||
"Whether spase array operations should be conducted safely, with careful
|
||||
checking of data conventions and exceptions thrown if expectations are not
|
||||
met. Normally `false`."
|
||||
false)
|
||||
|
||||
(defmacro unsafe-sparse-operations?
|
||||
(defn- unsafe-sparse-operations?
|
||||
"returns `true` if `*safe-sparse-operations*` is `false`, and vice versa."
|
||||
[]
|
||||
(not (true? *safe-sparse-operations*)))
|
||||
|
@ -79,13 +81,9 @@
|
|||
sparse-array?
|
||||
(map #(x %) (filter integer? (keys x)))))))))
|
||||
|
||||
(defn put
|
||||
"Return a sparse array like this `array` but with this `value` at these
|
||||
`coordinates`. Returns `nil` if any coordinate is invalid."
|
||||
[array value & coordinates]
|
||||
(defn- unsafe-put
|
||||
[array value coordinates]
|
||||
(cond
|
||||
(and *safe-sparse-operations* (not (sparse-array? array)))
|
||||
(throw (ex-info "Sparse array expected" {:array array}))
|
||||
(every?
|
||||
#(and (integer? %) (or (zero? %) (pos? %)))
|
||||
coordinates)
|
||||
|
@ -101,37 +99,77 @@
|
|||
(or
|
||||
(array (first coordinates))
|
||||
(apply make-sparse-array (:content array)))
|
||||
(cons value (rest coordinates))))))
|
||||
*safe-sparse-operations*
|
||||
(cons value (rest coordinates))))))))
|
||||
|
||||
(defn put
|
||||
"Return a sparse array like this `array` but with this `value` at these
|
||||
`coordinates`. Returns `nil` if any coordinate is invalid."
|
||||
[array value & coordinates]
|
||||
(cond
|
||||
(nil? value)
|
||||
nil
|
||||
(unsafe-sparse-operations?)
|
||||
(unsafe-put array value coordinates)
|
||||
(not (sparse-array? array))
|
||||
(throw (ex-info "Sparse array expected" {:array array}))
|
||||
(not= (:dimensions array) (count coordinates))
|
||||
(throw
|
||||
(ex-info
|
||||
(str "Expected " (:dimensions array) " coordinates; found " (count coordinates))
|
||||
{:array array
|
||||
:coordinates coordinates}))
|
||||
(not
|
||||
(every?
|
||||
#(and (integer? %) (or (zero? %) (pos? %)))
|
||||
coordinates))
|
||||
(throw
|
||||
(ex-info
|
||||
"Coordinates must be zero or positive integers"
|
||||
{:array array
|
||||
:coordinates coordinates
|
||||
:invalid (remove #(and (pos? %) (integer? %)) coordinates)}))))
|
||||
:invalid (remove #(and (pos? %) (integer? %)) coordinates)}))
|
||||
:else
|
||||
(unsafe-put array value coordinates)
|
||||
value
|
||||
*safe-sparse-operations*))
|
||||
|
||||
(defn- unsafe-get
|
||||
;; TODO: I am CERTAIN there is a more elegant solution to this.
|
||||
[array coordinates]
|
||||
(let [v (array (first coordinates))]
|
||||
(cond
|
||||
(= :data (:content array))
|
||||
v
|
||||
(nil? v)
|
||||
nil
|
||||
:else
|
||||
(apply get (cons v (rest coordinates))))))
|
||||
|
||||
(defn get
|
||||
"Return the value in this sparse `array` at these `coordinates`."
|
||||
;; TODO: I am CERTAIN there is a more elegant solution to this.
|
||||
[array & coordinates]
|
||||
(cond
|
||||
*safe-sparse-operations*
|
||||
(cond
|
||||
(not (sparse-array? array))
|
||||
(throw (ex-info "Sparse array expected" {:array array}))
|
||||
(not (every?
|
||||
#(and (integer? %) (or (zero? %) (pos? %)))
|
||||
coordinates))
|
||||
(throw
|
||||
(ex-info
|
||||
"Coordinates must be zero or positive integers"
|
||||
{:array array
|
||||
:coordinates coordinates
|
||||
:invalid (remove #(and (pos? %) (integer? %)) coordinates)})))
|
||||
(= :data (:content array))
|
||||
(array (first coordinates))
|
||||
(unsafe-sparse-operations?)
|
||||
(unsafe-get array coordinates)
|
||||
(not (sparse-array? array))
|
||||
(throw (ex-info "Sparse array expected" {:array array}))
|
||||
(not (every?
|
||||
#(and (integer? %) (or (zero? %) (pos? %)))
|
||||
coordinates))
|
||||
(throw
|
||||
(ex-info
|
||||
"Coordinates must be zero or positive integers"
|
||||
{:array array
|
||||
:coordinates coordinates
|
||||
:invalid (remove #(and (pos? %) (integer? %)) coordinates)}))
|
||||
(not (= (:dimensions array) (count coordinates)))
|
||||
(throw
|
||||
(ex-info
|
||||
(str "Expected " (:dimensions array) " coordinates; found " (count coordinates))
|
||||
{:array array
|
||||
:coordinates coordinates}))
|
||||
:else
|
||||
(apply get (cons (array (first coordinates)) (rest coordinates)))))
|
||||
(unsafe-get array coordinates)))
|
||||
|
||||
(defn merge-sparse-arrays
|
||||
"Return a sparse array taking values from sparse arrays `a1` and `a2`,
|
||||
|
|
|
@ -101,6 +101,13 @@
|
|||
:content '(:y)
|
||||
3 {:dimensions 1 :coord :y :content :data 4 "hello"}}
|
||||
actual (get array 3 4)]
|
||||
(is (= actual expected)))
|
||||
(let [expected nil
|
||||
array {:dimensions 2,
|
||||
:coord :x,
|
||||
:content '(:y)
|
||||
3 {:dimensions 1 :coord :y :content :data 4 "hello"}}
|
||||
actual (get array 4 3)]
|
||||
(is (= actual expected))))
|
||||
(testing "put"
|
||||
(let [expected "hello"
|
||||
|
@ -113,12 +120,24 @@
|
|||
(let
|
||||
[expected "hello"
|
||||
actual (get (put (make-sparse-array :x) expected 3) 3)]
|
||||
(is (= actual expected))))
|
||||
(is (= actual expected)))
|
||||
(binding [*safe-sparse-operations* true]
|
||||
;; enabling error handling shouldn't make any difference
|
||||
(let
|
||||
[expected "hello"
|
||||
actual (get (put (make-sparse-array :x) expected 3) 3)]
|
||||
(is (= actual expected)))))
|
||||
(testing "round trip, two dimensions"
|
||||
(let
|
||||
[expected "hello"
|
||||
actual (get (put (make-sparse-array :x :y) expected 3 4) 3 4)]
|
||||
(is (= actual expected))))
|
||||
(is (= actual expected)))
|
||||
(binding [*safe-sparse-operations* true]
|
||||
;; enabling error handling shouldn't make any difference
|
||||
(let
|
||||
[expected "hello"
|
||||
actual (get (put (make-sparse-array :x :y) expected 3 4) 3 4)]
|
||||
(is (= actual expected)))))
|
||||
(testing "round trip, three dimensions"
|
||||
(let
|
||||
[expected "hello"
|
||||
|
@ -128,7 +147,29 @@
|
|||
(let
|
||||
[expected "hello"
|
||||
actual (get (put (make-sparse-array :p :q :r :s) expected 3 4 5 6) 3 4 5 6)]
|
||||
(is (= actual expected)))))
|
||||
(is (= actual expected))))
|
||||
(testing "Error handling, number of dimensions"
|
||||
(binding [*safe-sparse-operations* true]
|
||||
(is
|
||||
(thrown-with-msg?
|
||||
clojure.lang.ExceptionInfo
|
||||
#"Expected 3 coordinates; found 2"
|
||||
(put (make-sparse-array :x :y :z) "hello" 3 4)))
|
||||
(is
|
||||
(thrown-with-msg?
|
||||
clojure.lang.ExceptionInfo
|
||||
#"Expected 3 coordinates; found 4"
|
||||
(put (make-sparse-array :x :y :z) "hello" 3 4 5 6)))
|
||||
(is
|
||||
(thrown-with-msg?
|
||||
clojure.lang.ExceptionInfo
|
||||
#"Expected 3 coordinates; found 2"
|
||||
(get (make-sparse-array :x :y :z) 3 4)))
|
||||
(is
|
||||
(thrown-with-msg?
|
||||
clojure.lang.ExceptionInfo
|
||||
#"Expected 3 coordinates; found 4"
|
||||
(get (make-sparse-array :x :y :z) 3 4 5 6))))))
|
||||
|
||||
(deftest merge-test
|
||||
(testing "merge, one dimension"
|
||||
|
|
Loading…
Reference in a new issue