1 | module datatypes |
2 | |
3 | struct DoublyListNode[T] { |
4 | mut: |
5 | data T |
6 | next &DoublyListNode[T] = unsafe { 0 } |
7 | prev &DoublyListNode[T] = unsafe { 0 } |
8 | } |
9 | |
10 | // DoublyLinkedList[T] represents a generic doubly linked list of elements, each of type T. |
11 | pub struct DoublyLinkedList[T] { |
12 | mut: |
13 | head &DoublyListNode[T] = unsafe { 0 } |
14 | tail &DoublyListNode[T] = unsafe { 0 } |
15 | // Internal iter pointer for allowing safe modification |
16 | // of the list while iterating. TODO: use an option |
17 | // instead of a pointer to determine it is initialized. |
18 | iter &DoublyListIter[T] = unsafe { 0 } |
19 | len int |
20 | } |
21 | |
22 | // is_empty checks if the linked list is empty |
23 | pub fn (list DoublyLinkedList[T]) is_empty() bool { |
24 | return list.len == 0 |
25 | } |
26 | |
27 | // len returns the length of the linked list |
28 | pub fn (list DoublyLinkedList[T]) len() int { |
29 | return list.len |
30 | } |
31 | |
32 | // first returns the first element of the linked list |
33 | pub fn (list DoublyLinkedList[T]) first() !T { |
34 | if list.is_empty() { |
35 | return error('Linked list is empty') |
36 | } |
37 | return list.head.data |
38 | } |
39 | |
40 | // last returns the last element of the linked list |
41 | pub fn (list DoublyLinkedList[T]) last() !T { |
42 | if list.is_empty() { |
43 | return error('Linked list is empty') |
44 | } |
45 | return list.tail.data |
46 | } |
47 | |
48 | // push_back adds an element to the end of the linked list |
49 | pub fn (mut list DoublyLinkedList[T]) push_back(item T) { |
50 | mut new_node := &DoublyListNode[T]{ |
51 | data: item |
52 | } |
53 | if list.is_empty() { |
54 | // first node case |
55 | list.head = new_node |
56 | list.tail = new_node |
57 | } else { |
58 | list.tail.next = new_node |
59 | new_node.prev = list.tail |
60 | list.tail = new_node |
61 | } |
62 | list.len += 1 |
63 | } |
64 | |
65 | // push_front adds an element to the beginning of the linked list |
66 | pub fn (mut list DoublyLinkedList[T]) push_front(item T) { |
67 | mut new_node := &DoublyListNode[T]{ |
68 | data: item |
69 | } |
70 | if list.is_empty() { |
71 | // first node case |
72 | list.head = new_node |
73 | list.tail = new_node |
74 | } else { |
75 | list.head.prev = new_node |
76 | new_node.next = list.head |
77 | list.head = new_node |
78 | } |
79 | list.len += 1 |
80 | } |
81 | |
82 | // pop_back removes the last element of the linked list |
83 | pub fn (mut list DoublyLinkedList[T]) pop_back() !T { |
84 | if list.is_empty() { |
85 | return error('Linked list is empty') |
86 | } |
87 | defer { |
88 | list.len -= 1 |
89 | } |
90 | if list.len == 1 { |
91 | // head == tail |
92 | value := list.tail.data |
93 | list.head = unsafe { nil } |
94 | list.tail = unsafe { nil } |
95 | return value |
96 | } |
97 | value := list.tail.data |
98 | list.tail.prev.next = unsafe { nil } // unlink tail |
99 | list.tail = list.tail.prev |
100 | return value |
101 | } |
102 | |
103 | // pop_front removes the last element of the linked list |
104 | pub fn (mut list DoublyLinkedList[T]) pop_front() !T { |
105 | if list.is_empty() { |
106 | return error('Linked list is empty') |
107 | } |
108 | defer { |
109 | list.len -= 1 |
110 | } |
111 | if list.len == 1 { |
112 | // head == tail |
113 | value := list.head.data |
114 | list.head = unsafe { nil } |
115 | list.tail = unsafe { nil } |
116 | return value |
117 | } |
118 | value := list.head.data |
119 | list.head.next.prev = unsafe { nil } // unlink head |
120 | list.head = list.head.next |
121 | return value |
122 | } |
123 | |
124 | // insert adds an element to the linked list at the given index |
125 | pub fn (mut list DoublyLinkedList[T]) insert(idx int, item T) ! { |
126 | if idx < 0 || idx > list.len { |
127 | return error('Index ${idx} out of bounds [0..${list.len}]') |
128 | } else if idx == 0 { |
129 | // new head |
130 | list.push_front(item) |
131 | } else if idx == list.len { |
132 | // new tail |
133 | list.push_back(item) |
134 | } else if idx <= list.len / 2 { |
135 | list.insert_front(idx, item) |
136 | } else { |
137 | list.insert_back(idx, item) |
138 | } |
139 | } |
140 | |
141 | // insert_back walks from the tail and inserts a new item at index idx |
142 | // (determined from the forward index). This function should be called |
143 | // when idx > list.len/2. This helper function assumes idx bounds have |
144 | // already been checked and idx is not at the edges. |
145 | fn (mut list DoublyLinkedList[T]) insert_back(idx int, item T) { |
146 | mut node := list.node(idx + 1) |
147 | mut prev := node.prev |
148 | // prev node |
149 | // ------ ------ |
150 | // |next|---->|next| |
151 | // |prev|<----|prev| |
152 | // ------ ------ |
153 | new := &DoublyListNode[T]{ |
154 | data: item |
155 | next: node |
156 | prev: prev |
157 | } |
158 | // prev new node |
159 | // ------ ------ ------ |
160 | // |next|---->|next|---->|next| |
161 | // |prev|<----|prev|<----|prev| |
162 | // ------ ------ ------ |
163 | node.prev = new |
164 | prev.next = new |
165 | list.len += 1 |
166 | } |
167 | |
168 | // insert_front walks from the head and inserts a new item at index idx |
169 | // (determined from the forward index). This function should be called |
170 | // when idx <= list.len/2. This helper function assumes idx bounds have |
171 | // already been checked and idx is not at the edges. |
172 | fn (mut list DoublyLinkedList[T]) insert_front(idx int, item T) { |
173 | mut node := list.node(idx - 1) |
174 | mut next := node.next |
175 | // node next |
176 | // ------ ------ |
177 | // |next|---->|next| |
178 | // |prev|<----|prev| |
179 | // ------ ------ |
180 | new := &DoublyListNode[T]{ |
181 | data: item |
182 | next: next |
183 | prev: node |
184 | } |
185 | // node new next |
186 | // ------ ------ ------ |
187 | // |next|---->|next|---->|next| |
188 | // |prev|<----|prev|<----|prev| |
189 | // ------ ------ ------ |
190 | node.next = new |
191 | next.prev = new |
192 | list.len += 1 |
193 | } |
194 | |
195 | // node walks from the head or tail and finds the node at index idx. |
196 | // This helper function assumes the list is not empty and idx is in |
197 | // bounds. |
198 | fn (list &DoublyLinkedList[T]) node(idx int) &DoublyListNode[T] { |
199 | if idx <= list.len / 2 { |
200 | mut node := list.head |
201 | for h := 0; h < idx; h += 1 { |
202 | node = node.next |
203 | } |
204 | return node |
205 | } |
206 | mut node := list.tail |
207 | for t := list.len - 1; t >= idx; t -= 1 { |
208 | node = node.prev |
209 | } |
210 | return node |
211 | } |
212 | |
213 | // index searches the linked list for item and returns the forward index |
214 | // or none if not found. |
215 | pub fn (list &DoublyLinkedList[T]) index(item T) !int { |
216 | mut hn := list.head |
217 | mut tn := list.tail |
218 | for h, t := 0, list.len - 1; h <= t; { |
219 | if hn.data == item { |
220 | return h |
221 | } else if tn.data == item { |
222 | return t |
223 | } |
224 | h += 1 |
225 | hn = hn.next |
226 | t -= 1 |
227 | tn = tn.prev |
228 | } |
229 | return error('none') |
230 | } |
231 | |
232 | // delete removes index idx from the linked list and is safe to call |
233 | // for any idx. |
234 | pub fn (mut list DoublyLinkedList[T]) delete(idx int) { |
235 | if idx < 0 || idx >= list.len { |
236 | return |
237 | } else if idx == 0 { |
238 | list.pop_front() or {} |
239 | return |
240 | } else if idx == list.len - 1 { |
241 | list.pop_back() or {} |
242 | return |
243 | } |
244 | // node should be somewhere in the middle |
245 | mut node := list.node(idx) |
246 | node.prev.next = node.next |
247 | node.next.prev = node.prev |
248 | list.len -= 1 |
249 | } |
250 | |
251 | // str returns a string representation of the linked list |
252 | pub fn (list DoublyLinkedList[T]) str() string { |
253 | return list.array().str() |
254 | } |
255 | |
256 | // array returns a array representation of the linked list |
257 | pub fn (list DoublyLinkedList[T]) array() []T { |
258 | mut result_array := []T{cap: list.len} |
259 | mut node := list.head |
260 | for unsafe { node != 0 } { |
261 | result_array << node.data |
262 | node = node.next |
263 | } |
264 | return result_array |
265 | } |
266 | |
267 | // next implements the iter interface to use DoublyLinkedList with |
268 | // V's `for x in list {` loop syntax. |
269 | pub fn (mut list DoublyLinkedList[T]) next() ?T { |
270 | if list.iter == unsafe { nil } { |
271 | // initialize new iter object |
272 | list.iter = &DoublyListIter[T]{ |
273 | node: list.head |
274 | } |
275 | return list.next() |
276 | } |
277 | if list.iter.node == unsafe { nil } { |
278 | list.iter = unsafe { nil } |
279 | return none |
280 | } |
281 | defer { |
282 | list.iter.node = list.iter.node.next |
283 | } |
284 | return list.iter.node.data |
285 | } |
286 | |
287 | // iterator returns a new iterator instance for the `list`. |
288 | pub fn (mut list DoublyLinkedList[T]) iterator() DoublyListIter[T] { |
289 | return DoublyListIter[T]{ |
290 | node: list.head |
291 | } |
292 | } |
293 | |
294 | // back_iterator returns a new backwards iterator instance for the `list`. |
295 | pub fn (mut list DoublyLinkedList[T]) back_iterator() DoublyListIterBack[T] { |
296 | return DoublyListIterBack[T]{ |
297 | node: list.tail |
298 | } |
299 | } |
300 | |
301 | // DoublyListIter[T] is an iterator for DoublyLinkedList. |
302 | // It starts from *the start* and moves forwards to *the end* of the list. |
303 | // It can be used with V's `for x in iter {` construct. |
304 | // One list can have multiple independent iterators, pointing to different positions/places in the list. |
305 | // A DoublyListIter iterator instance always traverses the list from *start to finish*. |
306 | pub struct DoublyListIter[T] { |
307 | mut: |
308 | node &DoublyListNode[T] = unsafe { 0 } |
309 | } |
310 | |
311 | // next returns *the next* element of the list, or `none` when the end of the list is reached. |
312 | // It is called by V's `for x in iter{` on each iteration. |
313 | pub fn (mut iter DoublyListIter[T]) next() ?T { |
314 | if iter.node == unsafe { nil } { |
315 | return none |
316 | } |
317 | res := iter.node.data |
318 | iter.node = iter.node.next |
319 | return res |
320 | } |
321 | |
322 | // DoublyListIterBack[T] is an iterator for DoublyLinkedList. |
323 | // It starts from *the end* and moves backwards to *the start* of the list. |
324 | // It can be used with V's `for x in iter {` construct. |
325 | // One list can have multiple independent iterators, pointing to different positions/places in the list. |
326 | // A DoublyListIterBack iterator instance always traverses the list from *finish to start*. |
327 | pub struct DoublyListIterBack[T] { |
328 | mut: |
329 | node &DoublyListNode[T] = unsafe { 0 } |
330 | } |
331 | |
332 | // next returns *the previous* element of the list, or `none` when the start of the list is reached. |
333 | // It is called by V's `for x in iter{` on each iteration. |
334 | pub fn (mut iter DoublyListIterBack[T]) next() ?T { |
335 | if iter.node == unsafe { nil } { |
336 | return none |
337 | } |
338 | res := iter.node.data |
339 | iter.node = iter.node.prev |
340 | return res |
341 | } |