source: release/1.5/source/contrib/krueger/InterfaceProjects/Utilities/assoc-array.lisp

Last change on this file was 13646, checked in by R. Matthew Emerson, 15 years ago

Merge r13631, r13636 from trunk. (Paul Krueger's updated InterfaceProjects
contrib; fix for ticket:652)

File size: 6.2 KB
Line 
1;;; assoc-array.lisp
2
3;; Implements an N-dimensional "array" where indices are arbitrary objects
4;; Implementation creates nested hash-tables
5
6#|
7The MIT license.
8
9Copyright (c) 2010 Paul L. Krueger
10
11Permission is hereby granted, free of charge, to any person obtaining a copy of this software
12and associated documentation files (the "Software"), to deal in the Software without restriction,
13including without limitation the rights to use, copy, modify, merge, publish, distribute,
14sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
15furnished to do so, subject to the following conditions:
16
17The above copyright notice and this permission notice shall be included in all copies or substantial
18portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
21LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24SOFTWARE 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
Note: See TracBrowser for help on using the repository browser.