Lazier, faster, more memory-efficient seq

Custom print methods, remove unused zip code
main
Eric Ihli 3 years ago
parent 91ca755adb
commit f57ea59c22

@ -194,3 +194,5 @@ If you're familiar with binary search over sorted lists, you'll know this is a c
**** How is a trie faster?
* Development

@ -4,7 +4,7 @@
<packaging>jar</packaging>
<groupId>com.owoga</groupId>
<artifactId>tightly-packed-trie</artifactId>
<version>0.2.1</version>
<version>0.2.2</version>
<name>tightly-packed-trie</name>
<scm>
<connection>scm:git:git://github.com/eihli/clj-tightly-packed-trie.git</connection>

@ -208,19 +208,40 @@
(valAt [self ks not-found]
(or (get self ks) not-found))
clojure.lang.Counted
(count [trie]
(count (seq trie)))
clojure.lang.Seqable
(seq [trie]
(->> trie
(#(trie->depth-first-post-order-traversable-zipperable-vector
[]
%
value-decode-fn))
zip/vector-zip
(iterate zip/next)
(take-while (complement zip/end?))
(map zip/node)
(filter (partial instance? clojure.lang.MapEntry))
(#(if (empty? %) nil %)))))
(let [step (fn step [path [[node & nodes] & stack] [parent & parents]]
(cond
node
(step (conj path (.key node))
(into (into stack (list nodes))
(list (trie/children node)))
(cons node (cons parent parents)))
(and parent (not= 0 (.key parent)))
(lazy-seq
(cons (clojure.lang.MapEntry.
(rest path)
(let [byte-buffer (.byte-buffer parent)]
(wrap-byte-buffer
byte-buffer
(.limit byte-buffer (.limit parent))
(.position byte-buffer (.address parent))
(value-decode-fn byte-buffer))))
(step (pop path)
stack
parents)))
:else nil))]
(step [] (list (list trie)) '()))))
(defmethod print-method TightlyPackedTrie [trie ^java.io.Writer w]
(print-method (into {} trie) w))
(defmethod print-dup TightlyPackedTrie [trie ^java.io.Writer w]
(print-ctor trie (fn [o w] (print-dup (into {} trie) w)) w))
(defn tightly-packed-trie
[trie value-encode-fn value-decode-fn]

@ -1,31 +1,4 @@
(ns com.owoga.trie
(:require [clojure.zip :as zip]))
(defn -trie->depth-first-post-order-traversable-zipperable-vector
([path node]
(vec
(map
(fn [[k v]]
[(-trie->depth-first-post-order-traversable-zipperable-vector (conj path k) v)
(clojure.lang.MapEntry. (conj path k) (.value v))])
(.children- node)))))
(defn trie->depth-first-post-order-traversable-zipperable-vector
[path node]
(if (.value node)
[(-trie->depth-first-post-order-traversable-zipperable-vector
path node)
(clojure.lang.MapEntry. path (.value node))]
(-trie->depth-first-post-order-traversable-zipperable-vector
path node)))
(defn depth-first-post-order-traversable-zipperable-vector->trie
[cls [children [key node]]]
(sorted-map
(last key)
(cls (.key node) (.value node)
(into (sorted-map)
(map depth-first-post-order-traversable-zipperable-vector->trie children)))))
(ns com.owoga.trie)
(declare ->Trie)
@ -61,47 +34,32 @@
(fn [[k child]]
(Trie. k
(.value child)
#_(sorted-map)
(.children- child)))
children-))
(lookup [trie k]
(loop [k' k
trie' trie]
(loop [k k
trie trie]
(cond
;; Allows `update` to work the same as with maps... can use `fnil`.
;; (nil? trie') (throw (Exception. (format "Key not found: %s" k)))
(nil? trie') nil
(empty? k')
(Trie. (.key trie')
(.value trie')
(.children- trie'))
(nil? trie) nil
(empty? k)
(Trie. (.key trie)
(.value trie)
(.children- trie))
:else (recur
(rest k')
(get (.children- trie') (first k'))))))
(rest k)
(get (.children- trie) (first k))))))
clojure.lang.ILookup
(valAt [trie k]
(loop [k' k
trie' trie]
(cond
;; Allows `update` to work the same as with maps... can use `fnil`.
;; (nil? trie') (throw (Exception. (format "Key not found: %s" k)))
(nil? trie') nil
(empty? k') (.value trie')
:else (recur
(rest k')
(get (.children- trie') (first k'))))))
(if-let [node (lookup trie k)]
(.value node)
nil))
(valAt [trie k not-found]
(loop [k' k
trie' trie]
(cond
(nil? trie') not-found
(empty? k') (.value trie')
:else (recur
(rest k')
(get (.children- trie') (first k'))))))
(or (get trie k) not-found))
clojure.lang.IPersistentCollection
(cons [trie entry]
@ -110,10 +68,8 @@
(assoc trie (first entry) (.value (second entry)))
:else
(assoc trie (first entry) (second entry))))
(empty [trie]
(Trie. key nil (sorted-map)))
(equiv [trie o]
(and (= (.value trie)
(.value o))
@ -145,20 +101,39 @@
(without [trie key]
(-without trie key))
java.lang.Iterable
(iterator [trie]
(.iterator (seq trie)))
clojure.lang.Counted
(count [trie]
(count (seq trie)))
clojure.lang.Seqable
(seq [trie]
(->> trie
((partial trie->depth-first-post-order-traversable-zipperable-vector []))
zip/vector-zip
(iterate zip/next)
(take-while (complement zip/end?))
(map zip/node)
(filter (partial instance? clojure.lang.MapEntry))
(#(if (empty? %) nil %)))))
(let [step (fn step [path [[node & nodes] & stack] [parent & parents]]
(cond
node
(step (conj path (.key node))
(into (into stack (list nodes))
(list (children node)))
(cons node (cons parent parents)))
(and parent (not= '() (.key parent)))
(lazy-seq
(cons (clojure.lang.MapEntry.
(rest path)
(.value parent))
(step (pop path)
stack
parents)))
:else nil))]
(step [] (list (list trie)) '()))))
(defmethod print-method Trie [trie ^java.io.Writer w]
(print-method (into {} trie) w))
(defmethod print-dup Trie [trie ^java.io.Writer w]
(print-ctor trie (fn [o w] (print-dup (into {} trie) w)) w))
(defn make-trie
([]

@ -31,8 +31,17 @@
(testing "ITrie"
(testing "lookup"
(is (= nil (trie/lookup empty-trie [1])))
;; A `get` of a node returns the value at the node.
(is (= 1 (get (trie/lookup initialized-trie [1]) [])))
(is (= 12 (get (trie/lookup initialized-trie [1]) [2]))))
(is (= 12 (get (trie/lookup initialized-trie [1]) [2])))
;; A `seq` of a node is a depth-first post-order traversal of its descendants.
(is (= '([[2] 12] [[3] 13] [[] 1])
(seq (trie/lookup initialized-trie [1])))))
;; The children of a node are only the immediate children and
;; the root node's value is excluded.
(testing "children"
(is (= '(12 13)
(map #(get % [])
@ -61,9 +70,14 @@
[[1 2] nil]
[[1 3 1] 131]
[[1 3] nil]
[[1] nil]
[[] nil])
(seq initialized-trie))))))
[[1] nil])
(seq initialized-trie))))
(testing "Seq on lookup"
(is (= '([[1] 121]
[[2] 122]
[[3] 123]
[[] nil])
(seq (trie/lookup initialized-trie [1 2])))))))
(comment
(let [trie (trie/make-trie '(1 2 3) 123 '(1 2 1) 121 '(1 2 2) 122 '(1 3 1) 131)

@ -13,9 +13,9 @@
(testing "dissoc"
(let [expected (-> (trie/make-trie)
(assoc '(1) 1))
trie (-> (trie/make-trie)
(assoc '(1) 1)
(assoc '(1 3) 13))]
trie (-> (trie/make-trie)
(assoc '(1) 1)
(assoc '(1 3) 13))]
(is (= expected (dissoc trie '(1 3))))))
(testing "ILookup"
(is (= 12 (get initialized-trie '(1 2))))
@ -30,5 +30,6 @@
(is (= 2 (count initialized-trie))))
(testing "Seqable"
(is (= '([[1 2] 12] [[1] nil])
(seq initialized-trie))))))
(seq initialized-trie)))
(is (= '([[1 2] 12] [[1] 1])
(seq (assoc initialized-trie '(1) 1)))))))

Loading…
Cancel
Save