(cond-expand (r7rs) (chicken (import r7rs))) (define-library (suspendable-procedures) (export &fail failure? success? suspend fail-forever make-generator-procedure) (import (scheme base)) (begin (define-record-type <&fail> (make-the-one-unique-&fail-that-you-must-not-make-twice) do-not-use-this:&fail?) (define &fail (make-the-one-unique-&fail-that-you-must-not-make-twice)) (define (failure? f) (eq? f &fail)) (define (success? f) (not (failure? f))) (define *suspend* (make-parameter (lambda (x) x))) (define (suspend v) ((*suspend*) v)) (define (fail-forever) (let loop () (suspend &fail) (loop))) (define (make-generator-procedure thunk) ;; This is for making a suspendable procedure that takes no ;; arguments when resumed. The result is a simple generator of ;; values. (define (next-run return) (define (my-suspend v) (set! return (call/cc (lambda (resumption-point) (set! next-run resumption-point) (return v))))) (parameterize ((*suspend* my-suspend)) (suspend (thunk)) (fail-forever))) (lambda () (call/cc next-run))) )) ;; end library (suspendable-procedures) (define-library (avl-trees) ;; ;; 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. ;; (export avl-make-generator) (export avl avl? avl-empty? avl-insert avl-search-values) (export avl-check-usage) (import (scheme base)) (import (scheme case-lambda)) (import (scheme process-context)) (import (scheme write)) (import (suspendable-procedures)) (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-make-generator (case-lambda ((tree) (avl-make-generator tree 1)) ((tree direction) (if (negative? direction) (make-generator-procedure (lambda () (define (traverse p) (unless (or (not p) (avl-empty? p)) (traverse (%right p)) (suspend (cons (%key p) (%data p))) (traverse (%left p))) &fail) (traverse tree))) (make-generator-procedure (lambda () (define (traverse p) (unless (or (not p) (avl-empty? p)) (traverse (%left p)) (suspend (cons (%key p) (%data p))) (traverse (%right p))) &fail) (traverse tree))))))) (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-make-generator array kind) (define tree-traverser (avl-make-generator (%table array))) (define get-desired-part (cond ((eq? kind 'key) (lambda (pair) (car pair))) ((eq? kind 'data) (lambda (pair) (cdr pair))) (else (lambda (pair) pair)))) (make-generator-procedure (lambda () (let traverse () (let ((tree-entry (tree-traverser))) (when (success? tree-entry) (let scan-lst ((lst (cdr tree-entry))) (when (pair? lst) (suspend (get-desired-part (car lst))) (scan-lst (cdr lst)))) (traverse)))) &fail))) (define (assoc-array-make-pair-generator array) (assoc-array-make-generator array 'pair)) (define (assoc-array-make-key-generator array) (assoc-array-make-generator array 'key)) (define (assoc-array-make-data-generator array) (assoc-array-make-generator array 'data)) (define assoc-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) (let ((hashfunc (%hashfunc array)) (pred=? (%pred=? array)) (default (%default array)) (table (%table array))) (let ((hash-value (hashfunc key))) (let*-values (((alst found?) (avl-search-values < table hash-value))) (cond (found? (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 (let* ((alst `((,key . ,data))) (table (avl-insert < table hash-value alst))) (%assoc-array hashfunc pred=? default table)))))))) (define (assoc-array-ref array key) (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 (suspendable-procedures)) (import (associative-arrays)) (define (hashfunc s) ;; Using Knuth’s random number generator to concoct a quick and ;; dirty and probably very bad hash function. It should be much ;; better to use something like SpookyHash, but this is a demo. (define a 6364136223846793005) (define c 1442695040888963407) (define M (expt 2 64)) (let ((n (string-length s)) (h 123)) (do ((i 0 (+ i 1))) ((= i n)) (let* ((x (char->integer (string-ref s i))) (x (+ (* a (+ h x)) c))) (set! h (truncate-remainder x M)))) h)) (define a (assoc-array hashfunc)) ;; Fill the associative array ‘a’ with (string . number) ;; associations. (do ((i 1 (+ i 1))) ((= i 11)) (set! a (assoc-array-set a (number->string i) i))) ;; Go through the association pairs (in arbitrary order) with a ;; generator. (let ((gen (assoc-array-make-pair-generator a))) (do ((pair (gen) (gen))) ((failure? pair)) (write pair) (display " ")) (newline)) ;; Go through the keys (in arbitrary order) with a generator. (let ((gen (assoc-array-make-key-generator a))) (do ((key (gen) (gen))) ((failure? key)) (write key) (display " ")) (newline)) ;; Go through the values (in arbitrary order) with a generator. (let ((gen (assoc-array-make-data-generator a))) (do ((value (gen) (gen))) ((failure? value)) (write value) (display " ")) (newline)) )) (else))