| [13631] | 1 | ;;; assoc-array.lisp
|
|---|
| 2 |
|
|---|
| 3 | ;; Implements an N-dimensional "array" where indices are arbitrary objects
|
|---|
| 4 | ;; Implementation creates nested hash-tables
|
|---|
| 5 |
|
|---|
| 6 | #|
|
|---|
| 7 | The MIT license.
|
|---|
| 8 |
|
|---|
| 9 | Copyright (c) 2010 Paul L. Krueger
|
|---|
| 10 |
|
|---|
| 11 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
|---|
| 12 | and associated documentation files (the "Software"), to deal in the Software without restriction,
|
|---|
| 13 | including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
|---|
| 14 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
|---|
| 15 | furnished to do so, subject to the following conditions:
|
|---|
| 16 |
|
|---|
| 17 | The above copyright notice and this permission notice shall be included in all copies or substantial
|
|---|
| 18 | portions of the Software.
|
|---|
| 19 |
|
|---|
| 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|---|
| 21 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|---|
| 22 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|---|
| 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|---|
| 24 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|---|
| 25 |
|
|---|
| 26 | |#
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 | (defpackage :interface-utilities
|
|---|
| 30 | (:nicknames :iu)
|
|---|
| 31 | (:export assoc-array
|
|---|
| 32 | assoc-aref
|
|---|
| 33 | mapcar-assoc-array
|
|---|
| 34 | mapcar-hash-keys))
|
|---|
| 35 |
|
|---|
| 36 | (in-package :iu)
|
|---|
| 37 |
|
|---|
| 38 | (defclass assoc-array ()
|
|---|
| 39 | ((arr-rank :accessor arr-rank :initarg :rank)
|
|---|
| 40 | (index1-ht :accessor index1-ht)
|
|---|
| 41 | (index-tests :accessor index-tests :initarg :tests)
|
|---|
| 42 | (default-value :accessor default-value :initarg :default))
|
|---|
| 43 | (:default-initargs
|
|---|
| 44 | :rank 2
|
|---|
| 45 | :tests nil
|
|---|
| 46 | :default nil))
|
|---|
| 47 |
|
|---|
| 48 | (defmethod initialize-instance :after ((self assoc-array) &key tests &allow-other-keys)
|
|---|
| 49 | (setf (index1-ht self)
|
|---|
| 50 | (make-hash-table :test (or (first tests) #'eql))))
|
|---|
| 51 |
|
|---|
| 52 | (defmethod assoc-aref ((self assoc-array) &rest indices)
|
|---|
| 53 | (unless (eql (list-length indices) (arr-rank self))
|
|---|
| 54 | (error "Access to ~s requires ~s indices" self (arr-rank self)))
|
|---|
| 55 | (do* ((res (index1-ht self))
|
|---|
| 56 | (index-list indices (rest index-list))
|
|---|
| 57 | (indx (first index-list) (first index-list))
|
|---|
| 58 | (found-next t))
|
|---|
| 59 | ((null index-list) (if found-next
|
|---|
| 60 | (values res t)
|
|---|
| 61 | (values (default-value self) nil)))
|
|---|
| 62 | (if found-next
|
|---|
| 63 | (multiple-value-setq (res found-next) (gethash indx res))
|
|---|
| 64 | (return-from assoc-aref (values (default-value self) nil)))))
|
|---|
| 65 |
|
|---|
| 66 | (defmethod (setf assoc-aref) (new-val (self assoc-array) &rest indices)
|
|---|
| 67 | (unless (eql (list-length indices) (arr-rank self))
|
|---|
| 68 | (error "Access to ~s requires ~s indices" self (arr-rank self)))
|
|---|
| 69 | (let* ((ht (index1-ht self))
|
|---|
| 70 | (last-indx (do* ((dim 1 (1+ dim))
|
|---|
| 71 | (index-list indices (rest index-list))
|
|---|
| 72 | (indx (first index-list) (first index-list))
|
|---|
| 73 | (tests (rest (index-tests self)) (rest tests))
|
|---|
| 74 | (test (first tests) (first tests)))
|
|---|
| 75 | ((>= dim (arr-rank self)) indx)
|
|---|
| 76 | (multiple-value-bind (next-ht found-next) (gethash indx ht)
|
|---|
| 77 | (unless found-next
|
|---|
| 78 | (setf next-ht (make-hash-table :test (or test #'eql)))
|
|---|
| 79 | (setf (gethash indx ht) next-ht))
|
|---|
| 80 | (setf ht next-ht)))))
|
|---|
| 81 | (setf (gethash last-indx ht) new-val)))
|
|---|
| 82 |
|
|---|
| 83 | (defmethod mapcar-assoc-array ((func function) (self assoc-array) &rest indices)
|
|---|
| 84 | ;; collects list of results of applying func to each bound index at
|
|---|
| 85 | ;; the next level after the indices provided.
|
|---|
| 86 | (unless (<= (list-length indices) (arr-rank self))
|
|---|
| 87 | (error "Access to ~s requires ~s or fewer indices" self (arr-rank self)))
|
|---|
| 88 | (do* ((res (index1-ht self))
|
|---|
| 89 | (index-list indices (rest index-list))
|
|---|
| 90 | (indx (first index-list) (first index-list))
|
|---|
| 91 | (found-next t))
|
|---|
| 92 | ((null index-list) (when found-next
|
|---|
| 93 | ;; apply map function to res
|
|---|
| 94 | (typecase res
|
|---|
| 95 | (hash-table (mapcar-hash-keys func res))
|
|---|
| 96 | (cons (mapcar func res))
|
|---|
| 97 | (sequence (map 'list func res)))))
|
|---|
| 98 | (if found-next
|
|---|
| 99 | (multiple-value-setq (res found-next) (gethash indx res))
|
|---|
| 100 | (return-from mapcar-assoc-array nil))))
|
|---|
| 101 |
|
|---|
| 102 | (defmethod map-assoc-array ((func function) (self assoc-array) &rest indices)
|
|---|
| 103 | ;; collects list of results of applying func of two arguments to
|
|---|
| 104 | ;; a bound index at the next level after the indices provided and to
|
|---|
| 105 | ;; the value resulting from indexing the array by appending that index
|
|---|
| 106 | ;; to those provided as initial arguments. This would typically be used
|
|---|
| 107 | ;; to get a list of all keys and values at the lowest level of an
|
|---|
| 108 | ;; assoc-array.
|
|---|
| 109 | (unless (<= (list-length indices) (arr-rank self))
|
|---|
| 110 | (error "Access to ~s requires ~s or fewer indices" self (arr-rank self)))
|
|---|
| 111 | (do* ((res (index1-ht self))
|
|---|
| 112 | (index-list indices (rest index-list))
|
|---|
| 113 | (indx (first index-list) (first index-list))
|
|---|
| 114 | (found-next t))
|
|---|
| 115 | ((null index-list) (when found-next
|
|---|
| 116 | ;; apply map function to res
|
|---|
| 117 | (typecase res
|
|---|
| 118 | (hash-table (map-hash-keys func res))
|
|---|
| 119 | (cons (mapcar func res nil))
|
|---|
| 120 | (sequence (map 'list func res nil)))))
|
|---|
| 121 | (if found-next
|
|---|
| 122 | (multiple-value-setq (res found-next) (gethash indx res))
|
|---|
| 123 | (return-from map-assoc-array nil))))
|
|---|
| 124 |
|
|---|
| 125 | (defun print-last-level (key val)
|
|---|
| 126 | (format t "~%Key = ~s Value = ~s" key val))
|
|---|
| 127 |
|
|---|
| 128 | (defmethod last-level ((self assoc-array) &rest indices)
|
|---|
| 129 | (apply #'map-assoc-array #'print-last-level self indices))
|
|---|
| 130 |
|
|---|
| 131 | (defmethod map-hash-keys ((func function) (self hash-table))
|
|---|
| 132 | (let ((res nil))
|
|---|
| 133 | (maphash #'(lambda (key val)
|
|---|
| 134 | (push (funcall func key val) res))
|
|---|
| 135 | self)
|
|---|
| 136 | (nreverse res)))
|
|---|
| 137 |
|
|---|
| 138 | (defmethod mapcar-hash-keys ((func function) (self hash-table))
|
|---|
| 139 | (let ((res nil))
|
|---|
| 140 | (maphash #'(lambda (key val)
|
|---|
| 141 | (declare (ignore val))
|
|---|
| 142 | (push (funcall func key) res))
|
|---|
| 143 | self)
|
|---|
| 144 | (nreverse res)))
|
|---|
| 145 |
|
|---|
| 146 | (provide :assoc-array)
|
|---|
| 147 |
|
|---|