163 lines
5.9 KiB
Racket
163 lines
5.9 KiB
Racket
#lang scheme/base
|
|
|
|
(require scheme/dict)
|
|
|
|
(provide make-eq-hashtable
|
|
make-eqv-hashtable
|
|
(rename-out [r6rs:make-hashtable make-hashtable])
|
|
hashtable?
|
|
hashtable-size
|
|
hashtable-ref
|
|
hashtable-set!
|
|
hashtable-delete!
|
|
hashtable-contains?
|
|
hashtable-update!
|
|
hashtable-copy
|
|
hashtable-clear!
|
|
hashtable-keys
|
|
hashtable-entries
|
|
hashtable-equivalence-function
|
|
hashtable-hash-function
|
|
hashtable-mutable?
|
|
equal-hash
|
|
string-hash
|
|
string-ci-hash
|
|
symbol-hash)
|
|
|
|
(define-struct hashtable ([ht #:mutable]
|
|
wrap
|
|
unwrap
|
|
mutable?
|
|
equivalence-function
|
|
hash-function)
|
|
#:property prop:dict
|
|
(vector (case-lambda
|
|
[(ht key)
|
|
(hash-ref (hashtable-ht ht) ((hashtable-wrap ht) key))]
|
|
[(ht key default)
|
|
(hash-ref (hashtable-ht ht) ((hashtable-wrap ht) key) default)])
|
|
(lambda (ht key val) (hashtable-set! ht key val))
|
|
#f
|
|
(lambda (ht key) (hashtable-delete! ht key))
|
|
#f
|
|
(lambda (ht) (hashtable-size ht))
|
|
(lambda (ht) (hash-iterate-first (hashtable-ht ht)))
|
|
(lambda (ht pos) (hash-iterate-next (hashtable-ht ht) pos))
|
|
(lambda (ht pos) ((hashtable-unwrap ht)
|
|
(hash-iterate-key (hashtable-ht ht) pos)))
|
|
(lambda (ht pos) (hash-iterate-value (hashtable-ht ht) pos))))
|
|
|
|
(define-struct eqv-box (val)
|
|
#:property prop:equal+hash (list
|
|
(lambda (a b recur) (eqv? (eqv-box-val a)
|
|
(eqv-box-val b)))
|
|
(lambda (v recur) (equal-hash-code (eqv-box-val v)))
|
|
(lambda (v recur) (equal-secondary-hash-code (eqv-box-val v)))))
|
|
|
|
|
|
(define (make-eq-hashtable [k 0])
|
|
(unless (exact-nonnegative-integer? k)
|
|
(raise-type-error 'make-eq-hashtable "exact, nonnegative integer" k))
|
|
(make-hashtable (make-hasheq) values values #t eq? #f))
|
|
|
|
(define (make-eqv-hashtable [k 0])
|
|
(unless (exact-nonnegative-integer? k)
|
|
(raise-type-error 'make-eqv-hashtable "exact, nonnegative integer" k))
|
|
(make-hashtable (make-hash) make-eqv-box eqv-box-val #t eqv? #f))
|
|
|
|
(define r6rs:make-hashtable
|
|
(let ([make-hashtable
|
|
(lambda (hash =? [k 0])
|
|
(unless (and (procedure? hash)
|
|
(procedure-arity-includes? hash 1))
|
|
(raise-type-error 'make-hashtable "procedure (arity 1)" hash))
|
|
(unless (and (procedure? =?)
|
|
(procedure-arity-includes? =? 2))
|
|
(raise-type-error 'make-hashtable "procedure (arity 2)" =?))
|
|
(unless (exact-nonnegative-integer? k)
|
|
(raise-type-error 'make-hashtable "exact, nonnegative integer" k))
|
|
(let ()
|
|
(define-struct hash-box (val)
|
|
#:property prop:equal+hash (list
|
|
(lambda (a b recur) (=? (hash-box-val a)
|
|
(hash-box-val b)))
|
|
(lambda (v recur) (hash (hash-box-val v)))
|
|
(lambda (v recur) 10001)))
|
|
(make-hashtable (make-hash) make-hash-box hash-box-val #t =? hash)))])
|
|
make-hashtable))
|
|
|
|
(define (hashtable-size ht)
|
|
(hash-count (hashtable-ht ht)))
|
|
|
|
(define tag (gensym))
|
|
|
|
(define (hashtable-ref ht key default)
|
|
(let ([v (hash-ref (hashtable-ht ht) ((hashtable-wrap ht) key) tag)])
|
|
(if (eq? v tag)
|
|
default
|
|
v)))
|
|
|
|
(define (hashtable-set! ht key val)
|
|
(if (hashtable-mutable? ht)
|
|
(hash-set! (hashtable-ht ht) ((hashtable-wrap ht) key) val)
|
|
(raise-type-error 'hashtable-set! "mutable hashtable" ht)))
|
|
|
|
(define (hashtable-delete! ht key)
|
|
(if (hashtable-mutable? ht)
|
|
(hash-remove! (hashtable-ht ht) ((hashtable-wrap ht) key))
|
|
(raise-type-error 'hashtable-delete! "mutable hashtable" ht)))
|
|
|
|
(define (hashtable-contains? ht key)
|
|
(not (eq? (hash-ref (hashtable-ht ht) ((hashtable-wrap ht) key) tag)
|
|
tag)))
|
|
|
|
(define (hashtable-update! ht key proc default)
|
|
(if (hashtable-mutable? ht)
|
|
(hashtable-set! ht key (proc (hashtable-ref ht key default)))
|
|
(raise-type-error 'hashtable-update! "mutable hashtable" ht)))
|
|
|
|
(define (hashtable-copy ht [mutable? #f])
|
|
(make-hashtable (hash-copy (hashtable-ht ht))
|
|
(hashtable-wrap ht)
|
|
(hashtable-unwrap ht)
|
|
mutable?
|
|
(hashtable-equivalence-function ht)
|
|
(hashtable-hash-function ht)))
|
|
|
|
(define (hashtable-clear! ht [k 0])
|
|
(unless (exact-nonnegative-integer? k)
|
|
(raise-type-error 'hashtable-clear! "exact, nonnegative integer" k))
|
|
(if (hashtable-mutable? ht)
|
|
(set-hashtable-ht! ht (if (eq? values (hashtable-wrap ht))
|
|
(make-hasheq)
|
|
(make-hash)))
|
|
(raise-type-error 'hashtable-clear! "mutable hashtable" ht)))
|
|
|
|
(define (hashtable-keys ht)
|
|
(let ([unwrap (hashtable-unwrap ht)])
|
|
(list->vector (hash-map (hashtable-ht ht) (lambda (a b) (unwrap a))))))
|
|
|
|
(define (hashtable-entries ht)
|
|
(let ([ps (hash-map (hashtable-ht ht) cons)]
|
|
[unwrap (hashtable-unwrap ht)])
|
|
(values (list->vector (map (lambda (p) (unwrap (car p))) ps))
|
|
(list->vector (map cdr ps)))))
|
|
|
|
(define (equal-hash v)
|
|
(abs (equal-hash-code v)))
|
|
|
|
(define (string-hash s)
|
|
(unless (string? s)
|
|
(raise-type-error 'string-hash "string" s))
|
|
(abs (equal-hash-code s)))
|
|
|
|
(define (string-ci-hash s)
|
|
(unless (string? s)
|
|
(raise-type-error 'string-ci-hash "string" s))
|
|
(abs (equal-hash-code (string-foldcase s))))
|
|
|
|
(define (symbol-hash s)
|
|
(unless (symbol? s)
|
|
(raise-type-error 'symbol-hash "symbol" s))
|
|
(abs (eq-hash-code s)))
|