diff lisp/oobr/hasht.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 4103f0995bd7
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/oobr/hasht.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,338 @@
+;;!emacs
+;;
+;; FILE:         hasht.el
+;; SUMMARY:      Create hash tables from lists and operate on them.
+;; USAGE:        GNU Emacs Lisp Library
+;; KEYWORDS:     extensions, tools
+;;
+;; AUTHOR:       Bob Weiner
+;; ORG:          Motorola Inc.
+;;
+;; ORIG-DATE:    16-Mar-90 at 03:38:48
+;; LAST-MOD:     27-Sep-95 at 21:31:20 by Bob Weiner
+;;
+;; Copyright (C) 1990-1995  Free Software Foundation, Inc.
+;; See the file BR-COPY for license information.
+;;
+;; This file is part of the OO-Browser.
+;;
+;; DESCRIPTION:  
+;;
+;;   Featureful set of hash table operators for use in personal programs.
+;;
+;;   'hash-make' creates a hash table from an association list, 'hash-add'
+;;   adds a value-key pair to a hash table, and 'hash-lookup' finds the value
+;;   associated with a given key in a hash table, if any.
+;;
+;;   'hash-map' does the same thing as 'mapcar' but operates on hash tables
+;;   instead.
+;;
+;;   For a list of 300 items, these hash tables improve lookup times by a
+;;   factor of between 8 and 10 to 1 over those for an unsorted list.
+;;
+;;   Public and private function names are alphabetized for easy location.
+;;
+;; DESCRIP-END.
+
+;;; ************************************************************************
+;;; Public variables
+;;; ************************************************************************
+
+(defvar hash-merge-values-function 'hash-merge-values
+  "*Function to call in hash-merge to merge the values from 2 hash tables that contain the same key.
+It is sent the two values as arguments.")
+
+;;; ************************************************************************
+;;; Public functions
+;;; ************************************************************************
+
+(defun hash-add (value key hash-table)
+  "Add VALUE, any lisp object, referenced by KEY, a string, to HASH-TABLE.
+Replaces any VALUE previously referenced by KEY."
+  (if (hashp hash-table)
+      (let* ((obarray (hash-obarray hash-table))
+	     (sym (intern key obarray)))
+	(if sym (set sym value)))))
+
+(defun hash-copy (hash-table)
+  "Return a copy of HASH-TABLE, list and vector elements are shared across both tables."
+  (if (not (hashp hash-table))
+      (error "(hash-copy): Invalid hash-table: '%s'" hash-table))
+  (let ((htable-copy (hash-make (length (hash-obarray hash-table)))))
+    (hash-map
+     (function (lambda (elt) (hash-add (car elt) (cdr elt) htable-copy)))
+     hash-table)
+    htable-copy))
+
+(defun hash-count (hash-table)
+  "Return number of elements stored in HASH-TABLE or nil if not a valid hash table."
+  (if (hashp hash-table)
+      (let ((obarray (hash-obarray hash-table))
+	    (count 0))
+	(mapatoms (function
+		    (lambda (sym)
+		      (and (boundp sym) sym (setq count (1+ count)))))
+		  obarray)
+	count)))
+
+(defun hash-delete (key hash-table)
+  "Delete element referenced by KEY, a string, from HASH-TABLE.
+Return nil if KEY is not in HASH-TABLE or non-nil otherwise."
+  (if (hashp hash-table)
+      (let* ((obarray (hash-obarray hash-table))
+	     (sym (intern-soft key obarray)))
+	(and sym (boundp sym) (makunbound sym)))))
+
+(defun hash-deep-copy (obj)
+  "Return a copy of OBJ with new copies of all elements, except symbols."
+  (cond ((null obj) nil)
+	((stringp obj)
+	 (copy-sequence obj))
+	((hashp obj)
+	 (let ((htable-copy (hash-make (length (hash-obarray obj)))))
+	   (mapcar
+	    (function
+	     (lambda (elt) (hash-add (car elt) (cdr elt) htable-copy)))
+	    (hash-map 'hash-deep-copy obj))
+	   htable-copy))
+	((vectorp obj)
+	 ;; convert to list for mapping
+	 (setq obj (append obj nil))
+	 ;; Return as a vector
+	 (vconcat (mapcar 'hash-deep-copy obj)))
+	((atom obj) obj)
+	((nlistp obj)
+	 (error "(hash-deep-copy): Invalid type, '%s'" obj))
+	(t ;; list
+	 (cons (hash-deep-copy (car obj)) (hash-deep-copy (cdr obj))))))
+
+(fset  'hash-get  'hash-lookup)
+(defun hash-key-p (key hash-table)
+  "Return non-nil iff KEY is in HASH-TABLE.  KEY's hash table symbol is returned."
+  (if (hashp hash-table)
+      (let* ((obarray (hash-obarray hash-table))
+	     (sym (intern-soft key obarray)))
+	 (if (boundp sym) sym))))
+
+(defun hash-lookup (key hash-table)
+  "Lookup KEY in HASH-TABLE and return associated value.
+If value is nil, this function does not tell you whether or not KEY is in the
+hash table.  Use 'hash-key-p' instead for that function."
+  (if (hashp hash-table)
+      (let* ((obarray (hash-obarray hash-table))
+	     (sym (intern-soft key obarray)))
+	 (if (boundp sym) (symbol-value sym)))))
+
+(defun hash-make (initializer &optional reverse)
+  "Create a hash table from INITIALIZER.
+INITIALIZER may be an alist with elements of the form (<value> . <key>) from
+which the hash table is built.  Alternatively, it may be a non-negative
+integer which is used as the minimum size of a new, empty hash table.
+Optional non-nil second argument REVERSE means INITIALIZER has elements of
+form (<key> . <value>)."
+  (if (integerp initializer)
+      (if (>= initializer 0)
+	  (cons 'hasht (make-vector (hash-next-prime initializer) 0))
+	(error "(hash-make): Initializer must be >= 0, not '%s'"
+	       initializer))
+    (let* ((vlen (hash-next-prime (length initializer)))
+	   (obarray (make-vector vlen 0))
+	   sym)
+      (mapcar
+       (function
+	(lambda (cns)
+	  (let ((key) (value))
+	    (if (consp cns)
+		(if reverse
+		    (setq key (car cns) value (cdr cns))
+		  (setq key (cdr cns) value (car cns))))
+	    (if (setq sym (intern key))
+		(set sym value)))))
+       initializer)
+      (cons 'hasht obarray))))
+
+(defun hash-map (func hash-table)
+  "Return result of application of FUNC to each (<value> . <key>) element of HASH-TABLE."
+  (if (not (hashp hash-table))
+      (error "(hash-map): Invalid hash-table: '%s'" hash-table))
+  (let ((result))
+    (mapatoms (function
+		(lambda (sym)
+		  (and (boundp sym)
+		       sym
+		       (setq result (cons (funcall
+					   func
+					   (cons (symbol-value sym)
+						 (symbol-name sym)))
+					  result)))))
+	      (hash-obarray hash-table))
+    result))
+
+(defun hash-merge (&rest hash-tables)
+  "Merge any number of HASH-TABLES.  Return resultant hash table.
+A single argument consisting of a list of hash tables may also be given.
+Return an empty hash table if any argument from the merge list is other
+than nil or a hash table."
+  (let ((empty-ht (hash-make 1)))
+    (and (not (hashp (car hash-tables)))
+	 (listp (car hash-tables))
+	 ;; Handle situation where a list of hash-tables is passed in as a
+	 ;; single argument, rather than as multiple arguments.
+	 (setq hash-tables (car hash-tables)))
+    (if (memq nil (mapcar (function (lambda (ht) (or (null ht) (hashp ht))))
+			  hash-tables))
+	empty-ht
+      (setq hash-tables
+	    (delq nil (mapcar (function (lambda (ht)
+					  (if (equal ht empty-ht)
+					      nil ht)))
+			      hash-tables)))
+      (let ((len (length hash-tables)))
+	(cond ((= len 0) empty-ht)
+	      ((= len 1) (car hash-tables))
+	      (t (let ((htable (hash-make
+				(apply '+ (mapcar 'hash-count hash-tables))))
+		       key value)
+		   (mapcar
+		     (function
+		       (lambda (ht)
+			 (hash-map (function
+				     (lambda (val-key-cons)
+				       (setq value (car val-key-cons)
+					     key (cdr val-key-cons))
+				       (if (not (hash-key-p key htable))
+					   (hash-add value key htable)
+					 ;; Merge values
+					 (hash-add
+					  (funcall hash-merge-values-function
+						   (hash-get key htable)
+						   value)
+					  key htable))))
+				   ht)))
+		     hash-tables)
+		   htable)))))))
+
+(defun hash-merge-values (value1 value2)
+  "Return a list from merging VALUE1 and VALUE2 or creating a new list.
+Nil values are thrown away.  If both arguments are lists, their elements are
+assumed to be strings and the result is a set of ordered strings."
+  (cond ((and (listp value1) (listp value2))
+	 ;; Assume desired result is a set of strings.
+	 (br-set-of-strings (sort (append value1 value2) 'string<)))
+	((null value1)
+	 value2)
+	((null value2)
+	 value1)
+	((listp value1)
+	 (cons value2 value1))
+	((listp value2)
+	 (cons value1 value2))
+	(t (list value1 value2))))
+
+(make-obsolete 'hash-new 'hash-make)
+(defun hash-new (size)
+  "Return a new hash table of SIZE elements.
+This is obsolete.  Use 'hash-make' instead."
+  (hash-make size))
+
+(defun hash-prin1 (hash-table &optional stream)
+  "Output the printed representation of HASH-TABLE.
+Quoting characters are printed when needed to make output that `read'
+can handle, whenever this is possible.
+Output stream is STREAM, or value of `standard-output'."
+  (if (not (hashp hash-table))
+      (progn (prin1 hash-table stream)
+	     (princ "\n" stream))
+    (princ "\(\n" stream)
+    (hash-map
+      (function (lambda (val-key-cons)
+		  (prin1 val-key-cons stream)
+		  (princ "\n" stream)))
+      hash-table)
+    (princ "\)\n" stream)))
+
+(defun hash-replace (value key hash-table)
+  "Replace VALUE referenced by KEY, a string, in HASH-TABLE.
+An error will occur if KEY is not found in HASH-TABLE."
+  (if (hashp hash-table)
+       (let* ((obarray (hash-obarray hash-table))
+	      (sym (intern-soft key obarray)))
+	 (if (and (boundp sym) sym)
+	     (set sym value)
+	   (error "(hash-replace): '%s' key not found in hash table." key)))))
+
+(defun hash-resize (hash-table new-size)
+  "Resize HASH-TABLE to NEW-SIZE without losing any elements and return new table.
+NEW-SIZE must be greater than 0.  Hashing works best if NEW-SIZE is a prime
+number.  See also 'hash-next-prime'."
+  (if (< new-size 1)
+      (error "(hash-resize): Cannot resize hash table to size %d" new-size))
+  (let ((htable (hash-make new-size)))
+    (hash-map (function
+		(lambda (elt)
+		  (hash-add (car elt) (cdr elt) htable)))
+	      hash-table)
+    htable))
+
+(defun hash-resize-p (hash-table)
+  "Resizes HASH-TABLE to 1.5 times its size if above 80% full.
+Returns new hash table when resized, else nil."
+  (if (hashp hash-table)
+      (let ((count (hash-count hash-table))
+	    (size (length (hash-obarray hash-table))))
+	(if (> (* count (/ count 5)) size)
+	    (hash-resize hash-table (hash-next-prime (+ size (/ size 2))))))))
+
+(defun hash-size (hash-table)
+  "Return size of HASH-TABLE which is >= number of elements in the table.
+Return nil if not a valid hash table."
+  (if (hashp hash-table)
+      (length (hash-obarray hash-table))))
+(fset 'hash-length 'hash-size)
+
+(defun hashp (object)
+  "Return non-nil if OBJECT is a hash-table."
+  (and (listp object) (eq (car object) 'hasht)
+       (vectorp (cdr object))))
+
+;;; ************************************************************************
+;;; Private functions
+;;; ************************************************************************
+
+(defun hash-next-prime (n)
+  "Return next prime number >= N."
+  (if (<= n 2)
+      2
+    (and (= (% n 2) 0) (setq n (1+ n)))
+    (while (not (hash-prime-p n))
+      (setq n (+ n 2)))
+    n))
+
+(defun hash-obarray (hash-table)
+  "Return symbol table (object array) portion of HASH-TABLE."
+  (cdr hash-table))
+
+(defun hash-prime-p (n)
+  "Return non-nil iff N is prime."
+  (if (< n 0) (setq n (- n)))
+  (let ((small-primes '(1 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67
+			  71 73 79 83 89)))
+    (cond ((< n 91) (memq n small-primes))
+	  ((< n 7921)  ;; 89, max small-prime, squared
+	   (let ((prime t)
+		 (pr-list small-primes))
+	     (while (and (setq pr-list (cdr pr-list))
+			 (setq prime (/= (% n (car pr-list)) 0))))
+	     prime))
+	  ((or (= (% n 3) 0) (= (% n 2) 0)) nil)
+	  ((let ((factor1 5)
+		 (factor2 7)
+		 (is-prime))
+	     (while (and (<= (* factor1 factor1) n)
+			 (setq is-prime (and (/= (% n factor1) 0)
+					     (/= (% n factor2) 0))))
+	       (setq factor1 (+ factor1 6)
+		     factor2 (+ factor2 6)))
+	     is-prime)))))
+
+(provide 'hasht)