4 ;; SUMMARY: Create hash tables from lists and operate on them.
5 ;; USAGE: GNU Emacs Lisp Library
6 ;; KEYWORDS: extensions, tools
11 ;; ORIG-DATE: 16-Mar-90 at 03:38:48
12 ;; LAST-MOD: 9-Jun-99 at 18:06:06 by Bob Weiner
14 ;; Copyright (C) 1990-1995, 1997 BeOpen.com
15 ;; See the file BR-COPY for license information.
17 ;; This file is part of the OO-Browser.
21 ;; Featureful set of hash table operators for use in personal programs.
23 ;; `hash-make' creates a hash table from an association list, `hash-add'
24 ;; adds a value-key pair to a hash table, and `hash-lookup' finds the value
25 ;; associated with a given key in a hash table, if any.
27 ;; `hash-map' does the same thing as `mapcar' but operates on hash tables
30 ;; For a list of 300 items, these hash tables improve lookup times by a
31 ;; factor of between 8 and 10 to 1 over those for an unsorted list.
33 ;; Public and private function names are alphabetized for easy location.
37 ;;; ************************************************************************
39 ;;; ************************************************************************
41 (defvar hash-merge-values-function 'hash-merge-values
42 "*Function to call in hash-merge to merge the values from 2 hash tables that contain the same key.
43 It is sent the two values as arguments.")
45 ;;; ************************************************************************
47 ;;; ************************************************************************
49 (defun hash-add (value key hash-table)
50 "Add VALUE, any lisp object, referenced by KEY, a string, to HASH-TABLE.
51 Replaces any VALUE previously referenced by KEY."
52 (if (hashp hash-table)
53 (let* ((obarray (hash-obarray hash-table))
54 (sym (intern key obarray)))
55 (if sym (set sym value)))))
57 (defun hash-copy (hash-table)
58 "Return a copy of HASH-TABLE, list and vector elements are shared across both tables."
59 (if (not (hashp hash-table))
60 (error "(hash-copy): Invalid hash-table: `%s'" hash-table))
61 (let ((htable-copy (hash-make (length (hash-obarray hash-table)))))
63 (function (lambda (elt) (hash-add (car elt) (cdr elt) htable-copy)))
67 (defun hash-count (hash-table)
68 "Return number of elements stored in HASH-TABLE or nil if not a valid hash table."
69 (if (hashp hash-table)
70 (let ((obarray (hash-obarray hash-table))
74 (and (boundp sym) sym (setq count (1+ count)))))
78 (defun hash-delete (key hash-table)
79 "Delete element referenced by KEY, a string, from HASH-TABLE.
80 Return nil if KEY is not in HASH-TABLE or non-nil otherwise."
81 (if (hashp hash-table)
82 (let* ((obarray (hash-obarray hash-table))
83 (sym (intern-soft key obarray)))
85 (progn (makunbound sym)
88 (defun hash-deep-copy (obj)
89 "Return a copy of OBJ with new copies of all elements, except symbols."
90 (cond ((null obj) nil)
94 (let ((htable-copy (hash-make (length (hash-obarray obj)))))
97 (lambda (elt) (hash-add (car elt) (cdr elt) htable-copy)))
98 (hash-map 'hash-deep-copy obj))
101 ;; convert to list for mapping
102 (setq obj (append obj nil))
103 ;; Return as a vector
104 (vconcat (mapcar 'hash-deep-copy obj)))
107 (error "(hash-deep-copy): Invalid type, `%s'" obj))
109 (cons (hash-deep-copy (car obj)) (hash-deep-copy (cdr obj))))))
111 (defun hash-empty-p (hash-table)
112 "Return t if HASH-TABLE is empty, else nil."
113 (and (hashp hash-table) (equal hash-table hash-empty-htable)))
115 (defalias 'hash-get 'hash-lookup)
117 (defun hash-key-p (key hash-table)
118 "Return non-nil iff KEY is in HASH-TABLE. KEY's hash table symbol is returned."
119 (if (hashp hash-table)
120 (let* ((obarray (hash-obarray hash-table))
121 (sym (intern-soft key obarray)))
122 (if (boundp sym) sym))))
124 (defun hash-lookup (key hash-table)
125 "Lookup KEY in HASH-TABLE and return associated value.
126 If value is nil, this function does not tell you whether or not KEY is in the
127 hash table. Use `hash-key-p' instead for that function."
128 (if (hashp hash-table)
129 (let* ((obarray (hash-obarray hash-table))
130 (sym (intern-soft key obarray)))
131 (if (boundp sym) (symbol-value sym)))))
133 (defun hash-make (initializer &optional reverse)
134 "Create a hash table from INITIALIZER.
135 INITIALIZER may be an alist with elements of the form (<value> . <key>) from
136 which the hash table is built. Alternatively, it may be a non-negative
137 integer which is used as the minimum size of a new, empty hash table.
138 Optional non-nil second argument REVERSE means INITIALIZER has elements of
139 form (<key> . <value>)."
140 (cond ((integerp initializer)
141 (if (>= initializer 0)
142 (cons 'hasht (make-vector (hash-next-prime initializer) 0))
143 (error "(hash-make): Initializer must be >= 0, not `%s'"
145 ((numberp initializer)
146 (error "(hash-make): Initializer must be a positive integer, not `%f'"
148 (t (let* ((vlen (hash-next-prime (length initializer)))
149 (obarray (make-vector vlen 0))
156 (setq key (car cns) value (cdr cns))
157 (setq key (cdr cns) value (car cns))))
158 (if (setq sym (intern key))
161 (cons 'hasht obarray)))))
163 (defun hash-make-prepend (initializer &optional reverse)
164 "Create a hash table from INITIALIZER.
165 INITIALIZER may be an alist with elements of the form (<value> . <key>) from
166 which the hash table is built. Optional non-nil second argument REVERSE
167 means INITIALIZER has elements of form (<key> . <value>).
169 The resultant value associated with a <key> is a list of all of the <values>
170 given in INITIALIZER entries which contain the <key>. The values are listed
171 in reverse order of occurrence (they are prepended to the list)."
172 (let* ((vlen (hash-next-prime (length initializer)))
173 (obarray (make-vector vlen 0))
180 (setq key (car cns) value (cdr cns))
181 (setq key (cdr cns) value (car cns))))
182 (setq sym (intern key))
184 (set sym (cons value (symbol-value sym)))
185 (set sym (cons value nil)))))
187 (cons 'hasht obarray)))
189 (defun hash-map (func hash-table)
190 "Return a list of the results of applying FUNC to each (<value> . <key>) element of HASH-TABLE."
191 (if (not (hashp hash-table))
192 (error "(hash-map): Invalid hash-table: `%s'" hash-table))
198 (setq result (cons (funcall
200 (cons (symbol-value sym)
203 (hash-obarray hash-table))
206 (defun hash-merge (&rest hash-tables)
207 "Merge any number of HASH-TABLES. Return resultant hash table.
208 A single argument consisting of a list of hash tables may also be given.
209 Return an empty hash table if any argument from the merge list is other
210 than nil or a hash table.
212 Use the value of `hash-merge-values-function' to merge the values of entries
213 whose keys are the same."
214 (let ((empty-ht (hash-make 1)))
215 (and (not (hashp (car hash-tables)))
216 (listp (car hash-tables))
217 ;; Handle situation where a list of hash-tables is passed in as a
218 ;; single argument, rather than as multiple arguments.
219 (setq hash-tables (car hash-tables)))
220 (if (memq nil (mapcar (function (lambda (ht) (or (null ht) (hashp ht))))
224 (delq nil (mapcar (function (lambda (ht)
225 (if (hash-empty-p ht) nil ht)))
227 (let ((len (length hash-tables)))
228 (cond ((= len 0) empty-ht)
229 ((= len 1) (car hash-tables))
230 ;; Make the merged hash-table be 20% larger than the number of
231 ;; entries filled in all hash-tables to be merged, so that
232 ;; hash misses are minimized.
233 (t (let ((htable (hash-make
235 (* 1.2 (apply '+ (mapcar 'hash-count
242 (lambda (val-key-cons)
243 (setq value (car val-key-cons)
244 key (cdr val-key-cons))
245 (if (not (hash-key-p key htable))
246 (hash-add value key htable)
249 (funcall hash-merge-values-function
250 (hash-get key htable)
257 (defun hash-merge-first-value (value1 value2)
258 "Return a copy of VALUE1 for use in a hash table merge.
260 This is suitable for use as a value of `hash-merge-values-function'."
261 ;; Copy list so that merged result does not share structure with the
262 ;; hash tables being merged.
263 (if (listp value1) (copy-sequence value1) value1))
265 (defun hash-merge-values (value1 value2)
266 "Return a list from merging VALUE1 and VALUE2 or creating a new list.
267 Nil values are thrown away. If both arguments are lists, their elements are
268 assumed to be strings and the result is a set of ordered strings.
270 This is suitable for use as a value of `hash-merge-values-function'."
271 ;; Copy lists so that merged result does not share structure with the
272 ;; hash tables being merged.
273 (if (listp value1) (setq value1 (copy-sequence value1)))
274 (if (listp value2) (setq value2 (copy-sequence value2)))
275 (cond ((and (listp value1) (listp value2))
276 ;; Assume desired result is a set of strings.
277 (hash-set-of-strings (sort (append value1 value2) 'string-lessp)))
283 (cons value2 value1))
285 (cons value1 value2))
286 (t (list value1 value2))))
288 (make-obsolete 'hash-new 'hash-make)
289 (defun hash-new (size)
290 "Return a new hash table of SIZE elements.
291 This is obsolete. Use `hash-make' instead."
294 (defun hash-prepend (value key hash-table)
295 "Prepend VALUE onto the list value referenced by KEY, a string, in HASH-TABLE.
296 If KEY is not found in HASH-TABLE, it is added with a value of (list VALUE)."
297 (if (hashp hash-table)
298 (let* ((obarray (hash-obarray hash-table))
299 (sym (intern key obarray)))
301 (if (listp (symbol-value sym))
302 (set sym (cons value (symbol-value sym)))
303 (error "(hash-prepend): `%s' key's value is not a list."
305 (set sym (cons value nil))))))
307 (defun hash-prin1 (hash-table &optional stream)
308 "Output the printed representation of HASH-TABLE as a list.
309 Quoting characters are printed when needed to make output that `read'
310 can handle, whenever this is possible.
311 Output stream is STREAM, or value of `standard-output'."
312 (if (not (hashp hash-table))
313 (progn (prin1 hash-table stream)
315 (princ "\(\n" stream)
317 (function (lambda (val-key-cons)
318 (prin1 val-key-cons stream)
319 (princ "\n" stream)))
321 (princ "\)\n" stream)))
323 (defun hash-replace (value key hash-table)
324 "Replace VALUE referenced by KEY, a string, in HASH-TABLE.
325 An error will occur if KEY is not found in HASH-TABLE."
326 (if (hashp hash-table)
327 (let* ((obarray (hash-obarray hash-table))
328 (sym (intern-soft key obarray)))
329 (if (and (boundp sym) sym)
331 (error "(hash-replace): `%s' key not found in hash table." key)))))
333 (defun hash-resize (hash-table new-size)
334 "Resize HASH-TABLE to NEW-SIZE without losing any elements and return new table.
335 NEW-SIZE must be greater than 0. Hashing works best if NEW-SIZE is a prime
336 number. See also `hash-next-prime'."
338 (error "(hash-resize): Cannot resize hash table to size %d" new-size))
339 (let ((htable (hash-make new-size)))
342 (hash-add (car elt) (cdr elt) htable)))
346 (defun hash-resize-p (hash-table)
347 "Resizes HASH-TABLE to 1.5 times its size if above 80% full.
348 Returns new hash table when resized, else nil."
349 (if (hashp hash-table)
350 (let ((count (hash-count hash-table))
351 (size (length (hash-obarray hash-table))))
352 (if (> (* count (/ count 5)) size)
353 (hash-resize hash-table (hash-next-prime (+ size (/ size 2))))))))
355 (defun hash-size (hash-table)
356 "Return size of HASH-TABLE which is >= number of elements in the table.
357 Return nil if not a valid hash table."
358 (if (hashp hash-table)
359 (length (hash-obarray hash-table))))
360 (defalias 'hash-length 'hash-size)
362 (defun hashp (object)
363 "Return non-nil if OBJECT is a hash-table."
364 (and (listp object) (eq (car object) 'hasht)
365 (vectorp (cdr object))))
367 ;;; ************************************************************************
368 ;;; Private functions
369 ;;; ************************************************************************
371 (defun hash-next-prime (n)
372 "Return next prime number >= N."
375 (and (= (% n 2) 0) (setq n (1+ n)))
376 (while (not (hash-prime-p n))
380 (defun hash-obarray (hash-table)
381 "Return symbol table (object array) portion of HASH-TABLE."
384 (defun hash-prime-p (n)
385 "Return non-nil iff N is prime."
386 (if (< n 0) (setq n (- n)))
387 (let ((small-primes '(1 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67
389 (cond ((< n 91) (memq n small-primes))
390 ((< n 7921) ;; 89, max small-prime, squared
392 (pr-list small-primes))
393 (while (and (setq pr-list (cdr pr-list))
394 (setq prime (/= (% n (car pr-list)) 0))))
396 ((or (= (% n 3) 0) (= (% n 2) 0)) nil)
400 (while (and (<= (* factor1 factor1) n)
401 (setq is-prime (and (/= (% n factor1) 0)
402 (/= (% n factor2) 0))))
403 (setq factor1 (+ factor1 6)
404 factor2 (+ factor2 6)))
407 (defun hash-set-of-strings (sorted-strings &optional count)
408 "Return SORTED-STRINGS list with any duplicate entries removed.
409 Optional COUNT conses number of duplicates on to front of list before return."
410 (and count (setq count 0))
411 (let ((elt1) (elt2) (lst sorted-strings)
414 (lambda (a b) (if (string-equal a b)
415 (setq count (1+ count)))))
416 (function (lambda (a b) (string-equal a b))))))
417 (while (setq elt1 (car lst) elt2 (car (cdr lst)))
418 (if (funcall test elt1 elt2)
419 (setcdr lst (cdr (cdr lst)))
420 (setq lst (cdr lst)))))
421 (if count (cons count sorted-strings) sorted-strings))
423 ;;; ************************************************************************
424 ;;; Private variables
425 ;;; ************************************************************************
427 (defvar hash-empty-htable (hash-make 1)
428 "Empty hash table used to test whether other hash tables are empty.")