123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- ;;; srfi-126.scm -- SRFI 126 - R6RS-based hashtables.
- ;; Copyright (C) Taylan Ulrich Bayırlı/Kammer (2015, 2016). All Rights Reserved.
- ;;
- ;; Permission is hereby granted, free of charge, to any person obtaining
- ;; a copy of this software and associated documentation files (the
- ;; "Software"), to deal in the Software without restriction, including
- ;; without limitation the rights to use, copy, modify, merge, publish,
- ;; distribute, sublicense, and/or sell copies of the Software, and to
- ;; permit persons to whom the Software is furnished to do so, subject to
- ;; the following conditions:
- ;;
- ;; The above copyright notice and this permission notice shall be
- ;; included in all copies or substantial portions of the Software.
- ;;
- ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- ;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- ;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- ;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- ;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- ;; SOFTWARE.
- (define-module (srfi srfi-126)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-9 gnu)
- #:use-module (srfi srfi-11)
- #:use-module (ice-9 control)
- #:use-module ((rnrs hashtables) #:select (equal-hash
- string-hash
- string-ci-hash))
- #:export (make-eq-hashtable
- make-eqv-hashtable make-hashtable
- alist->eq-hashtable alist->eqv-hashtable alist->hashtable
- weakness
- hashtable? hashtable-size
- hashtable-ref hashtable-set! hashtable-delete! hashtable-contains?
- hashtable-lookup hashtable-update! hashtable-intern!
- hashtable-copy hashtable-clear! hashtable-empty-copy
- hashtable-keys hashtable-values hashtable-entries
- hashtable-key-list hashtable-value-list hashtable-entry-lists
- hashtable-walk hashtable-update-all! hashtable-prune!
- hashtable-merge!
- hashtable-sum hashtable-map->lset hashtable-find
- hashtable-empty? hashtable-pop! hashtable-inc! hashtable-dec!
- hashtable-equivalence-function hashtable-hash-function
- hashtable-weakness hashtable-mutable?
- hash-salt)
- #:re-export (equal-hash string-hash string-ci-hash))
- (define-record-type <hashtable>
- (%make-hashtable %table %hash %assoc hash equiv weakness mutable)
- hashtable?
- (%table %hashtable-table)
- (%hash %hashtable-hash)
- (%assoc %hashtable-assoc)
- (hash hashtable-hash-function)
- (equiv hashtable-equivalence-function)
- (weakness hashtable-weakness)
- (mutable hashtable-mutable? %hashtable-set-mutable!))
- (define nil (cons #f #f))
- (define (nil? obj) (eq? obj nil))
- (define (make-table capacity weakness)
- (let ((capacity (or capacity 32)))
- (case weakness
- ((#f) (make-hash-table capacity))
- ((weak-key) (make-weak-key-hash-table capacity))
- ((weak-value) (make-weak-value-hash-table capacity))
- ((weak-key-and-value) (make-doubly-weak-hash-table capacity))
- (else (error "Hashtable weakness not supported." weakness)))))
- (define* (make-eq-hashtable #:optional capacity weakness)
- (let ((table (make-table capacity weakness)))
- (%make-hashtable table hashq assq #f eq? weakness #t)))
- (define* (make-eqv-hashtable #:optional capacity weakness)
- (let ((table (make-table capacity weakness)))
- (%make-hashtable table hashv assv #f eqv? weakness #t)))
- (define* (make-hashtable hash equiv #:optional capacity weakness)
- (cond
- ((and (not hash) (eq? equiv eq?))
- (make-eq-hashtable capacity weakness))
- ((and (not hash) (eq? equiv eqv?))
- (make-eqv-hashtable capacity weakness))
- (else
- (let* ((table (make-table capacity weakness))
- (hash (if (pair? hash)
- (car hash)
- hash))
- (%hash (lambda (obj bound)
- (modulo (hash obj) bound)))
- (assoc (lambda (key alist)
- (find (lambda (entry)
- (equiv (car entry) key))
- alist))))
- (%make-hashtable table %hash assoc hash equiv weakness #t)))))
- (define (alist->eq-hashtable . args)
- (apply alist->hashtable #f eq? args))
- (define (alist->eqv-hashtable . args)
- (apply alist->hashtable #f eqv? args))
- (define alist->hashtable
- (case-lambda
- ((hash equiv alist)
- (alist->hashtable hash equiv #f #f alist))
- ((hash equiv capacity alist)
- (alist->hashtable hash equiv capacity #f alist))
- ((hash equiv capacity weakness alist)
- (let ((ht (make-hashtable hash equiv capacity weakness)))
- (for-each (lambda (entry)
- (hashtable-set! ht (car entry) (cdr entry)))
- (reverse alist))
- ht))))
- (define-syntax weakness
- (lambda (stx)
- (syntax-case stx ()
- ((_ <sym>)
- (let ((sym (syntax->datum #'<sym>)))
- (case sym
- ((weak-key weak-value weak-key-and-value ephemeral-key
- ephemeral-value ephemeral-key-and-value)
- #''sym)
- (else
- (error "Bad weakness symbol." sym))))))))
- (define (hashtable-size ht)
- (hash-count (const #t) (%hashtable-table ht)))
- (define* (%hashtable-ref ht key default)
- (hashx-ref (%hashtable-hash ht) (%hashtable-assoc ht)
- (%hashtable-table ht) key default))
- (define* (hashtable-ref ht key #:optional (default nil))
- (let ((val (%hashtable-ref ht key default)))
- (if (nil? val)
- (error "No association for key in hashtable." key ht)
- val)))
- (define (assert-mutable ht)
- (when (not (hashtable-mutable? ht))
- (error "Hashtable is immutable." ht)))
- (define (hashtable-set! ht key value)
- (assert-mutable ht)
- (hashx-set! (%hashtable-hash ht) (%hashtable-assoc ht)
- (%hashtable-table ht) key value)
- *unspecified*)
- (define (hashtable-delete! ht key)
- (assert-mutable ht)
- (hashx-remove! (%hashtable-hash ht) (%hashtable-assoc ht)
- (%hashtable-table ht) key)
- *unspecified*)
- (define (hashtable-contains? ht key)
- (not (nil? (%hashtable-ref ht key nil))))
- (define (hashtable-lookup ht key)
- (let ((val (%hashtable-ref ht key nil)))
- (if (nil? val)
- (values #f #f)
- (values val #t))))
- (define* (hashtable-update! ht key updater #:optional (default nil))
- (assert-mutable ht)
- (let ((handle (hashx-create-handle!
- (%hashtable-hash ht) (%hashtable-assoc ht)
- (%hashtable-table ht) key nil)))
- (if (eq? nil (cdr handle))
- (if (nil? default)
- (error "No association for key in hashtable." key ht)
- (set-cdr! handle (updater default)))
- (set-cdr! handle (updater (cdr handle))))
- (cdr handle)))
- (define (hashtable-intern! ht key default-proc)
- (assert-mutable ht)
- (let ((handle (hashx-create-handle!
- (%hashtable-hash ht) (%hashtable-assoc ht)
- (%hashtable-table ht) key nil)))
- (when (nil? (cdr handle))
- (set-cdr! handle (default-proc)))
- (cdr handle)))
- (define* (hashtable-copy ht #:optional mutable weakness)
- (let ((copy (hashtable-empty-copy ht (hashtable-size ht) weakness)))
- (hashtable-walk ht
- (lambda (k v)
- (hashtable-set! copy k v)))
- (%hashtable-set-mutable! copy mutable)
- copy))
- (define* (hashtable-clear! ht #:optional _capacity)
- (assert-mutable ht)
- (hash-clear! (%hashtable-table ht))
- *unspecified*)
- (define* (hashtable-empty-copy ht #:optional capacity weakness)
- (make-hashtable (hashtable-hash-function ht)
- (hashtable-equivalence-function ht)
- (case capacity
- ((#f) #f)
- ((#t) (hashtable-size ht))
- (else capacity))
- (or weakness (hashtable-weakness ht))))
- (define (hashtable-keys ht)
- (let ((keys (make-vector (hashtable-size ht))))
- (hashtable-sum ht 0
- (lambda (k v i)
- (vector-set! keys i k)
- (+ i 1)))
- keys))
- (define (hashtable-values ht)
- (let ((vals (make-vector (hashtable-size ht))))
- (hashtable-sum ht 0
- (lambda (k v i)
- (vector-set! vals i v)
- (+ i 1)))
- vals))
- (define (hashtable-entries ht)
- (let ((keys (make-vector (hashtable-size ht)))
- (vals (make-vector (hashtable-size ht))))
- (hashtable-sum ht 0
- (lambda (k v i)
- (vector-set! keys i k)
- (vector-set! vals i v)
- (+ i 1)))
- (values keys vals)))
- (define (hashtable-key-list ht)
- (hashtable-map->lset ht (lambda (k v) k)))
- (define (hashtable-value-list ht)
- (hashtable-map->lset ht (lambda (k v) v)))
- (define (hashtable-entry-lists ht)
- (let ((keys&vals (cons '() '())))
- (hashtable-walk ht
- (lambda (k v)
- (set-car! keys&vals (cons k (car keys&vals)))
- (set-cdr! keys&vals (cons v (cdr keys&vals)))))
- (car+cdr keys&vals)))
- (define (hashtable-walk ht proc)
- (hash-for-each proc (%hashtable-table ht)))
- (define (hashtable-update-all! ht proc)
- (assert-mutable ht)
- (hash-for-each-handle
- (lambda (handle)
- (set-cdr! handle (proc (car handle) (cdr handle))))
- (%hashtable-table ht)))
- (define (hashtable-prune! ht pred)
- (assert-mutable ht)
- (let ((keys (hashtable-sum ht '()
- (lambda (k v keys-to-delete)
- (if (pred k v)
- (cons k keys-to-delete)
- keys-to-delete)))))
- (for-each (lambda (k)
- (hashtable-delete! ht k))
- keys)))
- (define (hashtable-merge! ht-dest ht-src)
- (assert-mutable ht-dest)
- (hashtable-walk ht-src
- (lambda (k v)
- (hashtable-set! ht-dest k v)))
- ht-dest)
- (define (hashtable-sum ht init proc)
- (hash-fold proc init (%hashtable-table ht)))
- (define (hashtable-map->lset ht proc)
- (hash-map->list proc (%hashtable-table ht)))
- (define (hashtable-find ht pred)
- (let/ec return
- (hashtable-walk ht
- (lambda (k v)
- (when (pred k v)
- (return k v #t))))
- (return #f #f #f)))
- (define (hashtable-empty? ht)
- (zero? (hashtable-size ht)))
- (define (hashtable-pop! ht)
- (assert-mutable ht)
- (when (hashtable-empty? ht)
- (error "Cannot pop from empty hashtable." ht))
- (let-values (((k v found?) (hashtable-find ht (const #t))))
- (hashtable-delete! ht k)
- (values k v)))
- (define* (hashtable-inc! ht k #:optional (x 1))
- (assert-mutable ht)
- (hashtable-update! ht k (lambda (v) (+ v x)) 0))
- (define* (hashtable-dec! ht k #:optional (x 1))
- (assert-mutable ht)
- (hashtable-update! ht k (lambda (v) (- v x)) 0))
- (define (hash-salt) 0)
- (set-record-type-printer!
- <hashtable>
- (lambda (ht port)
- (with-output-to-port port
- (lambda ()
- (let ((equal-hash (@ (rnrs hashtables) equal-hash))
- (string-hash (@ (rnrs hashtables) string-hash))
- (string-ci-hash (@ (rnrs hashtables) string-ci-hash))
- (symbol-hash (@ (rnrs hashtables) symbol-hash))
- (hash (hashtable-hash-function ht))
- (equiv (hashtable-equivalence-function ht))
- (alist (hashtable-map->lset ht cons)))
- (cond
- ((and (not hash) (eq? equiv eq?))
- (display "#hasheq")
- (display alist))
- ((and (not hash) (eq? equiv eqv?))
- (display "#hasheqv")
- (display alist))
- (else
- (display "#hash")
- (cond
- ((and (eq? hash (@ (rnrs hashtables) equal-hash)) (eq? equiv equal?))
- (display alist))
- ((and (eq? hash (@ (rnrs hashtables) string-hash)) (eq? equiv string=?))
- (display (cons 'string alist)))
- ((and (eq? hash string-ci-hash) (eq? equiv string-ci=?))
- (display (cons 'string-ci alist)))
- ((and (eq? hash symbol-hash) (eq? equiv eq?))
- (display (cons 'symbol alist)))
- (else
- (display (cons 'custom alist)))))))))))
- (read-hash-extend
- #\h
- (lambda (char port)
- (with-input-from-port port
- (lambda ()
- (let ((equal-hash (@ (rnrs hashtables) equal-hash))
- (string-hash (@ (rnrs hashtables) string-hash))
- (string-ci-hash (@ (rnrs hashtables) string-ci-hash))
- (symbol-hash (@ (rnrs hashtables) symbol-hash))
- (type (string-append "h" (symbol->string (read))))
- (alist (read)))
- (cond
- ((string=? type "hasheq")
- (alist->eq-hashtable alist))
- ((string=? type "hasheqv")
- (alist->eqv-hashtable alist))
- (else
- (when (not (string=? type "hash"))
- (error "Unrecognized hash type." type))
- (let* ((has-tag? (symbol? (car alist)))
- (subtype (if has-tag?
- (car alist)
- "equal"))
- (alist (if has-tag?
- (cdr alist)
- alist)))
- (cond
- ((string=? subtype "equal")
- (alist->hashtable equal-hash equal? alist))
- ((string=? subtype "string")
- (alist->hashtable string-hash string=? alist))
- ((string=? subtype "string-ci")
- (alist->hashtable string-ci-hash string-ci=? alist))
- ((string=? subtype "symbol")
- (alist->hashtable symbol-hash eq? alist))
- (else
- (error "Unrecognized hash subtype." subtype)))))))))))
- ;; Local Variables:
- ;; eval: (put 'hashtable-walk 'scheme-indent-function 1)
- ;; eval: (put 'hashtable-update-all! 'scheme-indent-function 1)
- ;; eval: (put 'hashtable-prune! 'scheme-indent-function 1)
- ;; eval: (put 'hashtable-sum 'scheme-indent-function 2)
- ;; eval: (put 'hashtable-map->lset 'scheme-indent-function 1)
- ;; eval: (put 'hashtable-find 'scheme-indent-function 1)
- ;; End:
|