Lazier, faster, more memory-efficient seq

Custom print methods, remove unused zip code
main
Eric Ihli 4 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? **** How is a trie faster?
* Development

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

@ -208,19 +208,40 @@
(valAt [self ks not-found] (valAt [self ks not-found]
(or (get self ks) not-found)) (or (get self ks) not-found))
clojure.lang.Counted
(count [trie]
(count (seq trie)))
clojure.lang.Seqable clojure.lang.Seqable
(seq [trie] (seq [trie]
(->> trie (let [step (fn step [path [[node & nodes] & stack] [parent & parents]]
(#(trie->depth-first-post-order-traversable-zipperable-vector (cond
[] node
% (step (conj path (.key node))
value-decode-fn)) (into (into stack (list nodes))
zip/vector-zip (list (trie/children node)))
(iterate zip/next) (cons node (cons parent parents)))
(take-while (complement zip/end?)) (and parent (not= 0 (.key parent)))
(map zip/node) (lazy-seq
(filter (partial instance? clojure.lang.MapEntry)) (cons (clojure.lang.MapEntry.
(#(if (empty? %) nil %))))) (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 (defn tightly-packed-trie
[trie value-encode-fn value-decode-fn] [trie value-encode-fn value-decode-fn]

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

@ -31,8 +31,17 @@
(testing "ITrie" (testing "ITrie"
(testing "lookup" (testing "lookup"
(is (= nil (trie/lookup empty-trie [1]))) (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 (= 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" (testing "children"
(is (= '(12 13) (is (= '(12 13)
(map #(get % []) (map #(get % [])
@ -61,9 +70,14 @@
[[1 2] nil] [[1 2] nil]
[[1 3 1] 131] [[1 3 1] 131]
[[1 3] nil] [[1 3] nil]
[[1] nil] [[1] nil])
(seq initialized-trie))))
(testing "Seq on lookup"
(is (= '([[1] 121]
[[2] 122]
[[3] 123]
[[] nil]) [[] nil])
(seq initialized-trie)))))) (seq (trie/lookup initialized-trie [1 2])))))))
(comment (comment
(let [trie (trie/make-trie '(1 2 3) 123 '(1 2 1) 121 '(1 2 2) 122 '(1 3 1) 131) (let [trie (trie/make-trie '(1 2 3) 123 '(1 2 1) 121 '(1 2 2) 122 '(1 3 1) 131)

@ -30,5 +30,6 @@
(is (= 2 (count initialized-trie)))) (is (= 2 (count initialized-trie))))
(testing "Seqable" (testing "Seqable"
(is (= '([[1 2] 12] [[1] nil]) (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