(cond-expand (r7rs) (chicken (import r7rs))) (define-library (avl-trees) ;; ;; This library implements ‘persistent’ (that is, ‘immutable’) AVL ;; trees for R7RS Scheme. ;; ;; References: ;; ;; * Niklaus Wirth, 1976. Algorithms + Data Structures = ;; Programs. Prentice-Hall, Englewood Cliffs, New Jersey. ;; ;; * Niklaus Wirth, 2004. Algorithms and Data Structures. Updated ;; by Fyodor Tkachov, 2014. ;; ;; THIS IS A TRIMMED-DOWN VERSION OF MY SOLUTION TO THE AVL TREES ;; TASK: https://rosettacode.org/wiki/AVL_tree#Scheme ;; (export avl) (export avl?) (export avl-empty?) (export avl-insert) (export avl-search-values) (export avl-check-usage) (import (scheme base)) (import (scheme case-lambda)) (import (scheme process-context)) (import (scheme write)) (begin (define-syntax avl-check-usage (syntax-rules () ((_ pred msg) (or pred (usage-error msg))))) (define-record-type (%avl key data bal left right) avl? (key %key) (data %data) (bal %bal) (left %left) (right %right)) (define (avl) (%avl #f #f #f #f #f)) (define (avl-empty? tree) (avl-check-usage (avl? tree) "avl-empty? expects an AVL tree as argument") (not (%bal tree))) (define (avl-search-values pred (%assoc-array hashfunc pred=? default table) assoc-array? (hashfunc %hashfunc) (pred=? %pred=?) (default %default) (table %table)) (define assoc-array ;; Create an associative array. (case-lambda ((hashfunc) (let ((pred=? equal?) (default #f)) (assoc-array hashfunc pred=? default))) ((hashfunc pred=?) (let ((default #f)) (assoc-array hashfunc pred=? default))) ((hashfunc pred=? default) (%assoc-array hashfunc pred=? default (avl))))) (define (assoc-array-set array key data) ;; Produce a new associative array that is the same as the input ;; array except for the given key-data association. The input ;; array is left unchanged (which is why the procedure is called ;; ‘assoc-array-set’ rather than ‘assoc-array-set!’). (let ((hashfunc (%hashfunc array)) (pred=? (%pred=? array)) (default (%default array)) (table (%table array))) (let ((hash-value (hashfunc key))) ;; The following could be made more efficient by combining ;; the ‘search’ and ‘insert’ operations for the AVL tree. (let*-values (((alst found?) (avl-search-values < table hash-value))) (cond (found? ;; Add a new entry to the association list. Removal of ;; any old associations with the key is not strictly ;; necessary, but without it the associative array will ;; grow every time you replace an ;; association. (Alternatively, you could occasionally ;; clean the associative array of shadowed key ;; associations.) (let* ((alst (alist-delete key alst pred=?)) (alst `((,key . ,data) . ,alst)) (table (avl-insert < table hash-value alst))) (%assoc-array hashfunc pred=? default table))) (else ;; Start a new association list. (let* ((alst `((,key . ,data))) (table (avl-insert < table hash-value alst))) (%assoc-array hashfunc pred=? default table)))))))) (define (assoc-array-ref array key) ;; Return the data associated with the key. If the key is not in ;; the table, return the associative array’s default data. (let* ((hashfunc (%hashfunc array)) (hash-value (hashfunc key))) (let*-values (((alst found?) (avl-search-values < (%table array) hash-value))) (if found? (let ((pair (assoc key alst (%pred=? array)))) (if pair (cdr pair) (%default array))) (%default array))))) )) ;; end library (associative-arrays) (cond-expand (DEMONSTRATION (begin (import (scheme base)) (import (scheme write)) (import (srfi 151)) (import (associative-arrays)) ;; I like SpookyHash, but for this demonstration I shall use the ;; simpler ‘ElfHash’ and define it only for strings. See ;; https://en.wikipedia.org/w/index.php?title=PJW_hash_function&oldid=997863283 (define (hashfunc s) (let ((n (string-length s)) (h 0)) (do ((i 0 (+ i 1))) ((= i n)) (let* ((ch ;; If the character is outside the 8-bit range, ;; probably I should break it into four bytes, each ;; incorporated separately into the hash. For this ;; demonstration, I shall simply discard the higher ;; bits. (bitwise-and (char->integer (string-ref s i)) #xFF)) (h^ (+ (arithmetic-shift h 4) ch)) (high^ (bitwise-and h^ #xF0000000))) (unless (zero? high^) (set! h^ (bitwise-xor h^ (arithmetic-shift high^ -24)))) (set! h (bitwise-and h^ (bitwise-not high^))))) h)) (let* ((a1 (assoc-array hashfunc)) (a2 (assoc-array-set a1 "A" #\A)) (a3 (assoc-array-set a2 "B" #x42)) ; ASCII ‘B’. (a4 (assoc-array-set a3 "C" "C"))) (write (assoc-array-ref a1 "A")) (newline) (write (assoc-array-ref a1 "B")) (newline) (write (assoc-array-ref a1 "C")) (newline) (write (assoc-array-ref a2 "A")) (newline) (write (assoc-array-ref a2 "B")) (newline) (write (assoc-array-ref a2 "C")) (newline) (write (assoc-array-ref a3 "A")) (newline) (write (assoc-array-ref a3 "B")) (newline) (write (assoc-array-ref a3 "C")) (newline) (write (assoc-array-ref a4 "A")) (newline) (write (assoc-array-ref a4 "B")) (newline) (write (assoc-array-ref a4 "C")) (newline)) )) (else))