Newer
Older
;;; -*- Mode: Lisp ; Base: 10 ; Syntax: ANSI-Common-Lisp -*-
;;;;; Generic heap interface
("package" "base/utils" "base/macros" #|"interface/order"|# "stateful/package")))
(exporting-definitions
;;;; Abstract Interface
;; Base class
(defclass container () ()
(:documentation "base virtual class for stateful container objects"))
(defgeneric copy-container (container)
(:documentation "duplicate a container into one with same contents"))
;; Emptiness, Size
(define-condition container-empty () ())
(defgeneric container-empty-p (container))
(defgeneric empty-container! (container))
(defun check-not-empty (container)
(when (container-empty-p container)
(error 'container-empty)))
(defgeneric container-size (container))
;; Containers and lists
(defgeneric container-add-list! (container list)
(:method ((container container) list)
(dolist (item list) (insert-item! container item))
(values)))
(defun container-from-list! (class list &rest r)
(apply #'make-instance class :initial-contents list r))
(defgeneric list-from-container! (container)
(:method (container)
(loop :until (container-empty-p container)
:collect (pop-item! container))))
(defgeneric set-container-contents-from-list! (container list)
(:method (container list)
(empty-container! container)
(container-add-list! container list)))
(defgeneric container-contents (container)
(:documentation "extract a fresh list containing the contents of the container"))
;; Insertion, Merging
(defgeneric insert-item! (container item))
(defgeneric merge-containers! (subject object))
;; Finding, Deleting
(defgeneric find-least-item (container)
Francois-Rene Rideau
committed
(:method :before (container)
(check-not-empty container)))
(defgeneric least-item (container)
Francois-Rene Rideau
committed
(:method :before (container)
(check-not-empty container)))
(defgeneric pop-least-item! (container)
Francois-Rene Rideau
committed
(:method :before (container)
(check-not-empty container)))
(defgeneric pop-item! (container)
(:method :before (container)
(check-not-empty container))
Francois-Rene Rideau
committed
(:method :before (container)
(pop-least-item! container)))
(defgeneric pop-last-item! (container)
Francois-Rene Rideau
committed
(:method :before (container)
(check-not-empty container)))
(defgeneric push-last-item! (container item))
(defgeneric delete-item! (container item))
(defgeneric node-item (container node))
(defgeneric (setf node-item) (item container node))
(defgeneric find-node (container item))
(defgeneric insert-node! (container item))
(defgeneric delete-node! (container item))
(defgeneric prune-node! (container item))
(defgeneric find-item (container item)
(:method (container item)
(node-item container (find-node container item))))
;;;; Generic container mixins
;;; Heaps
(defclass heap (#|order:<order>|# container)
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
())
;;; containers with nodes
(defclass node-container-mixin ()
((node-class :initarg :node-class
:accessor container-node-class
:allocation :class)))
(defclass node-mixin ()
((content :initarg :content :accessor node-content)))
(defgeneric make-node (container &rest args)
(:method ((container node-container-mixin) &rest args)
(apply #'make-instance (container-node-class container) args)))
(defmethod node-item ((container node-container-mixin) node)
(node-content node))
(defmethod (setf node-item) (item (container node-container-mixin) node)
(setf (node-content node) item))
(defgeneric exchange-nodes (container n1 n2)
(:method ((container node-container-mixin) n1 n2)
(declare (ignorable container))
(rotatef (node-content n1) (node-content n2))))
(defmethod initialize-instance :after ((container node-container-mixin) &key initial-contents &allow-other-keys)
(when initial-contents
(container-add-list! container initial-contents)))
;;; containers having an easily computed size
(defclass sized-container-mixin ()
())
(defmethod container-empty-p ((container sized-container-mixin))
(zerop (container-size container)))
;;; containers caching their actual size in a slot
(defclass cached-size-mixin (sized-container-mixin)
((size :initarg :size :initform 0
:accessor container-size)))
(defmethod empty-container! :after ((container cached-size-mixin))
(setf (container-size container) 0))
#|(defmethod insert-item! :after ((container cached-size-mixin) item)
(incf (container-size container)))
(defmethod delete-item! :after ((container cached-size-mixin) item)
(decf (container-size container)))
(defmethod merge-containers! :after ((c1 cached-size-mixin) c2)
(incf (container-size c1) (container-size c2)))|#
;;; containers using a simple vector as storage for their elements
(defclass vector-container-mixin (container cached-size-mixin)
((vector :initarg :vector
:initform #()
:accessor container-vector)))
(defmethod initialize-instance :after ((container vector-container-mixin)
&key initial-size initial-contents &allow-other-keys)
(when (or initial-size initial-contents)
(let* ((contents-size (length initial-contents))
(size (max (or initial-size 0) contents-size)))
(setf (container-vector container) (make-array size)
(container-size container) contents-size)
(replace (container-vector container) initial-contents))))
(defun-inline vector-container-ref (container index)
(svref (container-vector container) index))
(defun-inline (setf vector-container-ref) (new-value container index)
(setf (svref (container-vector container) index) new-value))
(defmethod empty-container! :after ((container vector-container-mixin))
(fill (container-vector container) nil))
(defgeneric adjust-size (container newsize)
(:method ((container vector-container-mixin) newsize)
(let ((oldvec (container-vector container))
(newvec (make-array newsize :initial-element nil)))
(replace newvec oldvec :end1 (container-size container))
(setf (container-vector container) newvec)
container)))
(defgeneric maybe-adjust-size-down (container)
(:method ((container vector-container-mixin))
(let ((buffer-size (length (container-vector container)))
(contents-size (container-size container)))
(when (< (* 4 contents-size) buffer-size)
(adjust-size container (* 2 contents-size))))))
(defgeneric maybe-adjust-size-up (container needed-size)
(:method ((container vector-container-mixin) needed-size)
(let ((buffer-size (length (container-vector container)))
(contents-size (container-size container)))
(unless (<= needed-size buffer-size)
(adjust-size container (max needed-size (* 2 contents-size)))))))
(defmethod pop-last-item! ((container vector-container-mixin))
(let* ((index (decf (container-size container)))
(item (svref (container-vector container) index)))
(setf (svref (container-vector container) index) nil)
(maybe-adjust-size-down container)
item))
(defmethod push-last-item! ((container vector-container-mixin) item)
(let* ((oldsize (container-size container))
(newsize (1+ oldsize)))
(maybe-adjust-size-up container newsize)
(setf (container-size container) newsize
(vector-container-ref container oldsize) item)
container))
)
#|
What I'd like to have, that CLOS won't provide, is some more declarative
metaprogramming facility, where you get a static optimized combination of
containers based on a specification of entities and their interrelation,
where entities include containers and contained, indices and abstract pointers,
and operations and invariants supposed as input or provided as output.
Can you change an item based on this type of pointer, and delete it, but
not insert a new node next? How do we merge existing entries with same key?
Is the container a pure datastructure or is there state? What pure abstract
view is the state local to (i.e. when we reify "the world" for
debugging/migration/specification/evolution/refactoring or whatever purpose?
And so if I want fast insert/remove from a set and adding to another one,
then doubly linked lists make a lot of sense. And if I *also* want
|#