comparison doodle/containers/list.d @ 40:1f97022e5c6d

Checkpoint. Development continues...
author daveb
date Mon, 12 Apr 2010 14:01:54 +0930
parents
children
comparison
equal deleted inserted replaced
39:b6c34f1fc7f3 40:1f97022e5c6d
1 module doodle.containers.list;
2
3 ///
4 /// Doubly-linked list.
5 /// Consists of a sequence of cells with references to the previous and next member.
6 /// List keeps track of its length.
7 ///
8 /// The list has two ends:
9 /// "head" and "tail".
10 /// There are two directions:
11 /// "forwards" -> towards the "head" and
12 /// "backwards" -> towards the "tail"
13 /// opApply works from the head to the tail.
14 /// opApplyReverse works from the tail to the head.
15 ///
16 /// When used as a queue, typically use addTail and removeHead.
17 ///
18 /// Design goals of list is to be completely symmetric and clear in its
19 /// forwards and backwards operations, and to be able to contain immutable types.
20 ///
21 class List(T) {
22
23 //
24 // Standard operations:
25 //
26
27 /// Construct an empty list
28 this() { }
29
30 /// Return a copy of the list
31 List!(T) dup() const {
32 auto l = new List!(T);
33 for (auto c = &mHead; c; c = &c.mBackward) {
34 l.addTail(c.mElement);
35 }
36 return l;
37 }
38
39 /// Return true if the list is empty
40 bool isEmpty() const {
41 return mLength == 0;
42 }
43
44 /// Return the length of the list
45 uint length() const {
46 return mLength;
47 }
48
49 /// Return the head of the list, asserting if the list is empty
50 T head() {
51 assert(!isEmpty());
52 return mHead.mElement;
53 }
54
55 /// Return the tail of the list, asserting if the list is empty
56 T tail() {
57 assert(!isEmpty());
58 return mTail.mElement;
59 }
60
61 /// Add to the head of the list
62 void addHead(T t) {
63 add(mHead, null, t);
64 }
65
66 /// Add to the tail of the list
67 void addTail(T t) {
68 add(null, mTail, t);
69 }
70
71 /// Remove the head of the list, asserting if the list is empty
72 T removeHead() {
73 return remove(mHead).mElement;
74 }
75
76 /// Remove the tail of the list, asserting of the list is empty
77 T removeTail() {
78 return remove(mTail).mElement;
79 }
80
81 // Clear the list (garbage collector does the rest)
82 void clear() {
83 mHead = mTail = null;
84 mLength = 0;
85 }
86
87 /// Iterate over the list from head to tail - used by foreach
88 int opApply(int delegate(ref T) dg) {
89 int result;
90
91 auto c = mHead;
92
93 while (c !is null) {
94 auto t = c.mElement;
95 result = dg(t);
96 if (result != 0) {
97 break;
98 }
99 c = c.mBackward;
100 }
101
102 return result;
103 }
104
105 /// Iterate over the list from tail to head - used by foreach_reverse
106 int opApplyReverse(int delegate(ref T) dg) {
107 int result;
108
109 auto c = mTail;
110
111 while (c !is null) {
112 auto t = c.mElement;
113 result = dg(t);
114 if (result != 0) {
115 break;
116 }
117 c = c.mForward;
118 }
119
120 return result;
121 }
122
123
124 //
125 // Iterator operations:
126 //
127
128 /// Return the first matching element going backward from head, or null
129 Iterator findHead(T t) {
130 for (auto c = mHead; c !is null; c = c.mBackward) {
131 if (c.mElement == t) {
132 return new Iterator(c);
133 }
134 }
135
136 return null;
137 }
138
139 /// Return the first matching element going forward from tail, or null
140 Iterator findTail(T t) {
141 for (auto c = mTail; c !is null; c = c.mForward) {
142 if (c.mElement == t) {
143 return new Iterator(c);
144 }
145 }
146
147 return null;
148 }
149
150 /// insert a new entry into list, forward of entry specified by iterator
151 void insertForward(Iterator i, T t) {
152 assert(i !is null);
153 add(i.mCell, i.mCell.mForward, t);
154 }
155
156 /// insert a new entry into list, backward of entry specified by iterator
157 void insertBackward(Iterator i, T t) {
158 assert(i !is null);
159 add(i.mCell.mBackward, i.mCell, t);
160 }
161
162 /// Remove specified entry from list, returning iterator to entry forward of removed one
163 Iterator eraseForward(Iterator i) {
164 assert(i !is null);
165 Cell cell = remove(i.mCell);
166 return cell.mForward ? new Iterator(cell.mForward) : null;
167 }
168
169 /// Remove specified entry from list, returning iterator to entry backward of removed one
170 Iterator eraseBackward(Iterator i) {
171 assert(i !is null);
172 Cell cell = remove(i.mCell);
173 return cell.mBackward ? new Iterator(cell.mBackward) : null;
174 }
175
176 ///
177 /// Iterator for a List
178 ///
179 class Iterator {
180
181 private Cell mCell;
182
183 private this(Cell cell) {
184 assert(cell !is null);
185 mCell = cell;
186 }
187
188 T element() {
189 return mCell.mElement;
190 }
191
192 void goForward() {
193 assert(mCell.mForward !is null);
194 mCell = mCell.mForward;
195 }
196
197 void goBackward() {
198 assert(mCell.mBackward !is null);
199 mCell = mCell.mBackward;
200 }
201
202 bool forwardValid() const {
203 return mCell.mForward !is null;
204 }
205
206 bool backwardValid() const {
207 return mCell.mBackward !is null;
208 }
209
210 override bool opEquals(Object other) const {
211 Iterator o = cast(Iterator) other;
212 if (!o) return 0;
213 return mCell is o.mCell;
214 }
215 }
216
217 //
218 // Implementation:
219 //
220
221 private {
222 Cell mHead;
223 Cell mTail;
224 uint mLength;
225
226 /// An entry in the list
227 class Cell {
228 Cell mForward, mBackward;
229 T mElement;
230
231 this(T element, Cell backward, Cell forward) {
232 mElement = element;
233 mBackward = backward;
234 mForward = forward;
235 }
236 }
237
238 /// Add new cell between backward and forward
239 void add(Cell backward, Cell forward, T t) {
240 Cell cell = new Cell(t, backward, forward);
241
242 if (backward) {
243 backward.mForward = cell;
244 }
245 else {
246 mTail = cell;
247 }
248
249 if (forward) {
250 forward.mBackward = cell;
251 }
252 else {
253 mHead = cell;
254 }
255
256 ++mLength;
257 }
258
259 /// Remove a cell from the list, returning the removed cell
260 Cell remove(Cell cell) {
261 if (cell.mBackward) {
262 cell.mBackward.mForward = cell.mForward;
263 }
264 else {
265 mTail = cell.mForward;
266 }
267
268 if (cell.mForward) {
269 cell.mForward.mBackward = cell.mBackward;
270 }
271 else {
272 mHead = cell.mBackward;
273 }
274
275 --mLength;
276
277 return cell;
278 }
279 }
280 }