-
Notifications
You must be signed in to change notification settings - Fork 42
/
queue.lisp
225 lines (180 loc) · 6.18 KB
/
queue.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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
225
(in-package #:serapeum)
(defun make-queue-cons ()
(let ((q (cons nil nil)))
(setf (car q) q)))
(declaim (inline make-queue queuep))
(defstruct (queue (:constructor make-queue (&aux (cons (make-queue-cons))))
(:copier nil)
(:predicate queuep))
"Basic cons queues, with an implementation based on PAIP and the
original Norvig & Waters paper, and an API mostly borrowed from Arc.
About Arc. For the most part, Arc-style identifiers are pessimal,
neither quite opaque nor quite explicit, like riddles. But by using
abbreviated names, we avoid the danger of clashing with
special-purpose queue implementations.
Create a queue with `queue', like `list':
(queue 1 2 3) => #<QUEUE (1 2 3)>
Get the items with `qlist':
(qlist (queue 1 2 3)) => '(1 2 3)
Add items with `enq':
(enq 3 (queue 1 2)) => #<QUEUE (1 2 3)>
Remove an item with `deq':
(deq (queue 1 2 3)) => 1
Prepend an item with `undeq':
(qlist (undeq 1 (queue 2 3))) => '(1 2 3)
To (destructively) join a list to the end of the queue, use `qconc':
(qconc (queue 1 2 3) '(4 5 6)) => #<QUEUE (1 2 3 4 5 6)>
The rest of the API:
- `queuep' Test for a queue
- `qlen' Like `(length (qlist ...))'
- `clear-queue' Clear the queue
- `front' Like `(car (qlist ...))'
- `qback' Get the last element of the queue
- `queue-empty-p' Test if the queue is empty
- `qappend' Non-destructively join a list to the end of the queue
- `qconc' Destructively join a list to the end of the queue
- `qprepend' Non-destructively join a list to the front of the queue
Note that support for both `deq' and `undeq' means that a queue is
also effectively a stack. (But not quite a double-ended queue: you can
push to either end, and pop from the front, but you can't pop from the
end.)
The idea is that *collecting* is something we do often enough to
justifying making *collectors* (queues) first-class."
(cons (error "No cons!") :type cons :read-only t))
(declaim-freeze-type queue)
(setf (documentation 'queuep 'function)
"Test for a queue.")
(defmethod print-object ((queue queue) stream)
(if (and *print-readably* *read-eval*)
(progn
(format stream "#.")
(print-object `(queue ,@(qlist queue)) stream))
(print-unreadable-object (queue stream :type t)
(format stream "~a" (qlist queue))))
queue)
(defmethod make-load-form ((queue queue) &optional env)
(declare (ignore env))
(values `(make-queue)
`(qconc ',queue (list ,@(qlist queue)))))
(-> queue (&rest t) (values queue &optional))
(defun queue (&rest initial-contents)
"Build a new queue with INITIAL-CONTENTS."
(qappend (make-queue) initial-contents))
(define-compiler-macro queue (&whole decline &rest xs)
"When there are no initial elements, use the bare constructor,
allowing the queue to be declared dynamic-extent."
(if xs
decline
`(make-queue)))
(defpattern queue (&rest args)
(with-unique-names (it)
`(trivia:guard1 ,it
(typep ,it 'queue)
(qlist ,it)
(list ,@args))))
(-> clear-queue (queue) list)
(defun clear-queue (queue)
"Return QUEUE's contents and reset it."
(prog1 (qlist queue)
(let ((q (queue-cons queue)))
(setf (cdr q) nil
(car q) q))))
(-> qlen (queue) array-length)
(defun qlen (queue)
"The number of items in QUEUE."
(length (qlist queue)))
(-> qlist (queue) list)
(defun qlist (queue)
"A list of the items in QUEUE.
Does not cons."
(cdr (queue-cons queue)))
(-> enq (t queue) queue)
(defun enq (item queue)
"Insert ITEM at the end of QUEUE."
(let ((q (queue-cons queue)))
(setf (car q)
(setf (cdr (car q))
(cons item nil))))
queue)
(-> deq (queue) t)
(defun deq (queue)
"Remove item from the front of the QUEUE."
;; Bizarrely, the version in PAIP returns the queue, not the
;; item dequeued. This version from Waters & Norvig,
;; "Implementing Queues in Lisp."
(let* ((q (queue-cons queue))
(items (cdr q)))
(unless (setf (cdr q) (cdr items))
(setf (car q) q))
(car items)))
(-> undeq (t queue) queue)
(defun undeq (item queue)
"Add an item to the front of QUEUE.
For an empty queue, this does the same thing as ENQ.
For a queue with elements, this adds a new element onto the front of
queue (like pushing to an ordinary list).
This is called `undeq' because it can be used to undo a `deq'."
(let ((q (queue-cons queue)))
(if (cdr q)
(push item (cdr q))
(enq item queue))
queue))
(-> queue-empty-p (queue) boolean)
(defun queue-empty-p (queue)
"Is QUEUE empty?"
(not (qlist queue)))
(-> front (queue) t)
(defun front (queue)
"The first element in QUEUE."
(first (qlist queue)))
(defun (setf front) (value queue)
(if (queue-empty-p queue)
(enq value queue)
(setf (car (qlist queue))
value))
value)
(-> qback (queue) (values t &optional))
(defun qback (queue)
"Get the last element of a queue."
(unless (queue-empty-p queue)
(caar (queue-cons queue))))
(defun (setf qback) (value queue)
(if (queue-empty-p queue)
(enq value queue)
(setf (caar (queue-cons queue))
value))
value)
(-> qconc (queue list) queue)
(defun qconc (queue list)
"Destructively concatenate LIST onto the end of QUEUE.
Return the queue."
(when (null list)
(return-from qconc queue))
(let ((q (queue-cons queue)))
(setf (car q)
(last (setf (cdr (car q)) list))))
queue)
(-> qappend (queue list) queue)
(defun qappend (queue list)
"Append the elements of LIST onto the end of QUEUE.
Return the queue."
(if list
;; It's probably faster to just copy LIST once and concatenate
;; it than to access the queue for each element.
(qconc queue (copy-list list))
queue))
(defun qprepend (list queue)
"Insert ITEMS at the beginning of QUEUE."
(qpreconc (copy-list list) queue))
(defun qpreconc (list queue)
"Destructively splice LIST at the beginning of QUEUE."
(let ((q (queue-cons queue)))
(if (cdr q)
(setf (cdr q)
(nconc list (cdr q)))
(qconc queue list))
queue))
(-> copy-queue (queue) (values queue &optional))
(defun copy-queue (queue)
"Copy QUEUE as another queue."
(qappend (queue) (qlist queue)))