Paste number 93626: N-element tuples with deftype

Index of paste annotations: 1 | 2 | 3 | 4 | 5

Paste number 93626: N-element tuples with deftype
Pasted by: stuartsierra
When:7 months, 2 weeks ago
Share:Tweet this! | http://paste.lisp.org/+208Q
Channel:#clojure
Paste contents:
Raw Source | XML | Display As
;; A N-element tuple

(defmacro def-tuple-type
  "Defines a type named TupleN for a N-element tuple."
  [N]
  (assert (integer? N))
  (assert (pos? N))
  (let [NAME (symbol (str "Tuple" N))
        FIELDS (vec (map #(symbol (str "e" %)) (range 0 N)))]
    `(deftype ~NAME ~FIELDS :as ~'this

       ;; method implementations
       clojure.lang.Associative
       (~'containsKey [~'n] (and (integer? ~'n)
                                 (< -1 ~'n ~N)))
       (~'entryAt [~'k] (case ~'k
                              ~@(interleave
                                 (range 0 N)
                                 (map (fn [e] `(clojure.lang.MapEntry. ~e ~(nth FIELDS e)))
                                      (range 0 N)))))
       (~'assoc [~'k ~'v] (case ~'k
                                ~@(interleave
                                   (range 0 N)
                                   (map (fn [e] `(new ~NAME ~@(assoc FIELDS e 'v)))
                                        (range 0 N)))))

       clojure.lang.Counted
       (~'count [] ~N)

       clojure.lang.ILookup
       (~'valAt [~'n] (case ~'n
                            ~@(interleave (range 0 N) FIELDS)
                            nil))
       (~'valAt [~'n not-found#] (case ~'n
                                       ~@(interleave (range 0 N) FIELDS)
                                       not-found#))

       clojure.lang.IFn
       (~'invoke [~'n] (. ~'this valAt ~'n))
       (~'invoke [~'n not-found#] (. ~'this valAt ~'n not-found#))
  
       clojure.lang.IPersistentCollection
       (~'cons [~'x] (cons ~'x (seq ~'this)))
       (~'empty [] (new ~NAME ~@(repeat N nil)))
       (~'equiv [~'x] (and (= ~N (count ~'x))
                           ~@(map (fn [e] `(= ~(get FIELDS e) (nth ~'x ~e)))
                                  (range 0 N))))
       (~'seq [] (list ~@FIELDS))

       clojure.lang.IPersistentVector
       (~'length [] 2)
       (~'assocN [~'n ~'v] (. ~'this ~'assoc ~'n ~'v))
       ;; cons again

       clojure.lang.Indexed
       (~'nth [~'n] (. ~'this ~'valAt ~'n))

       clojure.lang.Reversible
       (~'rseq [] (reverse (. ~'this ~'seq)))

       clojure.lang.Seqable
       ;; seq again 

       clojure.lang.Sequential
       ;; (no methods)

       java.io.Serializable
       ;; (no methods)

       java.lang.Comparable
       (~'compareTo [~'x] (compare ~FIELDS ~'x))

       java.lang.Iterable
       (~'iterator [] (.. ~'this ~'seq ~'iterator))

       java.lang.Runnable
       (~'run [] nil)

       java.util.Collection

       java.util.List
       (~'size [] 2)
       (~'indexOf [~'x] (cond
                         ~@(interleave
                            (map (fn [n] `(= ~'x ~(get FIELDS n)))
                                 (range 0 N))
                            (range 0 N))
                         :else -1))
       (~'lastIndexOf [~'x] (cond
                             ~@(interleave
                                (map (fn [n] `(= ~'x ~(get FIELDS n)))
                                     (reverse (range 0 N)))
                                (reverse (range 0 N)))
                             :else -1))

       java.util.RandomAccess
       ;; (no methods)

       java.util.concurrent.Callable
       (~'call [] nil))))

(def-tuple-type 2)  ;; Tuple2
(def-tuple-type 3)  ;; Tuple3
(def-tuple-type 4)  ;; Tuple4
(def-tuple-type 5)  ;; Tuple5

(defn tuple
  "Creates and returns a new 2, 3, 4, or 5-element tuple.
  Tuples support the same methods as vectors."
  ([a b] (Tuple2 a b))
  ([a b c] (Tuple3 a b c))
  ([a b c d] (Tuple4 a b c d))
  ([a b c d e] (Tuple5 a b c d e)))


;;; TESTS SHOWING USAGE

(use 'clojure.test)

(deftest t-equality
  (is (= (tuple 8 9) [8 9])))

(deftest t-compare
  (is (= -1 (compare (tuple 7 8 9) (tuple 7 8 10))))
  (is (= 0  (compare (tuple 1 2) (tuple 1 2))))
  (is (= 0  (compare (tuple :a :b :c) [:a :b :c]))))

(deftest t-assoc
  (is (= (tuple :a :c) (assoc (tuple :a :b) 1 :c))))

(deftest t-get
  (is (= :d (get (tuple :a :b :c :d) 3)))
  (is (= :notfound (get (tuple :a :b) 3 :notfound))))

(deftest t-count
  (is (= 5 (count (tuple :a :b :c :d :e)))))

(deftest t-invoke
  (is (= :d ((tuple :a :b :c :d) 3))))


;;; MICRO-BENCHMARK

(defn performance
  "Prints micro-benchmark comparison of create/get with 
  vectors vs tuples"
  []
  (println "3-VECTORS")
  (dotimes [j 5]
    (time (dotimes [i 1000000]
            (get (vector i i i) 2))))
  (println "3-TUPLES")
  (dotimes [j 5]
    (time (dotimes [i 1000000]
            (get (tuple i i i) 2))))
  (println "5-VECTORS")
  (dotimes [j 5]
    (time (dotimes [i 1000000]
            (get (vector i i i i i) 4))))
  (println "5-TUPLES")
  (dotimes [j 5]
    (time (dotimes [i 1000000]
            (get (tuple i i i i i) 4)))))

;; 3-VECTORS
;; "Elapsed time: 875.015094 msecs"
;; "Elapsed time: 521.541171 msecs"
;; "Elapsed time: 516.788084 msecs"
;; "Elapsed time: 503.471838 msecs"
;; "Elapsed time: 501.332416 msecs"
;; 3-TUPLES
;; "Elapsed time: 106.703716 msecs"
;; "Elapsed time: 98.58277 msecs"
;; "Elapsed time: 93.24528 msecs"
;; "Elapsed time: 103.784549 msecs"
;; "Elapsed time: 98.071803 msecs"
;; 5-VECTORS
;; "Elapsed time: 652.412623 msecs"
;; "Elapsed time: 654.554363 msecs"
;; "Elapsed time: 628.67042 msecs"
;; "Elapsed time: 612.373211 msecs"
;; "Elapsed time: 610.323665 msecs"
;; 5-TUPLES
;; "Elapsed time: 157.147686 msecs"
;; "Elapsed time: 124.156052 msecs"
;; "Elapsed time: 110.723893 msecs"
;; "Elapsed time: 109.796554 msecs"
;; "Elapsed time: 109.957743 msecs"

Annotations for this paste:

Annotation number 1: N-element tuples with type declarations
Pasted by: stuartsierra
When:7 months, 2 weeks ago
Share:Tweet this! | http://paste.lisp.org/+208Q/1
Paste contents:
Raw Source | Display As
;;; A N-element tuple with type declarations

(defmacro def-tuple-type
  "Defines a type, named NAME, for a N-element tuple with elements of
  type TYPE, which may be primitive."
  ([NAME N] (def-tuple-type NAME N java.lang.Object))
  ([NAME N TYPE]
      (assert (integer? N))
      (assert (pos? N))
      (let [TAGGED-FIELDS (vec (map #(with-meta (symbol (str "e" %))
                                       {:tag TYPE})
                                    (range 0 N)))
            FIELDS (vec (map #(symbol (str "e" %)) (range 0 N)))]
        `(deftype ~NAME ~TAGGED-FIELDS :as ~'this

           ;; method implementations
           clojure.lang.Associative
           (~'containsKey [~'n] (and (integer? ~'n)
                                     (< -1 ~'n ~N)))
           (~'entryAt [~'k] (case ~'k
                                  ~@(interleave
                                     (range 0 N)
                                     (map (fn [e] `(clojure.lang.MapEntry.
                                                    ~e ~(nth FIELDS e)))
                                          (range 0 N)))))
           (~'assoc [~'k ~'v] (case ~'k
                                    ~@(interleave
                                       (range 0 N)
                                       (map (fn [e]
                                              `(new ~NAME
                                                    ~@(assoc FIELDS e 'v)))
                                            (range 0 N)))))

           clojure.lang.Counted
           (~'count [] ~N)

           clojure.lang.ILookup
           (~'valAt [~'n] (case ~'n
                                ~@(interleave (range 0 N) FIELDS)
                                nil))
           (~'valAt [~'n not-found#] (case ~'n
                                           ~@(interleave (range 0 N) FIELDS)
                                           not-found#))

           clojure.lang.IFn
           (~'invoke [~'n] (. ~'this valAt ~'n))
           (~'invoke [~'n not-found#] (. ~'this valAt ~'n not-found#))
  
           clojure.lang.IPersistentCollection
           (~'cons [~'x] (cons ~'x (seq ~'this)))
           (~'empty [] (new ~NAME ~@(repeat N nil)))
           (~'equiv [~'x] (and (= ~N (count ~'x))
                               ~@(map (fn [e] `(= ~(get FIELDS e)
                                                  (nth ~'x ~e)))
                                      (range 0 N))))
           (~'seq [] (list ~@FIELDS))

           clojure.lang.IPersistentVector
           (~'length [] 2)
           (~'assocN [~'n ~'v] (. ~'this ~'assoc ~'n ~'v))
           ;; cons again

           clojure.lang.Indexed
           (~'nth [~'n] (. ~'this ~'valAt ~'n))

           clojure.lang.Reversible
           (~'rseq [] (reverse (. ~'this ~'seq)))

           clojure.lang.Seqable
           ;; seq again 

           clojure.lang.Sequential
           ;; (no methods)

           java.io.Serializable
           ;; (no methods)

           java.lang.Comparable
           (~'compareTo [~'x] (compare ~FIELDS ~'x))

           java.lang.Iterable
           (~'iterator [] (.. ~'this ~'seq ~'iterator))

           java.lang.Runnable
           (~'run [] nil)

           java.util.Collection

           java.util.List
           (~'size [] 2)
           (~'indexOf [~'x] (cond
                             ~@(interleave
                                (map (fn [n] `(= ~'x ~(get FIELDS n)))
                                     (range 0 N))
                                (range 0 N))
                             :else -1))
           (~'lastIndexOf [~'x] (cond
                                 ~@(interleave
                                    (map (fn [n] `(= ~'x ~(get FIELDS n)))
                                         (reverse (range 0 N)))
                                    (reverse (range 0 N)))
                                 :else -1))

           java.util.RandomAccess
           ;; (no methods)

           java.util.concurrent.Callable
           (~'call [] nil)))))

(def-tuple-type Tuple2 2)
(def-tuple-type Tuple3 3)
(def-tuple-type Tuple4 4)
(def-tuple-type Tuple5 5)

(defn tuple
  "Creates and returns a new 2, 3, 4, or 5-element tuple.
  Tuples support the same methods as vectors."
  ([a b] (Tuple2 a b))
  ([a b c] (Tuple3 a b c))
  ([a b c d] (Tuple4 a b c d))
  ([a b c d e] (Tuple5 a b c d e)))


(def-tuple-type Double3D 3 double)


;;; TESTS SHOWING USAGE

(use 'clojure.test)

(deftest t-equality
  (is (= (tuple 8 9) [8 9])))

(deftest t-compare
  (is (= -1 (compare (tuple 7 8 9) (tuple 7 8 10))))
  (is (= 0  (compare (tuple 1 2) (tuple 1 2))))
  (is (= 0  (compare (tuple :a :b :c) [:a :b :c]))))

(deftest t-assoc
  (is (= (tuple :a :c) (assoc (tuple :a :b) 1 :c))))

(deftest t-nth
  (is (= :d (nth (tuple :a :b :c :d) 3)))
  (is (= :notfound (nth (tuple :a :b) 3 :notfound))))

(deftest t-count
  (is (= 5 (count (tuple :a :b :c :d :e)))))

(deftest t-invoke
  (is (= :d ((tuple :a :b :c :d) 3))))


;;; MICRO-BENCHMARK

(defn performance
  "Prints micro-benchmark comparison of create/get with 
  vectors vs tuples"
  []
  (println "3-VECTORS of Objects")
  (dotimes [j 5]
    (time (dotimes [i 1000000]
            (get (vector i i i) 2))))
  (println "3-TUPLES of Objects")
  (dotimes [j 5]
    (time (dotimes [i 1000000]
            (get (tuple i i i) 2))))
  (println "3-TUPLES of doubles")
  (dotimes [j 5]
    (time (dotimes [i 1000000]
            (get (Double3D 2.0 3.0 4.0) 2)))))

;; 3-VECTORS of Objects
;; "Elapsed time: 881.735 msecs"
;; "Elapsed time: 817.342 msecs"
;; "Elapsed time: 775.121 msecs"
;; "Elapsed time: 781.765 msecs"
;; "Elapsed time: 782.984 msecs"
;; 3-TUPLES of Objects
;; "Elapsed time: 119.216 msecs"
;; "Elapsed time: 96.22 msecs"
;; "Elapsed time: 89.843 msecs"
;; "Elapsed time: 87.383 msecs"
;; "Elapsed time: 85.94 msecs"
;; 3-TUPLES of doubles
;; "Elapsed time: 76.151 msecs"
;; "Elapsed time: 65.123 msecs"
;; "Elapsed time: 58.15 msecs"
;; "Elapsed time: 56.87 msecs"
;; "Elapsed time: 55.464 msecs"

Annotation number 2: typed tuple comparison with arrays
Pasted by: stuartsierra
When:7 months, 2 weeks ago
Share:Tweet this! | http://paste.lisp.org/+208Q/2
Paste contents:
Raw Source | Display As
;;; MICRO-BENCHMARK

(defn performance
  "Prints micro-benchmark comparison of create/get with 
  vectors vs tuples"
  []
  (println "3-VECTORS of Objects")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (nth (vector i i i) 2))))
  (println "3-TUPLES of Objects")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (nth (tuple i i i) 2))))
  (println "3-TUPLES of doubles")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (nth (Double3D 2.0 3.0 4.0) 2))))
  (println "arrays of doubles")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (aget (double-array 3 2.0) 2)))))

;; 3-VECTORS of Objects
;; "Elapsed time: 866.365 msecs"
;; "Elapsed time: 801.139 msecs"
;; "Elapsed time: 794.013 msecs"
;; "Elapsed time: 793.486 msecs"
;; "Elapsed time: 797.959 msecs"
;; "Elapsed time: 790.892 msecs"
;; 3-TUPLES of Objects
;; "Elapsed time: 127.525 msecs"
;; "Elapsed time: 107.917 msecs"
;; "Elapsed time: 110.856 msecs"
;; "Elapsed time: 109.684 msecs"
;; "Elapsed time: 113.274 msecs"
;; "Elapsed time: 114.051 msecs"
;; 3-TUPLES of doubles
;; "Elapsed time: 90.987 msecs"
;; "Elapsed time: 83.257 msecs"
;; "Elapsed time: 81.719 msecs"
;; "Elapsed time: 81.561 msecs"
;; "Elapsed time: 83.074 msecs"
;; "Elapsed time: 88.07 msecs"
;; arrays of doubles
;; "Elapsed time: 71.501 msecs"
;; "Elapsed time: 55.681 msecs"
;; "Elapsed time: 54.293 msecs"
;; "Elapsed time: 52.743 msecs"
;; "Elapsed time: 53.485 msecs"
;; "Elapsed time: 49.405 msecs"

Annotation number 3: Ever so slightly faster with cond
Pasted by: stuartsierra
When:7 months, 2 weeks ago
Share:Tweet this! | http://paste.lisp.org/+208Q/3
Paste contents:
Raw Source | Display As
;;; A N-element tuple with type declarations

(defmacro def-tuple-type
  "Defines a type, named NAME, for a N-element tuple with elements of
  type TYPE, which may be primitive."
  ([NAME N] (def-tuple-type NAME N java.lang.Object))
  ([NAME N TYPE]
      (assert (integer? N))
      (assert (pos? N))
      (let [TAGGED-FIELDS (vec (map #(with-meta (symbol (str "e" %))
                                       {:tag TYPE})
                                    (range 0 N)))
            FIELDS (vec (map #(symbol (str "e" %)) (range 0 N)))]
        `(deftype ~NAME ~TAGGED-FIELDS :as ~'this

           ;; method implementations
           clojure.lang.Associative
           (~'containsKey [~'n] (and (integer? ~'n)
                                     (< -1 ~'n ~N)))
           (~'entryAt [~'k] (cond ~@(interleave
                                     (map (fn [e] `(= ~e ~'k))
                                          (range 0 N))
                                     (map (fn [e] `(clojure.lang.MapEntry.
                                                    ~e ~(nth FIELDS e)))
                                          (range 0 N)))
                                  :else (throw (IllegalArgumentException.))))
           (~'assoc [~'k ~'v] (cond ~@(interleave
                                       (map (fn [e] `(= ~e ~'k))
                                            (range 0 N))
                                       (map (fn [e]
                                              `(new ~NAME
                                                    ~@(assoc FIELDS e 'v)))
                                            (range 0 N)))
                                    :else (throw (IllegalArgumentException.))))

           clojure.lang.Counted
           (~'count [] ~N)

           clojure.lang.ILookup
           (~'valAt [~'n] (cond ~@(interleave
                                   (map (fn [e] `(= ~e ~'n))
                                        (range 0 N))
                                   FIELDS)
                                :else nil))
           (~'valAt [~'n not-found#] (cond
                                      ~@(interleave
                                         (map (fn [e] `(= ~e ~'n))
                                              (range 0 N))
                                         FIELDS)
                                      :else not-found#))

           clojure.lang.IFn
           (~'invoke [~'n] (. ~'this valAt ~'n))
           (~'invoke [~'n not-found#] (. ~'this valAt ~'n not-found#))
  
           clojure.lang.IPersistentCollection
           (~'cons [~'x] (cons ~'x (seq ~'this)))
           (~'empty [] (new ~NAME ~@(repeat N nil)))
           (~'equiv [~'x] (and (= ~N (count ~'x))
                               ~@(map (fn [e] `(= ~(get FIELDS e)
                                                  (nth ~'x ~e)))
                                      (range 0 N))))
           (~'seq [] (list ~@FIELDS))

           clojure.lang.IPersistentVector
           (~'length [] 2)
           (~'assocN [~'n ~'v] (. ~'this ~'assoc ~'n ~'v))
           ;; cons again

           clojure.lang.Indexed
           (~'nth [~'n] (. ~'this ~'valAt ~'n))

           clojure.lang.Reversible
           (~'rseq [] (reverse (. ~'this ~'seq)))

           clojure.lang.Seqable
           ;; seq again 

           clojure.lang.Sequential
           ;; (no methods)

           java.io.Serializable
           ;; (no methods)

           java.lang.Comparable
           (~'compareTo [~'x] (compare ~FIELDS ~'x))

           java.lang.Iterable
           (~'iterator [] (.. ~'this ~'seq ~'iterator))

           java.lang.Runnable
           (~'run [] nil)

           java.util.Collection

           java.util.List
           (~'size [] 2)
           (~'indexOf [~'x] (cond
                             ~@(interleave
                                (map (fn [n] `(= ~'x ~(get FIELDS n)))
                                     (range 0 N))
                                (range 0 N))
                             :else -1))
           (~'lastIndexOf [~'x] (cond
                                 ~@(interleave
                                    (map (fn [n] `(= ~'x ~(get FIELDS n)))
                                         (reverse (range 0 N)))
                                    (reverse (range 0 N)))
                                 :else -1))

           java.util.RandomAccess
           ;; (no methods)

           java.util.concurrent.Callable
           (~'call [] nil)))))

(def-tuple-type Tuple2 2)
(def-tuple-type Tuple3 3)
(def-tuple-type Tuple4 4)
(def-tuple-type Tuple5 5)

(defn tuple
  "Creates and returns a new 2, 3, 4, or 5-element tuple.
  Tuples support the same methods as vectors."
  ([a b] (Tuple2 a b))
  ([a b c] (Tuple3 a b c))
  ([a b c d] (Tuple4 a b c d))
  ([a b c d e] (Tuple5 a b c d e)))


(def-tuple-type Double3D 3 double)


;;; TESTS SHOWING USAGE

(use 'clojure.test)

(deftest t-equality
  (is (= (tuple 8 9) [8 9])))

(deftest t-compare
  (is (= -1 (compare (tuple 7 8 9) (tuple 7 8 10))))
  (is (= 0  (compare (tuple 1 2) (tuple 1 2))))
  (is (= 0  (compare (tuple :a :b :c) [:a :b :c]))))

(deftest t-assoc
  (is (= (tuple :a :c) (assoc (tuple :a :b) 1 :c))))

(deftest t-nth
  (is (= :d (nth (tuple :a :b :c :d) 3)))
  (is (= :notfound (nth (tuple :a :b) 3 :notfound))))

(deftest t-count
  (is (= 5 (count (tuple :a :b :c :d :e)))))

(deftest t-invoke
  (is (= :d ((tuple :a :b :c :d) 3))))


;;; MICRO-BENCHMARK

(defn performance
  "Prints micro-benchmark comparison of create/get with 
  vectors vs tuples"
  []
  (println "3-VECTORS of Objects")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (nth (vector i i i) 2))))
  (println "3-TUPLES of Objects")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (nth (tuple i i i) 2))))
  (println "3-TUPLES of doubles")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (nth (Double3D 2.0 3.0 4.0) 2))))
  (println "arrays of doubles")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (aget (double-array 3 2.0) 2)))))

;; 3-VECTORS of Objects
;; "Elapsed time: 866.365 msecs"
;; "Elapsed time: 801.139 msecs"
;; "Elapsed time: 794.013 msecs"
;; "Elapsed time: 793.486 msecs"
;; "Elapsed time: 797.959 msecs"
;; "Elapsed time: 790.892 msecs"
;; 3-TUPLES of Objects
;; "Elapsed time: 127.525 msecs"
;; "Elapsed time: 107.917 msecs"
;; "Elapsed time: 110.856 msecs"
;; "Elapsed time: 109.684 msecs"
;; "Elapsed time: 113.274 msecs"
;; "Elapsed time: 114.051 msecs"
;; 3-TUPLES of doubles
;; "Elapsed time: 90.987 msecs"
;; "Elapsed time: 83.257 msecs"
;; "Elapsed time: 81.719 msecs"
;; "Elapsed time: 81.561 msecs"
;; "Elapsed time: 83.074 msecs"
;; "Elapsed time: 88.07 msecs"
;; arrays of doubles
;; "Elapsed time: 71.501 msecs"
;; "Elapsed time: 55.681 msecs"
;; "Elapsed time: 54.293 msecs"
;; "Elapsed time: 52.743 msecs"
;; "Elapsed time: 53.485 msecs"
;; "Elapsed time: 49.405 msecs"

Annotation number 4: fixed recursive macro self-call
Pasted by: stuartsierra
When:7 months, 2 weeks ago
Share:Tweet this! | http://paste.lisp.org/+208Q/4
Paste contents:
Raw Source | Display As
;;; A N-element tuple with type declarations

(defmacro def-tuple-type
  "Defines a type, named NAME, for a N-element tuple with elements of
  type TYPE, which may be primitive."
  ([NAME N] `(def-tuple-type ~NAME ~N java.lang.Object))
  ([NAME N TYPE]
      (assert (integer? N))
      (assert (pos? N))
      (let [TAGGED-FIELDS (vec (map #(with-meta (symbol (str "e" %))
                                       {:tag TYPE})
                                    (range 0 N)))
            FIELDS (vec (map #(symbol (str "e" %)) (range 0 N)))]
        `(deftype ~NAME ~TAGGED-FIELDS :as ~'this

           ;; method implementations
           clojure.lang.Associative
           (~'containsKey [~'n] (and (integer? ~'n)
                                     (< -1 ~'n ~N)))
           (~'entryAt [~'k] (cond ~@(interleave
                                     (map (fn [e] `(= ~e ~'k))
                                          (range 0 N))
                                     (map (fn [e] `(clojure.lang.MapEntry.
                                                    ~e ~(nth FIELDS e)))
                                          (range 0 N)))
                                  :else (throw (IllegalArgumentException.))))
           (~'assoc [~'k ~'v] (cond ~@(interleave
                                       (map (fn [e] `(= ~e ~'k))
                                            (range 0 N))
                                       (map (fn [e]
                                              `(new ~NAME
                                                    ~@(assoc FIELDS e 'v)))
                                            (range 0 N)))
                                    :else (throw (IllegalArgumentException.))))

           clojure.lang.Counted
           (~'count [] ~N)

           clojure.lang.ILookup
           (~'valAt [~'n] (cond ~@(interleave
                                   (map (fn [e] `(= ~e ~'n))
                                        (range 0 N))
                                   FIELDS)
                                :else nil))
           (~'valAt [~'n not-found#] (cond
                                      ~@(interleave
                                         (map (fn [e] `(= ~e ~'n))
                                              (range 0 N))
                                         FIELDS)
                                      :else not-found#))

           clojure.lang.IFn
           (~'invoke [~'n] (. ~'this valAt ~'n))
           (~'invoke [~'n not-found#] (. ~'this valAt ~'n not-found#))
  
           clojure.lang.IPersistentCollection
           (~'cons [~'x] (cons ~'x (seq ~'this)))
           (~'empty [] (new ~NAME ~@(repeat N nil)))
           (~'equiv [~'x] (and (= ~N (count ~'x))
                               ~@(map (fn [e] `(= ~(get FIELDS e)
                                                  (nth ~'x ~e)))
                                      (range 0 N))))
           (~'seq [] (list ~@FIELDS))

           clojure.lang.IPersistentVector
           (~'length [] 2)
           (~'assocN [~'n ~'v] (. ~'this ~'assoc ~'n ~'v))
           ;; cons again

           clojure.lang.Indexed
           (~'nth [~'n] (. ~'this ~'valAt ~'n))

           clojure.lang.Reversible
           (~'rseq [] (reverse (. ~'this ~'seq)))

           clojure.lang.Seqable
           ;; seq again 

           clojure.lang.Sequential
           ;; (no methods)

           java.io.Serializable
           ;; (no methods)

           java.lang.Comparable
           (~'compareTo [~'x] (compare ~FIELDS ~'x))

           java.lang.Iterable
           (~'iterator [] (.. ~'this ~'seq ~'iterator))

           java.lang.Runnable
           (~'run [] nil)

           java.util.Collection

           java.util.List
           (~'size [] 2)
           (~'indexOf [~'x] (cond
                             ~@(interleave
                                (map (fn [n] `(= ~'x ~(get FIELDS n)))
                                     (range 0 N))
                                (range 0 N))
                             :else -1))
           (~'lastIndexOf [~'x] (cond
                                 ~@(interleave
                                    (map (fn [n] `(= ~'x ~(get FIELDS n)))
                                         (reverse (range 0 N)))
                                    (reverse (range 0 N)))
                                 :else -1))

           java.util.RandomAccess
           ;; (no methods)

           java.util.concurrent.Callable
           (~'call [] nil)))))

(def-tuple-type Tuple2 2)
(def-tuple-type Tuple3 3)
(def-tuple-type Tuple4 4)
(def-tuple-type Tuple5 5)

(defn tuple
  "Creates and returns a new 2, 3, 4, or 5-element tuple.
  Tuples support the same methods as vectors."
  ([a b] (Tuple2 a b))
  ([a b c] (Tuple3 a b c))
  ([a b c d] (Tuple4 a b c d))
  ([a b c d e] (Tuple5 a b c d e)))


(def-tuple-type Double3D 3 double)


;;; TESTS SHOWING USAGE

(use 'clojure.test)

(deftest t-equality
  (is (= (tuple 8 9) [8 9])))

(deftest t-compare
  (is (= -1 (compare (tuple 7 8 9) (tuple 7 8 10))))
  (is (= 0  (compare (tuple 1 2) (tuple 1 2))))
  (is (= 0  (compare (tuple :a :b :c) [:a :b :c]))))

(deftest t-assoc
  (is (= (tuple :a :c) (assoc (tuple :a :b) 1 :c))))

(deftest t-nth
  (is (= :d (nth (tuple :a :b :c :d) 3)))
  (is (= :notfound (nth (tuple :a :b) 3 :notfound))))

(deftest t-count
  (is (= 5 (count (tuple :a :b :c :d :e)))))

(deftest t-invoke
  (is (= :d ((tuple :a :b :c :d) 3))))


;;; MICRO-BENCHMARK

(defn performance
  "Prints micro-benchmark comparison of create/get with 
  vectors vs tuples"
  []
  (println "3-VECTORS of Objects")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (nth (vector i i i) 2))))
  (println "3-TUPLES of Objects")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (nth (tuple i i i) 2))))
  (println "3-TUPLES of doubles")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (nth (Double3D 2.0 3.0 4.0) 2))))
  (println "arrays of doubles")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (aget (double-array 3 2.0) 2)))))

;; 3-VECTORS of Objects
;; "Elapsed time: 866.365 msecs"
;; "Elapsed time: 801.139 msecs"
;; "Elapsed time: 794.013 msecs"
;; "Elapsed time: 793.486 msecs"
;; "Elapsed time: 797.959 msecs"
;; "Elapsed time: 790.892 msecs"
;; 3-TUPLES of Objects
;; "Elapsed time: 127.525 msecs"
;; "Elapsed time: 107.917 msecs"
;; "Elapsed time: 110.856 msecs"
;; "Elapsed time: 109.684 msecs"
;; "Elapsed time: 113.274 msecs"
;; "Elapsed time: 114.051 msecs"
;; 3-TUPLES of doubles
;; "Elapsed time: 90.987 msecs"
;; "Elapsed time: 83.257 msecs"
;; "Elapsed time: 81.719 msecs"
;; "Elapsed time: 81.561 msecs"
;; "Elapsed time: 83.074 msecs"
;; "Elapsed time: 88.07 msecs"
;; arrays of doubles
;; "Elapsed time: 71.501 msecs"
;; "Elapsed time: 55.681 msecs"
;; "Elapsed time: 54.293 msecs"
;; "Elapsed time: 52.743 msecs"
;; "Elapsed time: 53.485 msecs"
;; "Elapsed time: 49.405 msecs"

Annotation number 5: tuples vs arrays
Pasted by: rhickey
When:7 months, 2 weeks ago
Share:Tweet this! | http://paste.lisp.org/+208Q/5
Paste contents:
Raw Source | Display As
(defn da [x y z]
  (let [x (double x) y (double y) z (double z)]
    (let [a #^doubles (double-array 3)]
      (aset a 0 x)
      (aset a 1 y)
      (aset a 2 z)
      a)))

(defn oa [x y z]
  (let [a #^objects (make-array Object 3)]
    (aset a 0 x)
    (aset a 1 y)
    (aset a 2 z)
    a)))

(defn performance
  "Prints micro-benchmark comparison of create/get with 
  vectors vs tuples"
  []
  (println "3-VECTORS of Objects")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (nth (vector i i i) 2))))
  (println "3-TUPLES of Objects")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (nth (tuple i i i) 2))))
  (println "3-TUPLES of doubles")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (nth (Double3D 2.0 3.0 4.0) 2))))
  (println "arrays of doubles")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (aget #^doubles (da  2.0 3.0 4.0) 2))))
  (println "arrays of objects")
  (dotimes [j 6]
    (time (dotimes [i 1000000]
            (aget #^objects (oa  2.0 3.0 4.0) 2)))))

Colorize as:
Show Line Numbers
Index of paste annotations: 1 | 2 | 3 | 4 | 5

Lisppaste pastes can be made by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively.