@@ -23,6 +23,10 @@ public struct LazyChunked<Base: Collection, Subject> {
23
23
@usableFromInline
24
24
internal let belongInSameGroup : ( Subject , Subject ) -> Bool
25
25
26
+ /// The upper bound of the first chunk.
27
+ @usableFromInline
28
+ internal var firstUpperBound : Base . Index
29
+
26
30
@usableFromInline
27
31
internal init (
28
32
base: Base ,
@@ -32,46 +36,37 @@ public struct LazyChunked<Base: Collection, Subject> {
32
36
self . base = base
33
37
self . projection = projection
34
38
self . belongInSameGroup = belongInSameGroup
39
+ self . firstUpperBound = base. startIndex
40
+
41
+ if !base. isEmpty {
42
+ firstUpperBound = endOfChunk ( startingAt: base. startIndex)
43
+ }
35
44
}
36
45
}
37
46
38
47
extension LazyChunked : LazyCollectionProtocol {
39
48
/// A position in a chunked collection.
40
49
public struct Index : Comparable {
41
- /// The lower bound of the chunk at this position.
50
+ /// The range corresponding to the chunk at this position.
42
51
@usableFromInline
43
- internal var lowerBound : Base . Index
52
+ internal var baseRange : Range < Base . Index >
44
53
45
- /// The upper bound of the chunk at this position.
46
- ///
47
- /// `upperBound` is optional so that computing `startIndex` can be an O(1)
48
- /// operation. When `upperBound` is `nil`, the actual upper bound is found
49
- /// when subscripting or calling `index(after:)`.
50
54
@usableFromInline
51
- internal var upperBound : Base . Index ?
52
-
53
- @usableFromInline
54
- internal init ( lowerBound: Base . Index , upperBound: Base . Index ? = nil ) {
55
- self . lowerBound = lowerBound
56
- self . upperBound = upperBound
55
+ internal init ( _ baseRange: Range < Base . Index > ) {
56
+ self . baseRange = baseRange
57
57
}
58
58
59
59
@inlinable
60
60
public static func == ( lhs: Index , rhs: Index ) -> Bool {
61
- // Only use the lower bound to test for equality, since sometimes the
62
- // `startIndex` will have an upper bound of `nil` and sometimes it won't,
63
- // such as when retrieved by:
64
- // `c.index(before: c.index(after: c.startIndex))`.
65
- //
66
61
// Since each index represents the range of a disparate chunk, no two
67
62
// unique indices will have the same lower bound.
68
- lhs. lowerBound == rhs. lowerBound
63
+ lhs. baseRange . lowerBound == rhs. baseRange . lowerBound
69
64
}
70
65
71
66
@inlinable
72
67
public static func < ( lhs: Index , rhs: Index ) -> Bool {
73
68
// Only use the lower bound to test for ordering, as above.
74
- lhs. lowerBound < rhs. lowerBound
69
+ lhs. baseRange . lowerBound < rhs. baseRange . lowerBound
75
70
}
76
71
}
77
72
@@ -87,28 +82,27 @@ extension LazyChunked: LazyCollectionProtocol {
87
82
88
83
@inlinable
89
84
public var startIndex : Index {
90
- Index ( lowerBound : base. startIndex)
85
+ Index ( base. startIndex..< firstUpperBound )
91
86
}
92
87
93
88
@inlinable
94
89
public var endIndex : Index {
95
- Index ( lowerBound : base. endIndex)
90
+ Index ( base . endIndex ..< base. endIndex)
96
91
}
97
92
98
93
@inlinable
99
94
public func index( after i: Index ) -> Index {
100
95
precondition ( i != endIndex, " Can't advance past endIndex " )
101
- let upperBound = i. upperBound ?? endOfChunk ( startingAt : i . lowerBound )
96
+ let upperBound = i. baseRange . upperBound
102
97
guard upperBound != base. endIndex else { return endIndex }
103
98
let end = endOfChunk ( startingAt: upperBound)
104
- return Index ( lowerBound : upperBound, upperBound : end)
99
+ return Index ( upperBound..< end)
105
100
}
106
101
107
102
@inlinable
108
103
public subscript( position: Index ) -> Base . SubSequence {
109
- let upperBound = position. upperBound
110
- ?? endOfChunk ( startingAt: position. lowerBound)
111
- return base [ position. lowerBound..< upperBound]
104
+ precondition ( position != endIndex, " Can't subscript using endIndex " )
105
+ return base [ position. baseRange]
112
106
}
113
107
}
114
108
@@ -144,8 +138,8 @@ extension LazyChunked: BidirectionalCollection
144
138
@inlinable
145
139
public func index( before i: Index ) -> Index {
146
140
precondition ( i != startIndex, " Can't advance before startIndex " )
147
- let start = startOfChunk ( endingAt: i. lowerBound)
148
- return Index ( lowerBound : start, upperBound : i . lowerBound)
141
+ let start = startOfChunk ( endingAt: i. baseRange . lowerBound)
142
+ return Index ( start..< i . baseRange . lowerBound)
149
143
}
150
144
}
151
145
@@ -157,9 +151,7 @@ extension LazyCollectionProtocol {
157
151
/// Returns a lazy collection of subsequences of this collection, chunked by
158
152
/// the given predicate.
159
153
///
160
- /// - Complexity: O(1). When iterating over the resulting collection,
161
- /// accessing each successive chunk has a complexity of O(*m*), where *m*
162
- /// is the length of the chunk.
154
+ /// - Complexity: O(*n*), because the start index is pre-computed.
163
155
@inlinable
164
156
public func chunked(
165
157
by belongInSameGroup: @escaping ( Element , Element ) -> Bool
@@ -173,9 +165,7 @@ extension LazyCollectionProtocol {
173
165
/// Returns a lazy collection of subsequences of this collection, chunked by
174
166
/// grouping elements that project to the same value.
175
167
///
176
- /// - Complexity: O(1). When iterating over the resulting collection,
177
- /// accessing each successive chunk has a complexity of O(*m*), where *m*
178
- /// is the length of the chunk.
168
+ /// - Complexity: O(*n*), because the start index is pre-computed.
179
169
@inlinable
180
170
public func chunked< Subject: Equatable > (
181
171
on projection: @escaping ( Element ) -> Subject
@@ -276,7 +266,7 @@ public struct ChunkedByCount<Base: Collection> {
276
266
/// Creates a view instance that presents the elements of `base`
277
267
/// in `SubSequence` chunks of the given count.
278
268
///
279
- /// - Complexity: O(n)
269
+ /// - Complexity: O(*n*), because the start index is pre-computed.
280
270
@inlinable
281
271
internal init ( _base: Base , _chunkCount: Int ) {
282
272
self . base = _base
@@ -522,7 +512,7 @@ extension Collection {
522
512
/// print(c.chunks(ofCount: 3).map(Array.init))
523
513
/// // [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
524
514
///
525
- /// - Complexity: O(1)
515
+ /// - Complexity: O(*n*), because the start index is pre-computed.
526
516
@inlinable
527
517
public func chunks( ofCount count: Int ) -> ChunkedByCount < Self > {
528
518
precondition ( count > 0 , " Cannot chunk with count <= 0! " )
0 commit comments