Red-Black Trees
To use the bindings from this module:
(import :std/misc/rbtree)
make-rbtree
(make-rbtree cmp [root = +empty+]) -> rbtree
cmp := comparison procedure
root := optional tree root element
Creates a new red-black tree, a self-balancing binary search tree variant, similar to an AVL tree. Also usable as an sorted hash-table alternative for small datasets.
The comparison procedure cmp is expected to accept two keys, a and b, and perform a ternary comparison:
- If
(< a b)
, then it must return a negative number. - If
(= a b)
, then it must return 0. - If
(> a b)
, then it must return a positive number.
Examples of comparison procedures: -
, string-cmp
or symbol-cmp
. The latter
two are defined in this module.
Examples:
> (def rbt1 (make-rbtree -))
> (def rbt2 (list->rbtree string-cmp [["one" . 1] ["two" . 2] ["three" . 3]]))
> (rbtree-put! rbt1 1 "one")
> (rbtree-put! rbt1 2 "two")
> (rbtree-put! rbt1 3 "four")
> (rbtree-put! rbt1 4 "four")
> (rbtree-update! rbt1 3 (lambda (x) (when (string=? x "four") "three")))
> (rbtree-remove! rbt1 4)
> (rbtree->list rbt1)
((1 . "one") (2 . "two") (3 . "three"))
> (rbtree->list rbt2)
(("one" . 1) ("three" . 3) ("two" . 2))
> (import :std/iter)
> (for* ((key (in-rbtree-keys rbt2)) (val (in-rbtree-values rbt1)))
(unless (string=? key val)
(displayln key " " val)))
one two
one three
three one
three two
two one
two three
rbtree?
(rbtree? rbt) -> boolean
rbt := rbtree to check
Returns #t
if rbt is an rbtree, #f
otherwise.
Examples:
> (rbtree? (make-rbtree string-cmp))
#t
rbtree-empty?
(rbtree-empty? rbt) -> boolean
rbt := rbtree to check
Returns #t
if rbt is empty, #f
otherwise.
Examples:
> (rbtree-empty? (make-rbtree -))
#t
> (make-rbtree -)
#<rbtree #62>
> (rbtree-put! #62 0 'NULL)
> (rbtree-empty? #62)
#f
rbtree-put!
(rbtree-put! rbt key val) -> unspecified
rbt := rbtree to update
key, val := key-value association to add to rbt
Destructively updates rbt by inserting the key-value association key to
val. Similar to set!
, it's unspecified what rbtree-put!
returns.
Examples:
> (let (rbt (make-rbtree -))
(rbtree-put! rbt 3 'a)
(displayln (rbtree->list rbt))
(rbtree-put! rbt 2 'b)
(displayln (rbtree->list rbt))
(rbtree-put! rbt 4 'c)
(displayln (rbtree->list rbt))
(rbtree-put! rbt 1 'd)
(displayln (rbtree->list rbt)))
((3 . a))
((2 . b) (3 . a))
((2 . b) (3 . a) (4 . c))
((1 . d) (2 . b) (3 . a) (4 . c))
rbtree-put
(rbtree-put rbt key val) -> rbtree
rbt := rbtree to update
key, val := key-value association to add to rbt
rbtree-put
is similar to rbtree-put!
, but functionally updates rbt instead,
returning a new rbtree without modifying rbt.
Examples:
> (let (rbt (make-rbtree -))
(displayln (rbtree->list (rbtree-put rbt 1 'a)))
(displayln "empty? " (rbtree-empty? rbt)))
((1 . a))
empty? #t
rbtree-update!
(rbtree-update! rbt key proc [default = void]) -> unspecified
rbt := rbtree to update
key := key to look up
proc := update procedure, receives previous value
default := optional default value when key not present
Destructively updates rbt by modifying the value associated with key. Looks
up key in rbt and passes the associated value (or default, if key isn't
present) to proc, an updating procedure. Similar to set!
, it's unspecified
what rbtree-update!
returns.
Examples:
> (def rbt (make-rbtree string-cmp))
> (rbtree-update! rbt "one" 1+ 0) ; would signal error without default value
> (rbtree->list rbt)
(("one" . 1))
> (rbtree-put! rbt "two" 2)
> (rbtree-update! rbt "two" 1+)
> (rbtree-update! rbt "one" (cut * 2 <>))
> (rbtree->list rbt)
(("one" . 2) ("two" . 3))
rbtree-update
(rbtree-update rbt key proc [default = void]) -> rbtree
rbt := rbtree to update
key := key to look up
proc := update procedure, receives previous value
default := optional default value when key not present
rbtree-update
is similar to rbtree-update!
, but functionally updates rbt
instead, returning a new rbtree without modifying rbt.
Examples:
> (def rbt (make-rbtree symbol-cmp))
> (rbtree-put! rbt 'a 16)
> (rbtree->list (rbtree-update rbt 'a sqrt))
((a . 4))
> (rbtree->list rbt)
((a . 16))
rbtree-remove!
(rbtree-remove! rbt key) -> unspecified
rbt := rbtree to update
key := key to look up
Destructively updates rbt by removing the value associated with key. rbt
stays unmodified if key isn't present. Similar to set!
, it's unspecified
what rbtree-update!
returns.
Examples:
> (def rbt (make-rbtree symbol-cmp))
> (rbtree-put! rbt 'a 3)
> (rbtree-put! rbt 'b 2)
> (rbtree-put! rbt 'c 1)
> (rbtree-remove! rbt 'b)
> (rbtree-remove! rbt 'd) ; nothing happens
> (rbtree->list rbt)
((a . 3) (c . 1))
rbtree-remove
(rbtree-remove rbt key) -> rbtree
rbt := rbtree to update
key := key to look up
rbtree-remove
is similar to rbtree-update!
, but functionally updates rbt
instead, returning a new rbtree without modifying rbt. If key isn't present,
then rbtree-remove
simply returns rbt instead of allocating a new identical
tree.
Examples:
> (let (rbt (make-rbtree -))
(rbtree-put! rbt 1 "gambit")
(rbtree-put! (rbtree-remove rbt 2) 1 "gerbil") ; rbtree-remove returns rbt
(displayln (rbtree->list rbt))
(rbtree->list (rbtree-remove rbt 1)))
((1 . "gerbil"))
()
rbtree-ref
(rbtree-ref rbt key [default = absent-obj]) -> any | default | error
rbt := rbtree to search in
key := key to look up in rbt
default := optional default value when key not present
Returns the value associated with key in rbt, or default if no association is present; if the default value is omitted then an error is raised.
Examples:
> (def rbt (make-rbtree -))
> (rbtree-put! rbt 1 'a)
> (rbtree-put! rbt 2 'b)
> (rbtree-put! rbt 3 'c)
> (rbtree->list rbt)
((1 . a) (2 . b) (3 . c))
> (rbtree-ref rbt 2 'NONE)
b
> (rbtree-ref rbt 4 'NONE) ; would signal error without default value
NONE
rbtree-get
(rbtree-get rbt key) -> any | #f
rbt := rbtree to search in
key := key to loop up in rbt
Same as (rbtree-ref rbt key #f)
.
Examples:
> (make-rbtree symbol-cmp)
#<rbtree #54>
> (rbtree-put! #54 'C "single C")
> (rbtree-put! #54 'BB "double B")
> (rbtree-put! #54 'AAA "triple A")
> (rbtree-get #54 'BB)
"double B"
> (rbtree-get #54 'D)
#f
rbtree-copy
(rbtree-copy rbt) -> rbtree
rbt := rbtree to copy
Returns a copy of rbt.
Examples:
> (rbtree->list (rbtree-copy (make-rbtree string-cmp)))
()
> (def rbt (make-rbtree string-cmp))
> (rbtree-put! rbt "op" sqrt)
> (rbtree-put! (rbtree-copy rbt) "op" +) ; not overwriting rbt
> (rbtree->list rbt)
("op" . #<procedure #63 sqrt>)
rbtree-for-each
(rbtree-for-each proc rbt) -> void
proc := procedure to be called for each key-value association in rbt
rbt := rbtree to iterate over
Evaluates (proc key val)
for every key-value association in rbt, in
ascending order. Isn't returning anything, executed for its side effects.
Examples:
> (import :std/iter)
> (make-rbtree -)
#<rbtree #66>
> (for ((key [1 2 3 4 5]) (val ["I" "II" "III" "IV" "V"]))
(rbtree-put! #66 key val))
> (rbtree-for-each (cut displayln <> " -> " <>) #66)
1 -> I
2 -> II
3 -> III
4 -> IV
5 -> V
rbtree-for-eachr
(rbtree-for-eachr proc rbt) -> void
proc := procedure to be called for each key-value association in rbt
rbt := rbtree to iterate over
rbtree-for-eachr
is similar to rbtree-for-each
, but evaluates (proc key val)
in descending (reversed) order.
Examples:
> (import :std/iter)
> (make-rbtree -)
#<rbtree #66>
> (for ((key [1 2 3 4 5]) (val ["I" "II" "III" "IV" "V"]))
(rbtree-put! #66 key val))
> (rbtree-for-eachr (cut displayln <> " -> " <>) #66)
5 -> V
4 -> IV
3 -> III
2 -> II
1 -> I
rbtree-fold
(rbtree-fold proc init rbt) -> any
proc := procedure to be called for each key-value association in rbt
init := initial value
rbt := rbtree to iterate over
Folds rbt in ascending order.
proc's signature is expected to look like this: (proc key val prev-intermediate) -> next-intermediate)
. rbtree-fold
returns the last
next-intermediate value produced by proc. Furthermore, prev-intermediate
will be set to init at the beginning.
Examples:
> (let (rbt (make-rbtree -))
(for-each (cut rbtree-put! rbt <> <>) [1 2 3] ["short" "longest" "medium"])
(rbtree-fold (lambda (k v i)
(cond ((> (string-length v) (string-length i)) v)
(else i)))
"tiny" rbt))
"longest"
rbtree-foldr
(rbtree-foldr proc init rbt) -> any
proc := procedure to be called for each key-value association in rbt
init := initial value
rbt := rbtree to iterate over
Where rbtree-fold
folds in ascending order, rbtree-foldr
folds rbt in
descending (reverse) order.
Examples:
> (def rbt (make-rbtree -))
> (rbtree-put! rbt 3 (iota 3))
> (rbtree-put! rbt 4 (iota 4))
> (rbtree-put! rbt 5 (iota 5))
> (rbtree-fold (lambda (k v i) (cons v i)) [] rbt)
((0 1 2 3 4) (0 1 2 3) (0 1 2))
> (rbtree-foldr (lambda (k v i) (cons v i)) [] rbt)
((0 1 2) (0 1 2 3) (0 1 2 3 4))
rbtree->list
(rbtree->list rbt) -> alist
rbt := rbtree
Returns an alist of key-value associations in rbt, in ascending order.
Examples:
> (list->rbtree - [[3 . "drei"] [1 . "eins"] [2 . "zwei"] [4 . "vier"]])
#<rbtree #71>
> (rbtree->list #71)
((1 . "eins") (2 . "zwei") (3 . "drei") (4 . "vier"))
rbtree->listr
(rbtree->listr rbt) -> alist
rbt := rbtree
Returns an alist of key-value associations in rbt, in descending (reverse) order.
Examples:
> (list->rbtree - [[3 . "drei"] [1 . "eins"] [2 . "zwei"] [4 . "vier"]])
#<rbtree #71>
> (rbtree->listr #71)
((4 . "vier") (3 . "drei") (2 . "zwei") (1 . "eins"))
list->rbtree
(list->rbtree cmp lst) -> rbtree
cmp := comparison procedure
lst := alist of key-value associations
Creates a new rbtree from lst, an associative list. make-rbtree
describes
how cmp is expected to look like.
Examples:
> (rbtree->list (list->rbtree symbol-cmp '((n . 3) (c . 2) (f . 1))))
((c . 2) (f . 1) (n . 3))
> (rbtree-empty? (rbtree-remove (list->rbtree - [[0 . #\x]]) 0))
#t
in-rbtree
(in-rbtree rbt) -> iterator
rbt := rbtree to iterate over
Creates an iterator over rbt, yielding key-value values in ascending order.
Examples:
> (import :std/iter)
> (def rbt (list->rbtree - '((1 . a) (2 . b) (3 . c))))
> (for/collect ((values k v) (in-rbtree rbt))
(cons k v))
((1 . a) (2 . b) (3 . c)) ; equivalent to (rbtree->list rbt)
> (for ((values k v) rbt) ; generic :iter overload for rbtree
(displayln k " -> " v))
1 -> a
2 -> b
3 -> c
in-rbtree-keys
(in-rbtree-keys rbt) -> iterator
rbt := rbtree to iterate over
Creates an iterator over rbt, yielding keys in ascending order.
Examples:
> (import :std/iter)
> (def rbt1 (list->rbtree - [[1 . #\a] [2 . #\b] [3 . #\c] [4 . #\d] [5 . #\e]]))
> (def rbt2 (list->rbtree - [[5 . #\a] [4 . #\b] [3 . #\c] [2 . #\d] [1 . #\e]]))
> (for* ((x (in-rbtree-keys rbt1)) (y (in-rbtree-keys rbt2)))
(when (> x y) ; for* is testing all combinations
(displayln (cons x y))))
(2 . 1)
(3 . 1)
(3 . 2)
(4 . 1)
(4 . 2)
(4 . 3)
(5 . 1)
(5 . 2)
(5 . 3)
(5 . 4)
in-rbtree-values
(in-rbtree-values rbt) -> iterator
rbt := rbtree to iterate over
Creates an iterator over rbt, yielding values in ascending order.
Examples:
> (import :std/iter)
> (def rbt (list->rbtree symbol-cmp '((a . (1)) (b . (2 3)) (c . (4 5 6)))))
> (for/fold (i []) (lst (in-rbtree-values rbt))
(append lst i))
(4 5 6 2 3 1)
> (import :std/misc/list)
> (for (lst (in-rbtree-values rbt))
(when (length>=n? lst 3)
(displayln lst)))
(4 5 6)
rbtree
(defsyntax rbtree)
Red-black tree (rbtree) type, for user-defined generics and destructuring.
Examples:
;; Possible rbtree iterator implementation:
> (defmethod (:iter (rbt rbtree))
(in-rbtree rbt))
> (def (in-rbtree rbt)
(def (iterate)
(rbtree-for-each yield rbt))
(in-coroutine iterate))
string-cmp
(string-cmp a b) -> fixnum
a, b := strings to compare
Comparison function for lexicographic string ordering.
Examples:
> (string-cmp "gambit" "gerbil")
-4
> (string-cmp "aaa" "aaa")
0
> (string-cmp "aac" "aaa")
2
> (string-cmp "aa" "aaaa")
-2
> (string-cmp "aaa" "")
3
> (string-cmp "a" "cb")
-2
symbol-cmp
(symbol-cmp a b) -> fixnum
a, b := symbols to compare
Comparison function for lexicographic symbol ordering.
Examples:
> (symbol-cmp 'gerbil 'gambit)
4
> (symbol-cmp 'D 'B)
2
> (symbol-cmp 'func (string->symbol "func"))
0
symbol-hash-cmp
(symbol-hash-cmp a b) -> fixnum
a, b := symbols to compare
Comparison function for symbol ordering based on their hashes; ties are broken by lexicographic ordering.
Examples:
> (symbol-hash-cmp 'gerbil 'gambit)
-173422207
> (displayln (symbol-hash 'a) " vs. " (symbol-hash 'b))
67905836 vs. 118238693
> (symbol-hash-cmp 'a 'b)
-50332857