general-table.scm 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ; Hash table package that allows for different hash and comparison functions.
  3. ;
  4. ; The fields in a table are:
  5. ; size - the number of entries
  6. ; data - an a-list or vector of a-lists
  7. ; ref - a procedure: [table index] -> value
  8. ; set - a procedure: [table index new-value] -> void
  9. ;
  10. ; In small tables the data is stored in an a-list and no hashing is used.
  11. ; In large tables the data is stored in a vector of a-lists, with hashing
  12. ; used to index into the vector. When a small table grows large the REF
  13. ; and SET fields are replaced with vector-oriented versions.
  14. (define-record-type table :table
  15. (really-make-table size data ref set)
  16. table?
  17. (size table-size set-table-size!)
  18. (data table-data set-table-data!)
  19. (ref table-ref-procedure set-table-ref-procedure!)
  20. (set table-set!-procedure set-table-set!-procedure!))
  21. (define (table-ref table key)
  22. ((table-ref-procedure table) table key))
  23. (define (table-set! table key value)
  24. ((table-set!-procedure table) table key value))
  25. ; This number is a guess
  26. (define linear-table-size-limit 15)
  27. (define (next-table-size count) ; Figure out next good size for table.
  28. (+ (* count 3) 1))
  29. ; A table-maker is a thunk that returns a new, empty table each time it is
  30. ; called. There are four functions involved:
  31. ; assoc : [key alist] -> entry or #f
  32. ; ref-proc : [table key] -> entry or #f
  33. ; x->hash-table! : [assoc hash-function] -> void
  34. ; set!-proc : [table key value] -> void
  35. ; X->HASH-TABLE! replaces the data, ref, and set fields of the table, making
  36. ; it into a hash table.
  37. (define (make-table-maker comparison-function hash-function)
  38. (assoc->table-maker (make-assoc comparison-function)
  39. hash-function))
  40. (define (assoc->table-maker assoc hash-function)
  41. (let* ((ref-proc (make-linear-table-ref assoc))
  42. (x->hash-table! (make->hash-table assoc hash-function))
  43. (set!-proc (make-linear-table-set! assoc x->hash-table!)))
  44. (lambda ()
  45. (really-make-table 0 null-entry ref-proc set!-proc))))
  46. ;----------------
  47. ; A-lists. These are currently actual a-lists, because ASSQ is a VM primitive
  48. ; and thus very fast.
  49. (define null-entry '()) ; #f
  50. (define (new-entry key val others)
  51. ;(vector key val others)
  52. (cons (cons key val) others))
  53. ; This abstraction is violated at times. Eta-converted to get inlining.
  54. (define (entry-value x) (cdr x))
  55. (define (entry-key x) (car x))
  56. (define (set-entry-value! x v) (set-cdr! x v))
  57. ; ENTRIES is known to contain ENTRY.
  58. (define (delete-entry! entries entry)
  59. (if (eq? entry (car entries))
  60. (cdr entries)
  61. (begin
  62. (let loop ((entries entries))
  63. (if (eq? entry
  64. (cadr entries))
  65. (set-cdr! entries (cddr entries))
  66. (loop (cdr entries))))
  67. entries)))
  68. (define (make-assoc pred)
  69. (if (eq? pred eq?)
  70. assq
  71. (lambda (thing alist)
  72. (let loop ((alist alist))
  73. (cond ((null? alist)
  74. #f)
  75. ((pred thing (caar alist))
  76. (car alist))
  77. (else
  78. (loop (cdr alist))))))))
  79. ; Using actual a-lists allows us to use ASSQ instead of the following.
  80. ;(define eq?-assoc
  81. ; (lambda (thing alist)
  82. ; (let loop ((alist alist))
  83. ; (cond ((not alist)
  84. ; #f)
  85. ; ((eq? thing (vector-ref alist 0))
  86. ; alist)
  87. ; (else
  88. ; (loop (vector-ref alist 2)))))))
  89. ;----------------
  90. ; Turn some version of ASSOC into a table reference procedure for a-list
  91. ; tables.
  92. (define (make-linear-table-ref assoc)
  93. (lambda (table key)
  94. (let ((probe (assoc key (table-data table))))
  95. (if probe (entry-value probe) #f))))
  96. ; Turn some version of ASSOC and a hash function into a table set! procedure
  97. ; for a-list tables. If the table gets too big it is turned into a hash table.
  98. (define (make-linear-table-set! assoc x->hash-table!)
  99. (lambda (table key value)
  100. (let* ((data (table-data table))
  101. (probe (assoc key data)))
  102. (cond (probe
  103. (if value
  104. (set-entry-value! probe value)
  105. (begin
  106. (set-table-data! table (delete-entry! data probe))
  107. (set-table-size! table (- (table-size table) 1)))))
  108. (value
  109. (set-table-data! table (new-entry key value data))
  110. (let ((size (table-size table)))
  111. (if (< size linear-table-size-limit)
  112. (set-table-size! table (+ size 1))
  113. (x->hash-table! table size))))))))
  114. ; Return a function to transform linear tables into hash tables.
  115. (define (make->hash-table assoc hash-function)
  116. (let ((hash-table-ref (make-hash-table-ref assoc hash-function))
  117. (hash-table-set! (make-hash-table-set! assoc hash-function)))
  118. (lambda (table size)
  119. (let ((data (table-data table)))
  120. (set-table-ref-procedure! table hash-table-ref)
  121. (set-table-set!-procedure! table hash-table-set!)
  122. (table-expand-table! table (next-table-size size))
  123. (table-enter-alist! table data)))))
  124. (define (make-hash-table-ref assoc hash-function)
  125. (lambda (table key)
  126. (let* ((data (table-data table))
  127. (h (remainder (hash-function key)
  128. (vector-length data)))
  129. (alist (vector-ref data h))
  130. (probe (assoc key alist)))
  131. (if probe (entry-value probe) #f))))
  132. (define (make-hash-table-set! assoc hash-function)
  133. (lambda (table key value)
  134. (let* ((data (table-data table))
  135. (h (remainder (hash-function key)
  136. (vector-length data)))
  137. (alist (vector-ref data h))
  138. (probe (assoc key alist)))
  139. (cond (probe
  140. (if value
  141. (set-entry-value! probe value)
  142. (begin
  143. (vector-set! data h (delete-entry! alist probe))
  144. (set-table-size! table (- (table-size table) 1)))))
  145. (value
  146. (vector-set! data h (new-entry key value alist))
  147. (let ((size (+ (table-size table) 1)))
  148. (if (< size (vector-length data))
  149. (set-table-size! table size)
  150. (expand-hash-table! table size))))))))
  151. (define (expand-hash-table! table size)
  152. (let ((data (table-data table)))
  153. (table-expand-table! table (next-table-size size))
  154. (do ((i 0 (+ i 1)))
  155. ((>= i (vector-length data)))
  156. (table-enter-alist! table (vector-ref data i)))))
  157. (define (table-enter-alist! table alist)
  158. (let ((set!-proc (table-set!-procedure table)))
  159. (do ((alist alist (cdr alist)))
  160. ((null? alist))
  161. (set!-proc table (caar alist) (cdar alist)))))
  162. ; Reset the size and data of a table. The size will be incremented as
  163. ; the entries are added back into the table.
  164. (define (table-expand-table! table vector-size)
  165. (set-table-size! table 0)
  166. (set-table-data! table (make-vector vector-size null-entry)))
  167. (define (table-walk proc table)
  168. (really-table-walk (lambda (v)
  169. (proc (entry-key v) (entry-value v)))
  170. table))
  171. (define (really-table-walk proc table)
  172. (let ((data (table-data table)))
  173. (cond ((null? data))
  174. ((pair? data)
  175. (alist-walk proc data))
  176. (else
  177. (do ((i 0 (+ i 1)))
  178. ((>= i (vector-length data)))
  179. (alist-walk proc (vector-ref data i)))))))
  180. (define (alist-walk proc alist)
  181. (do ((alist alist (cdr alist)))
  182. ((null? alist))
  183. (proc (car alist))))
  184. (define (make-table-immutable! table)
  185. (really-table-walk make-immutable! table)
  186. (make-immutable! (table-data table))
  187. (make-immutable! table))
  188. (define (table->entry-list table)
  189. (let ((list '()))
  190. (table-walk (lambda (k v)
  191. (set! list (cons v list)))
  192. table)
  193. list))
  194. ; Actual tables
  195. ; The default hash function only on works on things that would work in
  196. ; a CASE expression. Even then, numbers don't really "work," since
  197. ; they are compared using eq?.
  198. (define (default-hash-function obj)
  199. (cond ((symbol? obj) (string-hash (symbol->string obj)))
  200. ((integer? obj)
  201. (if (< obj 0) (- -1 obj) obj))
  202. ((char? obj) (+ 333 (char->integer obj)))
  203. ((eq? obj #f) 3001)
  204. ((eq? obj #t) 3003)
  205. ((null? obj) 3005)
  206. (else (error "value cannot be used as a table key" obj))))
  207. (define eqv?-assoc (make-assoc eqv?))
  208. (define (default-table-assoc key alist)
  209. (if (number? key)
  210. (eqv?-assoc key alist)
  211. (assq key alist)))
  212. (define (symbol-hash symbol)
  213. (string-hash (symbol->string symbol)))
  214. (define make-table
  215. (let ((make-usual-table (assoc->table-maker default-table-assoc
  216. default-hash-function)))
  217. (lambda hash-function-option
  218. (if (null? hash-function-option)
  219. (make-usual-table)
  220. ((assoc->table-maker default-table-assoc
  221. (car hash-function-option)))))))
  222. (define make-string-table (make-table-maker string=? string-hash))
  223. (define make-symbol-table (make-table-maker eq? symbol-hash))
  224. (define make-integer-table (make-table-maker = abs))