-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathlist.go
249 lines (226 loc) · 6.73 KB
/
list.go
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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
package mlink
// A List is a singly-linked ordered list. A zero value is ready for use.
//
// The methods of a List value do not allow direct modification of the list.
// To insert and update entries in the list, use the At, Find, Last, or End
// methods to obtain a Cursor to a location in the list. A cursor can be used
// to insert, update, and delete elements of the list.
type List[T any] struct {
first entry[T] // sentinel; first.link points to the real first element
}
// NewList returns a new empty list.
func NewList[T any]() *List[T] { return new(List[T]) }
// IsEmpty reports whether lst is empty.
func (lst *List[T]) IsEmpty() bool { return lst.first.link == nil }
// Clear discards all the values in lst, leaving it empty. Calling Clear
// invalidates all cursors to the list.
func (lst *List[T]) Clear() { lst.first.link.invalidate(); lst.first.link = nil }
// Peek reports whether lst has a value at offset n from the front of the list,
// and if so returns its value.
//
// This method takes time proportional to n. Peek will panic if n < 0.
func (lst *List[T]) Peek(n int) (T, bool) {
cur := lst.At(n)
return cur.Get(), !cur.AtEnd()
}
// Each is a range function that calls f with each value in lst in order from
// first to last. If f returns false, Each returns immediately.
func (lst *List[T]) Each(f func(T) bool) {
for cur := lst.cfirst(); !cur.AtEnd(); cur.Next() {
if !f(cur.Get()) {
return
}
}
}
// Len reports the number of elements in lst. This method takes time proportional
// to the length of the list.
func (lst *List[T]) Len() (n int) {
for range lst.Each {
n++
}
return
}
// At returns a cursor to the element at index n ≥ 0 in the list. If n is
// greater than or equal to n.Len(), At returns a cursor to the end of the list
// (equivalent to End).
//
// At will panic if n < 0.
func (lst *List[T]) At(n int) *Cursor[T] {
if n < 0 {
panic("index out of range")
}
cur := lst.cfirst()
for ; !cur.AtEnd(); cur.Next() {
if n == 0 {
break
}
n--
}
return &cur
}
// Last returns a cursor to the last element of the list. If lst is empty, it
// returns a cursor to the end of the list (equivalent to End).
// This method takes time proportional to the length of the list.
func (lst *List[T]) Last() *Cursor[T] {
cur := lst.cfirst()
if !cur.AtEnd() {
for cur.pred.link.link != nil {
cur.Next()
}
}
return &cur
}
// End returns a cursor to the position just past the end of the list.
// This method takes time proportional to the length of the list.
func (lst *List[T]) End() *Cursor[T] { c := lst.Last(); c.Next(); return c }
// Find returns a cursor to the first element of the list for which f returns
// true. If no such element is found, the resulting cursor is at the end of the
// list.
func (lst *List[T]) Find(f func(T) bool) *Cursor[T] {
cur := lst.cfirst()
for !cur.AtEnd() {
if f(cur.Get()) {
break
}
cur.Next()
}
return &cur
}
func (lst *List[T]) cfirst() Cursor[T] { return Cursor[T]{pred: &lst.first} }
// A Cursor represents a location in a list. A nil *Cursor is not valid, and
// operations on it will panic. Through a valid cursor, the caller can add,
// modify, or remove elements, and navigate forward through the list.
//
// Multiple cursors into the same list are fine, but note that modifying the
// list through one cursor may invalidate others.
type Cursor[T any] struct {
// pred is the entry in its list whose link points to the target. This
// permits a cursor to delete the element it points to from the list.
// Invariant: pred != nil
pred *entry[T]
}
// Get returns the value at c's location. If c is at the end of the list, Get
// returns a zero value.
func (c *Cursor[T]) Get() T {
if c.AtEnd() {
var zero T
return zero
}
return c.pred.checkValid().link.X
}
// Set replaces the value at c's location. If c is at the end of the list,
// calling Set is equivalent to calling Push.
//
// Before:
//
// [1, 2, 3]
// ^--- c
//
// After c.Set(9)
//
// [1, 9, 3]
// ^--- c
func (c *Cursor[T]) Set(v T) {
if c.AtEnd() {
c.pred.link = &entry[T]{X: v}
// N.B.: c is now no longer AtEnd
} else {
c.pred.checkValid().link.X = v
}
}
// AtEnd reports whether c is at the end of its list.
func (c *Cursor[T]) AtEnd() bool { return c.pred.checkValid().link == nil }
// Next advances c to the next position in the list (if possible) and reports
// whether the resulting position is at the end of the list. If c was already
// at the end its position is unchanged.
func (c *Cursor[T]) Next() bool {
if c.AtEnd() {
return false
}
c.pred = c.pred.link
return !c.AtEnd()
}
// Push inserts a new value into the list at c's location. After insertion, c
// points to the newly-added item and the previous value is now at c.Next().
//
// Before:
//
// [1, 2, 3]
// ^--- c
//
// After c.Push(4):
//
// [4, 1, 2, 3]
// ^--- c
func (c *Cursor[T]) Push(v T) {
added := &entry[T]{X: v, link: c.pred.checkValid().link}
c.pred.link = added
}
// Add inserts one or more new values into the list at c's location. After
// insertion, c points to the original item, now in the location after the
// newly-added values. This is a shorthand for Push followed by Next.
//
// Before:
//
// [1, 2, 3]
// ^--- c
//
// After c.Add(4):
//
// [4, 1, 2, 3]
// ^--- c
func (c *Cursor[T]) Add(vs ...T) {
for _, v := range vs {
c.Push(v)
c.Next()
}
}
// Remove removes and returns the element at c's location from the list. If c
// is at the end of the list, Remove does nothing and returns a zero value.
//
// After removal, c is still valid and points the element after the one that
// was removed, or the end of the list.
//
// Successfully removing an element invalidates any cursors to the location
// after the element that was removed.
//
// Before:
//
// [1, 2, 3, 4]
// ^--- c
//
// After c.Remove()
//
// [1, 3, 4]
// ^--- c
func (c *Cursor[T]) Remove() T {
if c.AtEnd() {
var zero T
return zero
}
// Detach the discarded entry from its neighbor so that any cursors pointing
// to that entry will be AtEnd, and changes made through them will not
// affect the remaining list.
val := c.pred.link.X
next := c.pred.link.link
c.pred.link.link = c.pred.link // invalidate the outgoing (but not all)
c.pred.link = next // the successor of the removed element
return val
}
// Truncate removes all the elements of the list at and after c's location.
// After calling Truncate, c is at the end of the remaining list. If c was
// already at the end of the list, Truncate does nothing. After truncation, c
// remains valid.
//
// Truncate invalidates any cursors to locations after c in the list.
//
// Before:
//
// [1, 2, 3, 4]
// ^--- c
//
// After c.Truncate():
//
// [1, 2] *
// ^--- c (c.AtEnd() == true)
func (c *Cursor[T]) Truncate() { c.pred.link.invalidate(); c.pred.link = nil }