123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- (library (binary-tree)
- (export <tree>
- make-tree
- make-leaf
- make-empty-tree
- make-complete-tree
- make-balanced-tree
- create-balanced-trees-pair
- tree?
- tree-empty?
- tree-value
- tree-left
- tree-right
- set-tree-left
- set-tree-right
- set-tree-value
- tree-leaf?
- tree-member?
- tree-insert
- tree-insert-using-continuation
- tree-insert-with-exception
- tree-depth
- tree-size
- tree-fold
- display-tree
- make-already-exists-exception
- &already-exists
- already-exists?)
- (import (except (rnrs base) error vector-map)
- (only (guile)
- lambda*
- λ
- call-with-output-string
- current-output-port
- display
- error
- even?
- floor
- record-constructor
- simple-format)
- ;; SRFI-9 for structs
- (srfi srfi-9 gnu)
- ;; let-values
- (srfi srfi-11)
- (ice-9 exceptions)
- (string-helpers))
- (define-immutable-record-type <tree>
- (make-tree value left right)
- tree?
- (left tree-left set-tree-left)
- (right tree-right set-tree-right)
- (value tree-value set-tree-value))
- (define-immutable-record-type <empty-tree>
- (make-empty-tree)
- empty-tree?)
- (define make-leaf
- (λ (val)
- (make-tree val
- (make-empty-tree)
- (make-empty-tree))))
- (define tree-empty?
- (λ (tree)
- (empty-tree? tree)))
- (define tree-leaf?
- (λ (a-tree)
- (and (tree-empty? (tree-left a-tree))
- (tree-empty? (tree-right a-tree)))))
- ;; tree-member? makes use of a performance trick to reduce the
- ;; number of comparisons needed to find out, whether a value is a
- ;; member or not. The trick is to keep track of a candidate element
- ;; which could be equal to the value that is searched and drawing
- ;; conclusions from comparisons with elements deeper in the tree.
- (define tree-member?
- (λ (a-tree searched-val less)
- (cond
- [(tree-empty? a-tree) #f]
- [else
- (let traverse ([tree° a-tree] [candidate° #f])
- (let ([node-val (tree-value tree°)])
- (cond
- [(less searched-val node-val)
- (let ([left (tree-left tree°)])
- (if (tree-empty? left)
- ;; Not possible to go deeper left. Compare with
- ;; candidate.
- (and candidate°
- (not (less candidate° searched-val)))
- ;; Otherwise descent left.
- (traverse left candidate°)))]
- [else
- (let ([right (tree-right tree°)])
- (cond
- [(tree-empty? right)
- ;; Not possible to go deeper right. Compare with
- ;; candidate.
- (and node-val
- (not (less node-val searched-val)))]
- [else
- (traverse right node-val)]))])))])))
- ;; The GNU Guile manual recommends to only use
- ;; call-with-current-continuation, when elegance warants it or no
- ;; other simple solution is available. Still putting it here to show
- ;; it.
- (define tree-insert-using-continuation
- (λ (tree insertion-value less)
- (call-with-current-continuation
- (λ (exit-cont)
- (cond
- [(tree-empty? tree) (make-leaf insertion-value)]
- [else
- (let traverse ([tree° tree])
- (let ([node-val (tree-value tree°)])
- (cond
- [(less insertion-value node-val)
- (set-tree-left tree°
- (tree-insert-using-continuation (tree-left tree°)
- insertion-value
- less))]
- [(less node-val insertion-value)
- (set-tree-right tree°
- (tree-insert-using-continuation (tree-right tree°)
- insertion-value
- less))]
- [else
- (exit-cont tree)])))])))))
- ;; To use an exception, we define one.
- (define &already-exists (make-exception-type '&already-exists &exception '()))
- (define already-exists? (exception-predicate &already-exists))
- (define make-already-exists-exception
- (λ (irritants)
- (make-exception
- ((record-constructor &already-exists))
- (make-exception-with-message "value already exists in tree")
- (make-exception-with-irritants irritants))))
- (define tree-insert
- (λ (tree insertion-value less)
- (guard (exn [(already-exists? exn) tree])
- (cond
- [(tree-empty? tree) (make-leaf insertion-value)]
- [else
- (let traverse ([tree° tree] [candidate° #f])
- (let ([node-val (tree-value tree°)])
- (cond
- [(less insertion-value node-val)
- ;; Check if left subtree is empty. If it is empty,
- ;; compare with the memorized candidate. If equal to
- ;; the candidate value, raise the exception, otherwise
- ;; insert as a new left subtree.
- (let ([left (tree-left tree°)])
- (cond
- [(tree-empty? left)
- (if (and candidate° (not (less candidate° insertion-value)))
- (raise-exception
- (make-already-exists-exception insertion-value))
- (set-tree-left tree° (make-leaf insertion-value)))]
- [else
- (traverse left candidate°)]))]
- [else
- ;; Check if the right subtree is empty. (1) If it is
- ;; empty, compare with the memorized candidate. If the
- ;; candidate is equal to the insertion value, raise
- ;; the exception, otherwise insert the insertion value
- ;; as a new right subtree. (2) If the right subtree is
- ;; not empty, memorize the value at the current node
- ;; and insert into the right subtree.
- (let ([right (tree-right tree°)])
- (cond
- [(tree-empty? right)
- (if (and candidate° (not (less candidate° insertion-value)))
- (raise-exception
- (make-already-exists-exception insertion-value))
- (set-tree-right tree° (make-leaf insertion-value)))]
- [else
- (traverse right node-val)]))])))]))))
- (define make-complete-tree
- (λ (fill depth)
- "Build a full binary tree bottom up, enabling to share
- already created subtrees for both, left and right, branches
- of each next higher level."
- (cond
- [(= depth 0) (make-empty-tree)]
- [else
- (let iter-depth ([tree° (make-leaf fill)]
- [depth° (- depth 1)])
- (cond
- [(= depth° 0) tree°]
- [else
- (iter-depth (make-tree fill tree° tree°)
- (- depth° 1))]))])))
- (define tree-depth
- (λ (tree)
- "Calculate the tree depth. That is, how many splits the tree
- has in its longest path from the root to any leaf."
- (cond
- [(tree-empty? tree) 0]
- [else
- (+ 1 (max (tree-depth (tree-left tree))
- (tree-depth (tree-right tree))))])))
- (define create-balanced-trees-pair
- (λ (fill size)
- "Make a pair of balanced trees, one tree of size SIZE and
- one tree of SIZE + 1. This is according to the hint in Chris
- Okasaki's book 'Purely Functional Data Structures'."
- (simple-format #t "create2 got size: ~a\n" size)
- (values (make-balanced-tree fill (+ size 1))
- (make-balanced-tree fill size))))
- (define make-balanced-tree
- (λ (fill num-nodes)
- "Create a balanced binary tree. Should run in O(log(n)),
- where n is the number of nodes."
- (cond
- [(= num-nodes 0)
- (make-empty-tree)]
- [(even? num-nodes)
- (let ([subtree-size (floor (/ (- num-nodes 1) 2))])
- (let-values ([(left-subtree right-subtree)
- (create-balanced-trees-pair fill subtree-size)])
- (make-tree fill
- left-subtree
- right-subtree)))]
- [else
- (let ([subtree-size (/ (- num-nodes 1) 2)])
- (let ([subtree (make-balanced-tree fill subtree-size)])
- (make-tree fill
- ;; Share equal subtrees, only do half the work.
- subtree
- subtree)))])))
- (define tree-size
- (λ (tree)
- (cond
- [(empty-tree? tree) 0]
- [else (+ 1
- (tree-size (tree-left tree))
- (tree-size (tree-right tree)))])))
- (define tree-fold
- (λ (reducing-func neutral-elem tree)
- "Fold operation over a tree. Reduce left branch, then right
- branch, then the parent node value."
- (cond
- [(tree-empty? tree) neutral-elem]
- [(tree-leaf? tree)
- (reducing-func (tree-value tree)
- neutral-elem)]
- [else
- (reducing-func (tree-value tree)
- (reducing-func
- (tree-fold reducing-func neutral-elem (tree-left tree))
- (tree-fold reducing-func neutral-elem (tree-right tree))))])))
- (define display-tree
- (lambda* (tree
- #:optional (port (current-output-port))
- #:key
- (indentation-str " ")
- (tree-value->string (λ (val) (simple-format #f "~s" val))))
- (define display-value-at-level
- (lambda* (val level)
- (simple-format
- port "~a\n"
- (string-append (string-repeat indentation-str level)
- (tree-value->string val)))))
- (let iter ([tree° tree] [level 0])
- (display-value-at-level (tree-value tree°) level)
- (cond
- [(tree-leaf? tree°)
- (display-value-at-level 'empty (+ level 1))
- (display-value-at-level 'empty (+ level 1))]
- [(tree-empty? (tree-left tree°))
- (display-value-at-level 'empty (+ level 1))
- (iter (tree-right tree°) (+ level 1))]
- [(tree-empty? (tree-right tree°))
- (iter (tree-left tree°) (+ level 1))
- (display-value-at-level 'empty (+ level 1))]
- [else
- (iter (tree-left tree°) (+ level 1))
- (iter (tree-right tree°) (+ level 1))]))))
- ;; TODO: implement tree-balanced?
- ;; TODO: tree-leaf-count
- )
- ;; (define tree-leaf-count
- ;; (λ (a-tree)
- ;; (cond
- ;; [(leaf? a-tree) 1]
- ;; [(branch-empty? (tree-left a-tree))
- ;; (tree-leaf-count (tree-right a-tree))]
- ;; [(branch-empty? (tree-right a-tree))
- ;; (tree-leaf-count (tree-left a-tree))]
- ;; [else (+ (tree-leaf-count (tree-left a-tree))
- ;; (tree-leaf-count (tree-right a-tree)))])))
|