Annotation of 43BSD/ucb/lisp/lisplib/hash.l, revision 1.1.1.1

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)

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.