9
9
//
10
10
//===----------------------------------------------------------------------===//
11
11
12
- /// A collection that lazily splits a base collection into subsequences separated by elements that satisfy the given `whereSeparator` predicate.
12
+ /// A collection that lazily splits a base collection into subsequences separated by elements that satisfy the
13
+ /// given `whereSeparator` predicate.
13
14
///
14
- /// - Note: This type is the result of `x.split(maxSplits:omittingEmptySubsequences:whereSeparator)` and
15
- /// `x.split(separator:maxSplits:omittingEmptySubsequences)`, where `x` conforms to `LazyCollection`.
16
- public struct LazySplitCollection < Base: LazyCollectionProtocol > where Base. Element: Equatable , Base. Elements. Index == Base . Index {
15
+ /// - Note: This type is the result of
16
+ ///
17
+ /// x.split(maxSplits:omittingEmptySubsequences:whereSeparator)
18
+ /// x.split(separator:maxSplits:omittingEmptySubsequences)
19
+ ///
20
+ /// where `x` conforms to `LazyCollection`.
21
+ public struct LazySplitCollection < Base: LazyCollectionProtocol >
22
+ where Base. Element: Equatable , Base. Elements. Index == Base . Index {
17
23
internal let base : Base
18
- internal let whereSeparator : ( Base . Element ) -> Bool
24
+ internal let isSeparator : ( Base . Element ) -> Bool
19
25
internal let maxSplits : Int
20
26
internal let omittingEmptySubsequences : Bool
21
27
}
22
28
23
29
extension LazySplitCollection {
24
30
public struct Iterator {
31
+ public typealias Index = Base . Index
32
+
25
33
internal let base : Base
26
- internal let whereSeparator : ( Base . Element ) -> Bool
34
+ internal let isSeparator : ( Base . Element ) -> Bool
27
35
internal let maxSplits : Int
28
36
internal let omittingEmptySubsequences : Bool
29
- internal var subSequenceStart : Base . Index
30
- internal var splitCount = 0
37
+ internal var subsequenceStart : Base . Index
38
+ internal var separatorCount = 0
39
+ internal var sequenceLength = 0
31
40
32
41
internal init (
33
42
base: Base ,
@@ -36,10 +45,10 @@ extension LazySplitCollection {
36
45
omittingEmptySubsequences: Bool
37
46
) {
38
47
self . base = base
39
- self . whereSeparator = whereSeparator
48
+ self . isSeparator = whereSeparator
40
49
self . maxSplits = maxSplits
41
50
self . omittingEmptySubsequences = omittingEmptySubsequences
42
- self . subSequenceStart = self . base. startIndex
51
+ self . subsequenceStart = self . base. startIndex
43
52
}
44
53
}
45
54
}
@@ -48,53 +57,91 @@ extension LazySplitCollection.Iterator: IteratorProtocol, Sequence {
48
57
public typealias Element = Base . Elements . SubSequence
49
58
50
59
public mutating func next( ) -> Element ? {
51
- guard subSequenceStart < base. endIndex else { return nil }
60
+ /// Separators mark the points where we want to split (cut in two) the base collection, removing
61
+ /// the separator in the process.
62
+ ///
63
+ /// Each split yields two subsequences, though splitting at the start or end of a sequence yields
64
+ /// an empty subsequence where there were no elements adjacent to the cut.
65
+ ///
66
+ /// Thus the maximum number of subsequences returned after iterating the entire base collection
67
+ /// (including empty ones, if they are not omitted) will be at most one more than the number of
68
+ /// splits made (equivalently, one more than the number of separators encountered).
69
+ ///
70
+ /// The number of splits is limited by `maxSplits`, and thus may be less than total number of
71
+ /// separators in the base collection.
72
+ ///
73
+ /// [1, 2, 42, 3, 4, 42, 5].split(separator: 42,
74
+ /// omittingEmptySubsequences: false)
75
+ /// // first split -> [1, 2], [3, 4, 42, 5]
76
+ /// // last split -> [1, 2], [3, 4], [5]
77
+ ///
78
+ /// [1, 2, 42, 3, 4, 42, 5, 42].split(separator: 42,
79
+ /// maxSplits: 2,
80
+ /// omittingEmptySubsequences: false)
81
+ /// // first split -> [1, 2], [3, 4, 42, 5, 42]
82
+ /// // last split -> [1, 2], [3, 4], [5, 42]
83
+ ///
84
+ /// [42, 1, 42].split(separator: 42, omittingEmptySubsequences: false)
85
+ /// // first split -> [], [1, 42]
86
+ /// // last split -> [], [1], []
87
+ ///
88
+ /// [42, 42].split(separator: 42, omittingEmptySubsequences: false)
89
+ /// // first split -> [], [42]
90
+ /// // last split -> [], [], []
91
+ ///
92
+ /// Preconditions:
93
+ /// `subsequenceStart` points to the beginning of the next subsequence to return (which may
94
+ /// turn out to be empty), or the end of the base collection.
52
95
53
- var subSequenceEnd : Base . Index
54
- var lastAdjacentSeparator : Base . Index
96
+ guard subsequenceStart < base. endIndex else {
97
+ if !omittingEmptySubsequences && sequenceLength < separatorCount + 1 {
98
+ /// We've reached the end of the base collection, and we're returning empty subsequences, but we
99
+ /// haven't yet returned one more subsequence than the number of splits we've performed (i.e., the
100
+ /// number of separators we've encountered). This happens when the last element of the base
101
+ /// collection is a separator. Return one last empty subsequence.
102
+ sequenceLength += 1
103
+ return base. elements [ subsequenceStart..< subsequenceStart]
104
+ } else {
105
+ return nil
106
+ }
107
+ }
108
+
109
+ /// The non-inclusive end of the next subsequence is marked by the next separator, or the end of the base collection.
110
+ var subsequenceEnd : Base . Index
55
111
56
- if splitCount < maxSplits {
57
- splitCount += 1
58
- subSequenceEnd = base [ subSequenceStart... ] . firstIndex ( where: whereSeparator) ?? base. endIndex
59
- lastAdjacentSeparator = subSequenceEnd
112
+ /// The number of separators encountered thus far is identical to the number of splits performed thus far.
113
+ if separatorCount < maxSplits {
114
+ subsequenceEnd = base [ subsequenceStart... ] . firstIndex ( where: isSeparator) ?? base. endIndex
60
115
61
- if omittingEmptySubsequences {
62
- /// TODO: should be able to replace this raw loop with something like
63
- /// ```
64
- /// lastAdjacentSeparator = indexBeforeFirst { !whereSeparator($0) } ?? base.endIndex
65
- /// ```
66
- /// when available.
67
- while lastAdjacentSeparator < base. endIndex {
68
- let next = base. index ( after: lastAdjacentSeparator)
69
- if next < base. endIndex && whereSeparator ( base [ next] ) {
70
- lastAdjacentSeparator = next
71
- } else {
72
- break
73
- }
116
+ if omittingEmptySubsequences && base [ subsequenceStart..< subsequenceEnd] . isEmpty {
117
+ /// Find the next sequence of non-separators.
118
+ subsequenceStart = base [ subsequenceEnd... ] . firstIndex ( where: { !isSeparator( $0) } ) ?? base. endIndex
119
+ if subsequenceStart == base. endIndex {
120
+ /// No non-separators left in the base collection, so we're done.
121
+ return nil
74
122
}
123
+ subsequenceEnd = base [ subsequenceStart... ] . firstIndex ( where: isSeparator) ?? base. endIndex
75
124
}
76
125
} else {
77
- subSequenceEnd = base. endIndex
78
- lastAdjacentSeparator = subSequenceEnd
126
+ /// We've performed the requested number of splits. Return all remaining elements in the base collection as one final subsequence.
127
+ subsequenceEnd = base . endIndex
79
128
}
80
129
81
130
defer {
82
- if lastAdjacentSeparator < base. endIndex {
83
- subSequenceStart = base. index ( after: lastAdjacentSeparator)
84
- } else {
85
- subSequenceStart = base. endIndex
86
- }
131
+ separatorCount += subsequenceEnd < base. endIndex ? 1 : 0
132
+ sequenceLength += 1
133
+ subsequenceStart = subsequenceEnd < base. endIndex ? base. index ( after: subsequenceEnd) : base. endIndex
87
134
}
88
135
89
- return base. elements [ subSequenceStart ..< subSequenceEnd ]
136
+ return base. elements [ subsequenceStart ..< subsequenceEnd ]
90
137
}
91
138
}
92
139
93
140
extension LazySplitCollection : LazySequenceProtocol {
94
141
public func makeIterator( ) -> Iterator {
95
142
return Iterator (
96
143
base: self . base,
97
- whereSeparator: self . whereSeparator ,
144
+ whereSeparator: self . isSeparator ,
98
145
maxSplits: self . maxSplits,
99
146
omittingEmptySubsequences: self . omittingEmptySubsequences
100
147
)
@@ -106,8 +153,9 @@ extension LazyCollection where Element: Equatable {
106
153
/// that don't contain elements satisfying the given predicate.
107
154
///
108
155
/// The resulting lazy sequence consists of at most `maxSplits + 1` subsequences.
109
- /// Elements that are used to split the sequence are not returned as part of
110
- /// any subsequence.
156
+ /// Elements that are used to split the collection are not returned as part of any
157
+ /// subsequence (except possibly the last one, in the case where `maxSplits` is
158
+ /// less than the number of separators in the collection).
111
159
///
112
160
/// The following examples show the effects of the `maxSplits` and
113
161
/// `omittingEmptySubsequences` parameters when lazily splitting a string using a
@@ -179,13 +227,13 @@ extension LazyCollection where Element: Equatable {
179
227
func split(
180
228
maxSplits: Int = Int . max,
181
229
omittingEmptySubsequences: Bool = true ,
182
- whereSeparator: @escaping ( Base . Element ) -> Bool
230
+ whereSeparator isSeparator : @escaping ( Base . Element ) -> Bool
183
231
) -> LazySplitCollection < Self > {
184
232
precondition ( maxSplits >= 0 , " Must take zero or more splits " )
185
233
186
234
return LazySplitCollection (
187
235
base: self ,
188
- whereSeparator : whereSeparator ,
236
+ isSeparator : isSeparator ,
189
237
maxSplits: maxSplits,
190
238
omittingEmptySubsequences: omittingEmptySubsequences
191
239
)
@@ -195,8 +243,9 @@ extension LazyCollection where Element: Equatable {
195
243
/// around elements equal to the given element.
196
244
///
197
245
/// The resulting lazy sequence consists of at most `maxSplits + 1` subsequences.
198
- /// Elements that are used to split the collection are not returned as part
199
- /// of any subsequence.
246
+ /// Elements that are used to split the collection are not returned as part of any
247
+ /// subsequence (except possibly the last one, in the case where `maxSplits` is
248
+ /// less than the number of separators in the collection).
200
249
///
201
250
/// The following examples show the effects of the `maxSplits` and
202
251
/// `omittingEmptySubsequences` parameters when splitting a string at each
@@ -272,7 +321,7 @@ extension LazyCollection where Element: Equatable {
272
321
273
322
return LazySplitCollection (
274
323
base: self ,
275
- whereSeparator : { $0 == separator } ,
324
+ isSeparator : { $0 == separator } ,
276
325
maxSplits: maxSplits,
277
326
omittingEmptySubsequences: omittingEmptySubsequences
278
327
)
0 commit comments