srfi-126.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. ;;; srfi-126.scm -- SRFI 126 - R6RS-based hashtables.
  2. ;; Copyright (C) Taylan Ulrich Bayırlı/Kammer (2015, 2016). All Rights Reserved.
  3. ;;
  4. ;; Permission is hereby granted, free of charge, to any person obtaining
  5. ;; a copy of this software and associated documentation files (the
  6. ;; "Software"), to deal in the Software without restriction, including
  7. ;; without limitation the rights to use, copy, modify, merge, publish,
  8. ;; distribute, sublicense, and/or sell copies of the Software, and to
  9. ;; permit persons to whom the Software is furnished to do so, subject to
  10. ;; the following conditions:
  11. ;;
  12. ;; The above copyright notice and this permission notice shall be
  13. ;; included in all copies or substantial portions of the Software.
  14. ;;
  15. ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. ;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  19. ;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  20. ;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  21. ;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. ;; SOFTWARE.
  23. (define-module (srfi srfi-126)
  24. #:use-module (srfi srfi-1)
  25. #:use-module (srfi srfi-9)
  26. #:use-module (srfi srfi-9 gnu)
  27. #:use-module (srfi srfi-11)
  28. #:use-module (ice-9 control)
  29. #:use-module ((rnrs hashtables) #:select (equal-hash
  30. string-hash
  31. string-ci-hash))
  32. #:export (make-eq-hashtable
  33. make-eqv-hashtable make-hashtable
  34. alist->eq-hashtable alist->eqv-hashtable alist->hashtable
  35. weakness
  36. hashtable? hashtable-size
  37. hashtable-ref hashtable-set! hashtable-delete! hashtable-contains?
  38. hashtable-lookup hashtable-update! hashtable-intern!
  39. hashtable-copy hashtable-clear! hashtable-empty-copy
  40. hashtable-keys hashtable-values hashtable-entries
  41. hashtable-key-list hashtable-value-list hashtable-entry-lists
  42. hashtable-walk hashtable-update-all! hashtable-prune!
  43. hashtable-merge!
  44. hashtable-sum hashtable-map->lset hashtable-find
  45. hashtable-empty? hashtable-pop! hashtable-inc! hashtable-dec!
  46. hashtable-equivalence-function hashtable-hash-function
  47. hashtable-weakness hashtable-mutable?
  48. hash-salt)
  49. #:re-export (equal-hash string-hash string-ci-hash))
  50. (define-record-type <hashtable>
  51. (%make-hashtable %table %hash %assoc hash equiv weakness mutable)
  52. hashtable?
  53. (%table %hashtable-table)
  54. (%hash %hashtable-hash)
  55. (%assoc %hashtable-assoc)
  56. (hash hashtable-hash-function)
  57. (equiv hashtable-equivalence-function)
  58. (weakness hashtable-weakness)
  59. (mutable hashtable-mutable? %hashtable-set-mutable!))
  60. (define nil (cons #f #f))
  61. (define (nil? obj) (eq? obj nil))
  62. (define (make-table capacity weakness)
  63. (let ((capacity (or capacity 32)))
  64. (case weakness
  65. ((#f) (make-hash-table capacity))
  66. ((weak-key) (make-weak-key-hash-table capacity))
  67. ((weak-value) (make-weak-value-hash-table capacity))
  68. ((weak-key-and-value) (make-doubly-weak-hash-table capacity))
  69. (else (error "Hashtable weakness not supported." weakness)))))
  70. (define* (make-eq-hashtable #:optional capacity weakness)
  71. (let ((table (make-table capacity weakness)))
  72. (%make-hashtable table hashq assq #f eq? weakness #t)))
  73. (define* (make-eqv-hashtable #:optional capacity weakness)
  74. (let ((table (make-table capacity weakness)))
  75. (%make-hashtable table hashv assv #f eqv? weakness #t)))
  76. (define* (make-hashtable hash equiv #:optional capacity weakness)
  77. (cond
  78. ((and (not hash) (eq? equiv eq?))
  79. (make-eq-hashtable capacity weakness))
  80. ((and (not hash) (eq? equiv eqv?))
  81. (make-eqv-hashtable capacity weakness))
  82. (else
  83. (let* ((table (make-table capacity weakness))
  84. (hash (if (pair? hash)
  85. (car hash)
  86. hash))
  87. (%hash (lambda (obj bound)
  88. (modulo (hash obj) bound)))
  89. (assoc (lambda (key alist)
  90. (find (lambda (entry)
  91. (equiv (car entry) key))
  92. alist))))
  93. (%make-hashtable table %hash assoc hash equiv weakness #t)))))
  94. (define (alist->eq-hashtable . args)
  95. (apply alist->hashtable #f eq? args))
  96. (define (alist->eqv-hashtable . args)
  97. (apply alist->hashtable #f eqv? args))
  98. (define alist->hashtable
  99. (case-lambda
  100. ((hash equiv alist)
  101. (alist->hashtable hash equiv #f #f alist))
  102. ((hash equiv capacity alist)
  103. (alist->hashtable hash equiv capacity #f alist))
  104. ((hash equiv capacity weakness alist)
  105. (let ((ht (make-hashtable hash equiv capacity weakness)))
  106. (for-each (lambda (entry)
  107. (hashtable-set! ht (car entry) (cdr entry)))
  108. (reverse alist))
  109. ht))))
  110. (define-syntax weakness
  111. (lambda (stx)
  112. (syntax-case stx ()
  113. ((_ <sym>)
  114. (let ((sym (syntax->datum #'<sym>)))
  115. (case sym
  116. ((weak-key weak-value weak-key-and-value ephemeral-key
  117. ephemeral-value ephemeral-key-and-value)
  118. #''sym)
  119. (else
  120. (error "Bad weakness symbol." sym))))))))
  121. (define (hashtable-size ht)
  122. (hash-count (const #t) (%hashtable-table ht)))
  123. (define* (%hashtable-ref ht key default)
  124. (hashx-ref (%hashtable-hash ht) (%hashtable-assoc ht)
  125. (%hashtable-table ht) key default))
  126. (define* (hashtable-ref ht key #:optional (default nil))
  127. (let ((val (%hashtable-ref ht key default)))
  128. (if (nil? val)
  129. (error "No association for key in hashtable." key ht)
  130. val)))
  131. (define (assert-mutable ht)
  132. (when (not (hashtable-mutable? ht))
  133. (error "Hashtable is immutable." ht)))
  134. (define (hashtable-set! ht key value)
  135. (assert-mutable ht)
  136. (hashx-set! (%hashtable-hash ht) (%hashtable-assoc ht)
  137. (%hashtable-table ht) key value)
  138. *unspecified*)
  139. (define (hashtable-delete! ht key)
  140. (assert-mutable ht)
  141. (hashx-remove! (%hashtable-hash ht) (%hashtable-assoc ht)
  142. (%hashtable-table ht) key)
  143. *unspecified*)
  144. (define (hashtable-contains? ht key)
  145. (not (nil? (%hashtable-ref ht key nil))))
  146. (define (hashtable-lookup ht key)
  147. (let ((val (%hashtable-ref ht key nil)))
  148. (if (nil? val)
  149. (values #f #f)
  150. (values val #t))))
  151. (define* (hashtable-update! ht key updater #:optional (default nil))
  152. (assert-mutable ht)
  153. (let ((handle (hashx-create-handle!
  154. (%hashtable-hash ht) (%hashtable-assoc ht)
  155. (%hashtable-table ht) key nil)))
  156. (if (eq? nil (cdr handle))
  157. (if (nil? default)
  158. (error "No association for key in hashtable." key ht)
  159. (set-cdr! handle (updater default)))
  160. (set-cdr! handle (updater (cdr handle))))
  161. (cdr handle)))
  162. (define (hashtable-intern! ht key default-proc)
  163. (assert-mutable ht)
  164. (let ((handle (hashx-create-handle!
  165. (%hashtable-hash ht) (%hashtable-assoc ht)
  166. (%hashtable-table ht) key nil)))
  167. (when (nil? (cdr handle))
  168. (set-cdr! handle (default-proc)))
  169. (cdr handle)))
  170. (define* (hashtable-copy ht #:optional mutable weakness)
  171. (let ((copy (hashtable-empty-copy ht (hashtable-size ht) weakness)))
  172. (hashtable-walk ht
  173. (lambda (k v)
  174. (hashtable-set! copy k v)))
  175. (%hashtable-set-mutable! copy mutable)
  176. copy))
  177. (define* (hashtable-clear! ht #:optional _capacity)
  178. (assert-mutable ht)
  179. (hash-clear! (%hashtable-table ht))
  180. *unspecified*)
  181. (define* (hashtable-empty-copy ht #:optional capacity weakness)
  182. (make-hashtable (hashtable-hash-function ht)
  183. (hashtable-equivalence-function ht)
  184. (case capacity
  185. ((#f) #f)
  186. ((#t) (hashtable-size ht))
  187. (else capacity))
  188. (or weakness (hashtable-weakness ht))))
  189. (define (hashtable-keys ht)
  190. (let ((keys (make-vector (hashtable-size ht))))
  191. (hashtable-sum ht 0
  192. (lambda (k v i)
  193. (vector-set! keys i k)
  194. (+ i 1)))
  195. keys))
  196. (define (hashtable-values ht)
  197. (let ((vals (make-vector (hashtable-size ht))))
  198. (hashtable-sum ht 0
  199. (lambda (k v i)
  200. (vector-set! vals i v)
  201. (+ i 1)))
  202. vals))
  203. (define (hashtable-entries ht)
  204. (let ((keys (make-vector (hashtable-size ht)))
  205. (vals (make-vector (hashtable-size ht))))
  206. (hashtable-sum ht 0
  207. (lambda (k v i)
  208. (vector-set! keys i k)
  209. (vector-set! vals i v)
  210. (+ i 1)))
  211. (values keys vals)))
  212. (define (hashtable-key-list ht)
  213. (hashtable-map->lset ht (lambda (k v) k)))
  214. (define (hashtable-value-list ht)
  215. (hashtable-map->lset ht (lambda (k v) v)))
  216. (define (hashtable-entry-lists ht)
  217. (let ((keys&vals (cons '() '())))
  218. (hashtable-walk ht
  219. (lambda (k v)
  220. (set-car! keys&vals (cons k (car keys&vals)))
  221. (set-cdr! keys&vals (cons v (cdr keys&vals)))))
  222. (car+cdr keys&vals)))
  223. (define (hashtable-walk ht proc)
  224. (hash-for-each proc (%hashtable-table ht)))
  225. (define (hashtable-update-all! ht proc)
  226. (assert-mutable ht)
  227. (hash-for-each-handle
  228. (lambda (handle)
  229. (set-cdr! handle (proc (car handle) (cdr handle))))
  230. (%hashtable-table ht)))
  231. (define (hashtable-prune! ht pred)
  232. (assert-mutable ht)
  233. (let ((keys (hashtable-sum ht '()
  234. (lambda (k v keys-to-delete)
  235. (if (pred k v)
  236. (cons k keys-to-delete)
  237. keys-to-delete)))))
  238. (for-each (lambda (k)
  239. (hashtable-delete! ht k))
  240. keys)))
  241. (define (hashtable-merge! ht-dest ht-src)
  242. (assert-mutable ht-dest)
  243. (hashtable-walk ht-src
  244. (lambda (k v)
  245. (hashtable-set! ht-dest k v)))
  246. ht-dest)
  247. (define (hashtable-sum ht init proc)
  248. (hash-fold proc init (%hashtable-table ht)))
  249. (define (hashtable-map->lset ht proc)
  250. (hash-map->list proc (%hashtable-table ht)))
  251. (define (hashtable-find ht pred)
  252. (let/ec return
  253. (hashtable-walk ht
  254. (lambda (k v)
  255. (when (pred k v)
  256. (return k v #t))))
  257. (return #f #f #f)))
  258. (define (hashtable-empty? ht)
  259. (zero? (hashtable-size ht)))
  260. (define (hashtable-pop! ht)
  261. (assert-mutable ht)
  262. (when (hashtable-empty? ht)
  263. (error "Cannot pop from empty hashtable." ht))
  264. (let-values (((k v found?) (hashtable-find ht (const #t))))
  265. (hashtable-delete! ht k)
  266. (values k v)))
  267. (define* (hashtable-inc! ht k #:optional (x 1))
  268. (assert-mutable ht)
  269. (hashtable-update! ht k (lambda (v) (+ v x)) 0))
  270. (define* (hashtable-dec! ht k #:optional (x 1))
  271. (assert-mutable ht)
  272. (hashtable-update! ht k (lambda (v) (- v x)) 0))
  273. (define (hash-salt) 0)
  274. (set-record-type-printer!
  275. <hashtable>
  276. (lambda (ht port)
  277. (with-output-to-port port
  278. (lambda ()
  279. (let ((equal-hash (@ (rnrs hashtables) equal-hash))
  280. (string-hash (@ (rnrs hashtables) string-hash))
  281. (string-ci-hash (@ (rnrs hashtables) string-ci-hash))
  282. (symbol-hash (@ (rnrs hashtables) symbol-hash))
  283. (hash (hashtable-hash-function ht))
  284. (equiv (hashtable-equivalence-function ht))
  285. (alist (hashtable-map->lset ht cons)))
  286. (cond
  287. ((and (not hash) (eq? equiv eq?))
  288. (display "#hasheq")
  289. (display alist))
  290. ((and (not hash) (eq? equiv eqv?))
  291. (display "#hasheqv")
  292. (display alist))
  293. (else
  294. (display "#hash")
  295. (cond
  296. ((and (eq? hash (@ (rnrs hashtables) equal-hash)) (eq? equiv equal?))
  297. (display alist))
  298. ((and (eq? hash (@ (rnrs hashtables) string-hash)) (eq? equiv string=?))
  299. (display (cons 'string alist)))
  300. ((and (eq? hash string-ci-hash) (eq? equiv string-ci=?))
  301. (display (cons 'string-ci alist)))
  302. ((and (eq? hash symbol-hash) (eq? equiv eq?))
  303. (display (cons 'symbol alist)))
  304. (else
  305. (display (cons 'custom alist)))))))))))
  306. (read-hash-extend
  307. #\h
  308. (lambda (char port)
  309. (with-input-from-port port
  310. (lambda ()
  311. (let ((equal-hash (@ (rnrs hashtables) equal-hash))
  312. (string-hash (@ (rnrs hashtables) string-hash))
  313. (string-ci-hash (@ (rnrs hashtables) string-ci-hash))
  314. (symbol-hash (@ (rnrs hashtables) symbol-hash))
  315. (type (string-append "h" (symbol->string (read))))
  316. (alist (read)))
  317. (cond
  318. ((string=? type "hasheq")
  319. (alist->eq-hashtable alist))
  320. ((string=? type "hasheqv")
  321. (alist->eqv-hashtable alist))
  322. (else
  323. (when (not (string=? type "hash"))
  324. (error "Unrecognized hash type." type))
  325. (let* ((has-tag? (symbol? (car alist)))
  326. (subtype (if has-tag?
  327. (car alist)
  328. "equal"))
  329. (alist (if has-tag?
  330. (cdr alist)
  331. alist)))
  332. (cond
  333. ((string=? subtype "equal")
  334. (alist->hashtable equal-hash equal? alist))
  335. ((string=? subtype "string")
  336. (alist->hashtable string-hash string=? alist))
  337. ((string=? subtype "string-ci")
  338. (alist->hashtable string-ci-hash string-ci=? alist))
  339. ((string=? subtype "symbol")
  340. (alist->hashtable symbol-hash eq? alist))
  341. (else
  342. (error "Unrecognized hash subtype." subtype)))))))))))
  343. ;; Local Variables:
  344. ;; eval: (put 'hashtable-walk 'scheme-indent-function 1)
  345. ;; eval: (put 'hashtable-update-all! 'scheme-indent-function 1)
  346. ;; eval: (put 'hashtable-prune! 'scheme-indent-function 1)
  347. ;; eval: (put 'hashtable-sum 'scheme-indent-function 2)
  348. ;; eval: (put 'hashtable-map->lset 'scheme-indent-function 1)
  349. ;; eval: (put 'hashtable-find 'scheme-indent-function 1)
  350. ;; End: