diff --git a/immutable-hashtable.sls b/immutable-hashtable.sls new file mode 100644 index 0000000..2ff79c3 --- /dev/null +++ b/immutable-hashtable.sls @@ -0,0 +1,173 @@ +(library (lassik immutable-hashtable) + (export make-eq-hashtable + make-eqv-hashtable + make-hashtable + + hashtable-contains? + hashtable-entries + hashtable-equivalence-function + hashtable-hash-function + hashtable-keys + hashtable-mutable? + hashtable-ref + hashtable-size + hashtable? + immutable-hashtable? + + equal-hash + string-ci-hash + string-hash + symbol-hash + + hashtable-delete + hashtable-set + hashtable-update) + (import (rnrs base) + (rename (except (rnrs hashtables) + make-hashtable + make-eq-hashtable + make-eqv-hashtable) + (hashtable? + rnrs-hashtable?) + (hashtable-mutable? + rnrs-hashtable-mutable?) + (hashtable-size + rnrs-hashtable-size) + (hashtable-contains? + rnrs-hashtable-contains?) + (hashtable-entries + rnrs-hashtable-entries) + (hashtable-equivalence-function + rnrs-hashtable-equivalence-function) + (hashtable-hash-function + rnrs-hashtable-hash-function) + (hashtable-keys + rnrs-hashtable-keys) + (hashtable-ref + rnrs-hashtable-ref))) + (begin + + ;; From SRFI 1. + (define (append-reverse list1 list2) + (append (reverse list1) list2)) + + ;; From R7RS. + (define (assoc key alist equivalent?) + (cond ((null? alist) #f) + ((equivalent? key (caar alist)) (car alist)) + (else (assoc key (cdr alist) equivalent?)))) + + ;; + + (define (immutable-hashtable? obj) + (and (vector? obj) (eq? 'immutable-hashtable (vector-ref obj 0)))) + + (define (hashtable? obj) + (or (rnrs-hashtable? obj) + (immutable-hashtable? obj))) + + (define (hashtable-mutable? table) + (if (rnrs-hashtable? table) + (rnrs-hashtable-mutable? table) + #f)) + + ;; + + (define (make-hashtable* hash equivalent? alist) + (vector 'immutable-hashtable hash equivalent? alist)) + + (define (make-hashtable hash equivalent?) + (make-hashtable* hash equivalent? '())) + + (define (make-eq-hashtable) + (make-hashtable #f eq?)) + + (define (make-eqv-hashtable) + (make-hashtable #f eqv?)) + + ;; + + (define (hashtable-hash-function table) + (if (rnrs-hashtable? table) + (rnrs-hashtable-hash-function table) + (vector-ref table 1))) + + (define (hashtable-equivalence-function table) + (if (rnrs-hashtable? table) + (rnrs-hashtable-equivalence-function table) + (vector-ref table 2))) + + (define (hashtable-alist table) + (vector-ref table 3)) + + (define (hashtable-assoc table key) + (assoc key + (hashtable-alist table) + (hashtable-equivalence-function table))) + + ;; + + (define (hashtable-contains? table key) + (if (rnrs-hashtable? table) + (rnrs-hashtable-contains? table key) + (not (not (hashtable-assoc table key))))) + + (define (hashtable-keys table) + (if (rnrs-hashtable? table) + (rnrs-hashtable-keys table) + (list->vector (map car (hashtable-alist table))))) + + (define (hashtable-entries table) + (if (rnrs-hashtable? table) + (rnrs-hashtable-entries table) + (values (list->vector (map car (hashtable-alist table))) + (list->vector (map cdr (hashtable-alist table)))))) + + (define (hashtable-ref table key default) + (if (rnrs-hashtable? table) + (rnrs-hashtable-ref table key default) + (let ((entry (hashtable-assoc table key))) + (if entry (cdr entry) default)))) + + (define (hashtable-size table) + (if (rnrs-hashtable? table) + (rnrs-hashtable-size table) + (length (hashtable-alist table)))) + + ;; + + (define (hashtable-delete table key) + (let ((equivalent? (hashtable-equivalence-function table))) + (make-hashtable* + (hashtable-hash-function table) + equivalent? + (let loop ((old-alist (hashtable-alist table)) + (new-alist '())) + (if (null? old-alist) (reverse new-alist) + (loop (cdr old-alist) + (if (equivalent? key (caar old-alist)) + new-alist + (cons (car old-alist) new-alist)))))))) + + (define (hashtable-update table key update default) + (let ((equivalent? (hashtable-equivalence-function table))) + (make-hashtable* + (hashtable-hash-function table) + equivalent? + (let loop ((old-alist (hashtable-alist table)) + (new-alist '())) + (cond ((null? old-alist) + (reverse (cons (cons key (update default)) + new-alist))) + ((equivalent? key (caar old-alist)) + (append-reverse (cons (cons (caar old-alist) + (update (cdar old-alist))) + new-alist) + (cdr old-alist))) + (else + (loop (cdr old-alist) + (cons (car old-alist) + new-alist)))))))) + + (define (hashtable-set table key value) + (hashtable-update table key (lambda (_) value) #f))))