|
|
1.1 root 1: (setq rcs-hash-
2: "$Header: hash.l,v 1.2 85/03/24 11:36:16 sklower Exp $")
3:
4: ; Aug 5, 1982
5: ; (c) copyright 1982, Massachusetts Institute of Technology
6: ;
7: ; Hash tables are basically just fast property lists. There are much the
8: ; same access functions: puthash, gethash, and remhash. The syntax is
9: ; different though. For small lists property lists are probably what you
10: ; want but when the lists start to become large hash tables become
11: ; infinitely better than property lists.
12:
13: ;; Current bugs: hash-table-rehash and the equal version need to be
14: ;; rewritten. There is no reason to write the array twice.
15:
16: ; Note very carefully that the syntax is <puthash key value hash-table>,
17: ; <gethash key hash-table>, and <remhash key hash-table>.
18:
19: ; Before hash tables are used they have to be made i.e. you first do
20: ; (setq myhash (make-hash-table)) then (puthash 'name 'joe myhash).
21: ; Make-hash-table takes several alternating keywords and arguments
22: ; the only one of which you will probably use is :size. So
23: ; (setq otherhash (make-hash-table ':size 20)) will make otherhash a
24: ; hash table of length 20. If you know what the length of the hash table
25: ; will be and it is greater than about 20 it is a good idea to specify
26: ; the length so that hash-table-rehash will not need to be called.
27: ; This will speed up puthashing considerably especially when the hash
28: ; table is very large.
29: ; Keys must be eq, equal will not work.
30:
31: #+Franz (environment-maclisp)
32:
33: (defstruct (hash-table (:constructor make-hash-table-internal)
34: :named)
35: (real-hash-table (new-vector 17)) ;where entries are stored
36: (hash-table-fullness 0) ; how many entries in table
37: (rehash-after-n-misses 4) ; when puthashing you rehash the table
38: ; if you miss this many times
39: (hash-table-size 17) ; how big the vector is
40: (hash-table-rehash-size 1.5) ; factor to multiply by current size
41: ; to the get new size of the vector
42: (hash-table-rehash-function 'hash-table-rehash))
43:
44: ; Make-hash-table makes a hash table. The vector that all the information
45: ; is stored in is made nmiss larger than the apparent size of the hash
46: ; table so that if you hash to a number close to the size of the table
47: ; you do not miss right off the table. So that for example if you
48: ; hash to the last element of the table and miss you are not aff the table.
49:
50: (defun make-hash-table (&rest options &aux (size 8) (rhf 'hash-table-rehash)
51: (rhs 1.5) (nmisses 4))
52: (loop for (key option) on options by #'cddr
53: do (selectq key
54: (:size (setq size option))
55: (:rehash-function (setq rhf option))
56: (:rehash-size (setq rhs option))
57: (otherwise
58: (ferror () "~S is not a valid hash table option"
59: key))))
60: (setq size (hash-table-good-size (* size 2)))
61: (make-hash-table-internal
62: real-hash-table (new-vector (+ size nmisses))
63: hash-table-size size
64: rehash-after-n-misses nmisses
65: hash-table-rehash-size rhs
66: hash-table-rehash-function rhf))
67:
68: (defun hash-table-good-size (size)
69: (setq size (max (fix size) 17)) ;minimum size is 17
70: (or (oddp size) (setq size (1+ size))) ; make it odd
71: (do ()
72: ((and (not (zerop (\ size 3))) ; make it a semi-prime number
73: (not (zerop (\ size 5)))
74: (not (zerop (\ size 7))))
75: size)
76: (setq size (+ size 2))))
77:
78: ;; Using conses instead of putting increasing the size of the data table
79: ;; by a factor of two, decreases the amount of storage required for a
80: ;; partially full hash table but can adversely affect the paging and
81: ;; caching behavior of the hash table. Sometime, should meter this
82: ;; difference. (A compactifying garbage collector could help.)
83:
84: (defmacro make-hash-element (key value) ; creates a hash element
85: `(cons ,key ,value))
86:
87: (defmacro hash-key (element) ; the key given a hash element
88: `(car ,element))
89:
90: (defmacro hash-value (element) ; the value of a hash element
91: `(cdr ,element))
92:
93: (defmacro si:hash-code (hash-table key) ;hash code for key
94: `(\ (maknum ,key) (hash-table-size ,hash-table)))
95:
96: ; Gethash either returns the value associated with that key in that
97: ; hash table or nil if there is none.
98:
99: (defun gethash (key hash-table &aux position-value)
100: (do ((try-position (si:hash-code hash-table key) (1+ try-position))
101: (n (rehash-after-n-misses hash-table) (1- n))
102: (real-hash-table (real-hash-table hash-table)))
103: ((zerop n) nil) ;it is not there so just return nil
104: (cond ((eq key
105: (hash-key (setq position-value
106: (vref real-hash-table try-position))))
107: (return (hash-value position-value))))))
108:
109: (eval-when (compile load eval)
110: (defsetf gethash (e v) `(puthash ,(cadr e) ,v ,(caddr e))))
111:
112: ; Puthash inserts a hash-element for the given key and value in the
113: ; hash table that is passed to it. If the key already exists in the hash
114: ; table the value of that key is replaced by the new value. If it finds an
115: ; empty space it adds a hash-element for that key and value into that
116: ; space and increments hash-table-fullness by one. If it cannot find
117: ; the key or an empty space in four tries then it calls rehash on the
118: ; hash table and tries again.
119:
120: (declare (localf puthash-internal))
121:
122: (defun puthash (key value hash-table)
123: (puthash-internal key value hash-table nil))
124:
125: (defun swaphash (key value hash-table)
126: (puthash-internal key value hash-table t))
127:
128: (defun puthash-internal (key value hash-table swap?)
129: (do ((try-position (si:hash-code hash-table key) (1+ try-position))
130: (n (rehash-after-n-misses hash-table) (1- n))
131: (real-hash-table (real-hash-table hash-table)))
132: ((zerop n) ;if cannot find a place in n tries then rehash
133: (funcall (hash-table-rehash-function hash-table)
134: hash-table (hash-table-rehash-size hash-table))
135: (puthash key value hash-table))
136: (cond ((or (eq (hash-key (vref real-hash-table try-position))
137: key)
138: (and (null (vref real-hash-table try-position))
139: (setf (hash-table-fullness hash-table)
140: (1+ (hash-table-fullness hash-table)))))
141: (return
142: (prog1 (if swap? (hash-value (vref real-hash-table try-position))
143: value)
144: (setf (vref real-hash-table try-position)
145: (make-hash-element key value))))))))
146:
147: ; Remhash removes the hash-element associated with the given key from
148: ; the hash table that is passed to it. If it finds the element and removes
149: ; it then it returns the key. If it cannot find the element then it returns
150: ; nil.
151:
152: (defun remhash (key hash-table)
153: (do ((try-position (si:hash-code hash-table key) (1+ try-position))
154: (n (rehash-after-n-misses hash-table) (1- n))
155: (real-hash-table (real-hash-table hash-table)))
156: ((zerop n) nil) ;not in the hash table return nil
157: (cond ((eq (hash-key (vref real-hash-table try-position)) key)
158: (setf (vref real-hash-table try-position) nil)
159: (return key))))) ;return the key if found and removed
160:
161: ; Hash-table-rehash first saves the contents of the current hash table
162: ; in a temporary vector then puthashes the elements of this temporary vector
163: ; into the original hash-table after making it larger by a factor of
164: ; the variable grow.
165:
166: (defun hash-table-rehash (hash-table grow)
167: (let* ((real-hash-table (real-hash-table hash-table))
168: (nmisses (rehash-after-n-misses hash-table))
169: (new-size (+ nmisses
170: (hash-table-good-size (times grow
171: (hash-table-size hash-table)))))
172: (j 0)
173: (temp-array (new-vector new-size)))
174: (do ((current-position 0 (1+ current-position))
175: (old-size (+ (hash-table-size hash-table) nmisses)))
176: ((>= current-position old-size))
177: (let ((current-hash-element (vref real-hash-table current-position)))
178: (cond ((null current-hash-element))
179: (t (setf (vref temp-array j) current-hash-element)
180: (setq j (1+ j))))))
181: (cond ((not (= grow 1)) ;if the hash table has grown
182: (setf (real-hash-table hash-table) (new-vector new-size))
183: (setf (hash-table-fullness hash-table) 0)
184: (setf (hash-table-size hash-table) (- new-size nmisses))))
185: (do ((position 0 (1+ position))) ;add old values to new table
186: ((= position j))
187: (puthash (hash-key (vref temp-array position))
188: (hash-value (vref temp-array position))
189: hash-table))))
190:
191: (defun si:lookhash (hash-table)
192: (let ((real-hash-table (real-hash-table hash-table)))
193: (loop for num from 0 to (1- (vsize real-hash-table))
194: collect (vref real-hash-table num))))
195:
196: (defun maphash (func hash-table)
197: (let ((real-hash-table (real-hash-table hash-table)))
198: (loop for num from 0 to (1- (vsize real-hash-table))
199: with keyword and value
200: do (setq keyword (vref real-hash-table num))
201: unless (null keyword)
202: do (progn (setq value (cdr keyword)
203: keyword (car keyword))
204: (funcall func keyword value)))))
205:
206: ;; SXHASH
207: ;; Sigh, this also comes from the LISP machine
208:
209: (defun sxhash (x)
210: (cond ((symbolp x)
211: (sxhash-string (get_pname x)))
212: ((stringp x)
213: (sxhash-string x))
214: ((eq (typep x) 'fixnum)
215: (if (minusp x)
216: (logxor x #o-1777777777)
217: x))
218: ((dtpr x)
219: (do ((rot 4)
220: (hash 0)
221: (y))
222: ((atom x)
223: (if (not (null x))
224: (setq hash (logxor (rot (sxhash x) (- rot 4)) hash)))
225: (if (minusp hash)
226: (logxor hash #o-1777777777)
227: hash))
228: (setq y (pop x))
229: (if (>= (setq rot (+ rot 7)) 24)
230: (setq rot (- rot 24)))
231: (setq hash (logxor (rot (cond ((symbolp y)
232: (sxhash-string (get_pname y)))
233: ((stringp y)
234: (sxhash-string y))
235: ((eq (typep y) 'fixnum)
236: y)
237: (t (sxhash y)))
238: rot)
239: hash))))
240: ((bigp x)
241: (sxhash (bignum-to-list x)))
242: ((floatp x)
243: (fix x))
244: (t 0)))
245:
246: (defun sxhash-string (string)
247: (do ((i 1 (1+ i))
248: (n (flatc string))
249: (hash 0))
250: ((> i n)
251: (if (minusp hash)
252: (logxor hash #o-1777777777)
253: hash))
254: (setq hash (rot (logxor (getcharn string i) #o177) 7))))
255:
256: ;; Equal hash tables
257:
258: ;; Notice the slots are exactly the same as in hash-table so we use the same
259: ;; macros.
260:
261: (defstruct (equal-hash-table (:constructor make-equal-hash-table-internal)
262: :named)
263: (real-hash-table (new-vector 17)) ;where entries are stored
264: (hash-table-fullness 0) ; how many entries in table
265: (rehash-after-n-misses 4) ; when puthashing you rehash the table
266: ; if you miss this many times
267: (hash-table-size 17) ; how big the vector is
268: (hash-table-rehash-size 1.5) ; factor to multiply by current size
269: ; to the get new size of the vector
270: (hash-table-rehash-function 'equal-hash-table-rehash))
271:
272: ; Make-hash-table makes a hash table. The vector that all the information
273: ; is stored in is made nmiss larger than the apparent size of the hash
274: ; table so that if you hash to a number close to the size of the table
275: ; you do not miss right off the table. So that for example if you
276: ; hash to the last element of the table and miss you are not aff the table.
277:
278: (defun make-equal-hash-table (&rest options &aux (size 8)
279: (rhf 'hash-table-rehash)
280: (rhs 1.5) (nmisses 4))
281: (loop for (key option) on options by #'cddr
282: do (selectq key
283: (:size (setq size option))
284: (:rehash-function (setq rhf option))
285: (:rehash-size (setq rhs option))
286: (otherwise
287: (ferror () "~S is not a valid hash table option"
288: key))))
289: (setq size (hash-table-good-size (* size 2)))
290: (make-equal-hash-table-internal
291: real-hash-table (new-vector (+ size nmisses))
292: hash-table-size size
293: rehash-after-n-misses nmisses
294: hash-table-rehash-size rhs
295: hash-table-rehash-function rhf))
296:
297: ; Gethash-equal either returns the value associated with that key in that
298: ; hash table or nil if there is none.
299:
300: (defun gethash-equal (key hash-table &aux position-value)
301: (do ((try-position (remainder (sxhash key) (hash-table-size hash-table))
302: (1+ try-position))
303: (n (rehash-after-n-misses hash-table) (1- n))
304: (real-hash-table (real-hash-table hash-table)))
305: ((zerop n) nil) ;it is not there so just return nil
306: (cond ((equal key
307: (hash-key (setq position-value
308: (vref real-hash-table try-position))))
309: (return (hash-value position-value))))))
310:
311: (eval-when (eval compile load)
312: (defsetf gethash-equal (e v) `(puthash-equal ,(cadr e) v ,(caddr e))))
313:
314: ; Puthash inserts a hash-element for the given key and value in the
315: ; hash table that is passed to it. If the key already exists in the hash
316: ; table the value of that key is replaced by the new value. If it finds an
317: ; empty space it adds a hash-element for that key and value into that
318: ; space and increments hash-table-fullness by one. If it cannot find
319: ; the key or an empty space in four tries then it calls rehash on the
320: ; hash table and tries again.
321:
322: (declare (localf puthash-equal-internal))
323:
324: (defun puthash-equal (key value hash-table)
325: (puthash-equal-internal key value hash-table nil))
326:
327: (defun swaphash-equal (key value hash-table)
328: (puthash-equal-internal key value hash-table t))
329:
330: (defun puthash-equal-internal (key value hash-table swap?)
331: (do ((try-position (remainder (sxhash key) (hash-table-size hash-table))
332: (1+ try-position))
333: (n (rehash-after-n-misses hash-table) (1- n))
334: (real-hash-table (real-hash-table hash-table)))
335: ((zerop n) ;if cannot find a place in n tries then rehash
336: (funcall (hash-table-rehash-function hash-table)
337: hash-table (hash-table-rehash-size hash-table))
338: (puthash-equal key value hash-table))
339:
340: (cond ((or (equal (hash-key (vref real-hash-table try-position))
341: key)
342: (and (null (vref real-hash-table try-position))
343: (setf (hash-table-fullness hash-table)
344: (1+ (hash-table-fullness hash-table)))))
345: (return
346: (prog1 (if swap? (hash-value
347: (vref real-hash-table try-position))
348: value)
349: (setf (vref real-hash-table try-position)
350: (make-hash-element key value))))))))
351:
352:
353: ; Remhash removes the hash-element associated with the given key from
354: ; the hash table that is passed to it. If it finds the element and removes
355: ; it then it returns the key. If it cannot find the element then it returns
356: ; nil.
357:
358: (defun remhash-equal (key hash-table)
359: (do ((try-position (remainder (sxhash key) (hash-table-size hash-table))
360: (1+ try-position))
361: (n (rehash-after-n-misses hash-table) (1- n))
362: (real-hash-table (real-hash-table hash-table)))
363: ((zerop n) nil) ;not in the hash table return nil
364: (cond ((equal (hash-key (vref real-hash-table try-position)) key)
365: (setf (vref real-hash-table try-position) nil)
366: (return key))))) ;return the key if found and removed
367:
368:
369: ; Hash-table-rehash first saves the contents of the current hash table
370: ; in a temporary vector then puthashes the elements of this temporary vector
371: ; into the original hash-table after making it larger by a factor of
372: ; the variable grow.
373:
374: (defun equal-hash-table-rehash (hash-table grow)
375: (let* ((real-hash-table (real-hash-table hash-table))
376: (nmisses (rehash-after-n-misses hash-table))
377: (new-size (+ nmisses
378: (hash-table-good-size (times grow
379: (hash-table-size hash-table)))))
380: (j 0)
381: (temp-array (new-vector new-size)))
382: (do ((current-position 0 (1+ current-position))
383: (old-size (+ (hash-table-size hash-table) nmisses)))
384: ((>= current-position old-size))
385: (let ((current-hash-element (vref real-hash-table current-position)))
386: (cond ((null current-hash-element))
387: (t (setf (vref temp-array j) current-hash-element)
388: (setq j (1+ j))))))
389: (cond ((not (= grow 1)) ;if the hash table has grown
390: (setf (real-hash-table hash-table) (new-vector new-size))
391: (setf (hash-table-fullness hash-table) 0)
392: (setf (hash-table-size hash-table) (- new-size nmisses))))
393: (do ((position 0 (1+ position))) ;add old values to new table
394: ((= position j))
395: (puthash (hash-key (vref temp-array position))
396: (hash-value (vref temp-array position))
397: hash-table))))
398:
399: (defun maphash-equal (func hash-table)
400: (let ((real-hash-table (real-hash-table hash-table)))
401: (loop for num from 0 to (1- (vsize real-hash-table))
402: with keyword and value
403: do (setq keyword (vref real-hash-table num))
404: unless (null keyword)
405: do (progn (setq value (cdr keyword)
406: keyword (car keyword))
407: (funcall func keyword value)))))
408:
409: (sstatus feature hash-tables)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.