Skip to content

Commit e8dcdc3

Browse files
Russ Bishoprussbishop
authored andcommitted
SE-0045 non-lazy implementation and tests
1 parent b61c68d commit e8dcdc3

File tree

5 files changed

+174
-0
lines changed

5 files changed

+174
-0
lines changed

stdlib/private/StdlibCollectionUnittest/CheckSequenceType.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,6 +1614,24 @@ self.test("\(testNamePrefix).dropLast/semantics/negative") {
16141614
_ = s.dropLast(-1)
16151615
}
16161616

1617+
//===----------------------------------------------------------------------===//
1618+
// drop(while:)
1619+
//===----------------------------------------------------------------------===//
1620+
1621+
self.test("\(testNamePrefix).drop(while:)/semantics") {
1622+
for test in findTests {
1623+
let s = makeWrappedSequenceWithEquatableElement(test.sequence)
1624+
let closureLifetimeTracker = LifetimeTracked(0)
1625+
let remainingSequence = s.drop {
1626+
_blackHole(closureLifetimeTracker)
1627+
return $0 == wrapValueIntoEquatable(test.element)
1628+
}
1629+
let remaining = Array(remainingSequence)
1630+
expectEqual(test.expectedLeftoverSequence.count, remaining.count)
1631+
expectEqualSequence(test.expectedLeftoverSequence, remaining)
1632+
}
1633+
}
1634+
16171635
//===----------------------------------------------------------------------===//
16181636
// prefix()
16191637
//===----------------------------------------------------------------------===//
@@ -1653,6 +1671,25 @@ self.test("\(testNamePrefix).prefix/semantics/negative") {
16531671
_ = s.prefix(-1)
16541672
}
16551673

1674+
//===----------------------------------------------------------------------===//
1675+
// prefix(while:)
1676+
//===----------------------------------------------------------------------===//
1677+
1678+
self.test("\(testNamePrefix).prefix(while:)/semantics") {
1679+
for test in findTests {
1680+
let s = makeWrappedSequenceWithEquatableElement(test.sequence)
1681+
let closureLifetimeTracker = LifetimeTracked(0)
1682+
let remainingSequence = s.prefix {
1683+
_blackHole(closureLifetimeTracker)
1684+
return $0 == wrapValueIntoEquatable(test.element)
1685+
}
1686+
let expectedPrefix = test.sequence.dropLast(expectedLeftoverSequence.count)
1687+
let remaining = Array(remainingSequence)
1688+
expectEqual(expectedPrefix.count, remaining.count)
1689+
expectEqualSequence(expectedPrefix, remaining)
1690+
}
1691+
}
1692+
16561693
//===----------------------------------------------------------------------===//
16571694
// suffix()
16581695
//===----------------------------------------------------------------------===//

stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ public class SequenceLog {
8484
public static var first = TypeIndexed(0)
8585
public static var dropFirst = TypeIndexed(0)
8686
public static var dropLast = TypeIndexed(0)
87+
public static var dropWhile = TypeIndexed(0)
88+
public static var prefixWhile = TypeIndexed(0)
8789
public static var prefixMaxLength = TypeIndexed(0)
8890
public static var suffixMaxLength = TypeIndexed(0)
8991
public static var split = TypeIndexed(0)
@@ -260,11 +262,25 @@ public struct ${Self}<
260262
Log.dropLast[selfType] += 1
261263
return base.dropLast(n)
262264
}
265+
266+
public func drop(
267+
while predicate: @noescape (Base.Iterator.Element) throws -> Bool
268+
) rethrows -> SubSequence {
269+
Log.dropWhile[selfType] += 1
270+
return base.drop(while: predicate)
271+
}
263272

264273
public func prefix(_ maxLength: Int) -> SubSequence {
265274
Log.prefixMaxLength[selfType] += 1
266275
return base.prefix(maxLength)
267276
}
277+
278+
public func prefix(
279+
while predicate: @noescape (Base.Iterator.Element) throws -> Bool
280+
) rethrows -> SubSequence {
281+
Log.prefixWhile[selfType] += 1
282+
return base.prefix(while: predicate)
283+
}
268284

269285
public func suffix(_ maxLength: Int) -> SubSequence {
270286
Log.suffixMaxLength[selfType] += 1

stdlib/public/core/Collection.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,23 @@ extension Collection {
13381338
offsetBy: numericCast(amount), limitedBy: endIndex) ?? endIndex
13391339
return self[startIndex..<end]
13401340
}
1341+
1342+
/// Returns a subsequence by skipping elements while `predicate` returns
1343+
/// `true` and returning the remaining elements.
1344+
///
1345+
/// - Parameter while: A closure that takes an element of the
1346+
/// sequence as its argument and returns `true` if the element should
1347+
/// be skipped or `false` if it should be included. Once the predicate
1348+
/// returns `false` it will not be called again.
1349+
public func drop(
1350+
while predicate: @noescape (Iterator.Element) throws -> Bool
1351+
) rethrows -> SubSequence {
1352+
var start = startIndex
1353+
while try start < endIndex && predicate(self[start]) {
1354+
formIndex(after: &start)
1355+
}
1356+
return self[start..<endIndex]
1357+
}
13411358

13421359
/// Returns a subsequence, up to the specified maximum length, containing
13431360
/// the initial elements of the collection.
@@ -1363,6 +1380,23 @@ extension Collection {
13631380
offsetBy: numericCast(maxLength), limitedBy: endIndex) ?? endIndex
13641381
return self[startIndex..<end]
13651382
}
1383+
1384+
/// Returns a subsequence containing the initial elements until `predicate`
1385+
/// returns `false` and skipping the remaining elements.
1386+
///
1387+
/// - Parameter while: A closure that takes an element of the
1388+
/// sequence as its argument and returns `true` if the element should
1389+
/// be included or `false` if it should be excluded. Once the predicate
1390+
/// returns `false` it will not be called again.
1391+
public func prefix(
1392+
while predicate: @noescape (Iterator.Element) throws -> Bool
1393+
) rethrows -> SubSequence {
1394+
var end = startIndex
1395+
while try end < endIndex && predicate(self[end]) {
1396+
formIndex(after: &end)
1397+
}
1398+
return self[startIndex..<end]
1399+
}
13661400

13671401
/// Returns a subsequence, up to the given maximum length, containing the
13681402
/// final elements of the collection.

stdlib/public/core/Sequence.swift

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,17 @@ public protocol Sequence {
470470
///
471471
/// - Complexity: O(*n*), where *n* is the length of the sequence.
472472
func dropLast(_ n: Int) -> SubSequence
473+
474+
475+
/// Returns a subsequence by skipping elements while `predicate` returns
476+
/// `true` and returning the remaining elements.
477+
///
478+
/// - Parameter while: A closure that takes an element of the
479+
/// sequence as its argument and returns a Boolean value indicating
480+
/// whether the element is a match.
481+
func drop(
482+
while predicate: @noescape (Iterator.Element) throws -> Bool
483+
) rethrows -> SubSequence
473484

474485
/// Returns a subsequence, up to the specified maximum length, containing
475486
/// the initial elements of the sequence.
@@ -488,6 +499,16 @@ public protocol Sequence {
488499
/// - Returns: A subsequence starting at the beginning of this sequence
489500
/// with at most `maxLength` elements.
490501
func prefix(_ maxLength: Int) -> SubSequence
502+
503+
/// Returns a subsequence containing the initial elements until `predicate`
504+
/// returns `false` and skipping the remaining elements.
505+
///
506+
/// - Parameter while: A closure that takes an element of the
507+
/// sequence as its argument and returns a Boolean value indicating
508+
/// whether the element is a match.
509+
func prefix(
510+
while predicate: @noescape (Iterator.Element) throws -> Bool
511+
) rethrows -> SubSequence
491512

492513
/// Returns a subsequence, up to the given maximum length, containing the
493514
/// final elements of the sequence.
@@ -1126,6 +1147,31 @@ extension Sequence where
11261147
}
11271148
return AnySequence(result)
11281149
}
1150+
1151+
/// Returns a subsequence by skipping elements while `predicate` returns
1152+
/// `true` and returning the remaining elements.
1153+
///
1154+
/// - Parameter while: A closure that takes an element of the
1155+
/// sequence as its argument and returns `true` if the element should
1156+
/// be skipped or `false` if it should be included. Once the predicate
1157+
/// returns `false` it will not be called again.
1158+
public func drop(
1159+
while predicate: @noescape (Iterator.Element) throws -> Bool
1160+
) rethrows -> AnySequence<Iterator.Element> {
1161+
var iterator = makeIterator()
1162+
var nextElement: Iterator.Element? = iterator.next()
1163+
repeat {
1164+
nextElement = iterator.next()
1165+
} while try nextElement.flatMap(predicate) == true
1166+
1167+
return AnySequence {
1168+
AnyIterator {
1169+
guard let next = nextElement else { return nil }
1170+
nextElement = iterator.next()
1171+
return next
1172+
}
1173+
}
1174+
}
11291175

11301176
/// Returns a subsequence, up to the specified maximum length, containing the
11311177
/// initial elements of the sequence.
@@ -1153,6 +1199,27 @@ extension Sequence where
11531199
return AnySequence(
11541200
_PrefixSequence(_iterator: makeIterator(), maxLength: maxLength))
11551201
}
1202+
1203+
/// Returns a subsequence containing the initial elements until `predicate`
1204+
/// returns `false` and skipping the remaining elements.
1205+
///
1206+
/// - Parameter while: A closure that takes an element of the
1207+
/// sequence as its argument and returns `true` if the element should
1208+
/// be included or `false` if it should be excluded. Once the predicate
1209+
/// returns `false` it will not be called again.
1210+
public func prefix(
1211+
while predicate: @noescape (Iterator.Element) throws -> Bool
1212+
) rethrows -> AnySequence<Iterator.Element> {
1213+
var result: [Iterator.Element] = []
1214+
1215+
for element in self {
1216+
guard try predicate(element) else {
1217+
break
1218+
}
1219+
result.append(element)
1220+
}
1221+
return AnySequence(result)
1222+
}
11561223
}
11571224

11581225
extension Sequence {

validation-test/stdlib/SequenceType.swift.gyb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,16 @@ SequenceTypeTests.test("dropLast/dispatch") {
971971
expectCustomizable(tester, tester.log.dropLast)
972972
}
973973

974+
//===----------------------------------------------------------------------===//
975+
// drop(while:)
976+
//===----------------------------------------------------------------------===//
977+
978+
SequenceTypeTests.test("drop(while:)/dispatch") {
979+
var tester = SequenceLog.dispatchTester([OpaqueValue(1)])
980+
tester.drop { _ in return false }
981+
expectCustomizable(tester, tester.log.dropWhile)
982+
}
983+
974984
//===----------------------------------------------------------------------===//
975985
// prefix()
976986
//===----------------------------------------------------------------------===//
@@ -981,6 +991,16 @@ SequenceTypeTests.test("prefix/dispatch") {
981991
expectCustomizable(tester, tester.log.prefixMaxLength)
982992
}
983993

994+
//===----------------------------------------------------------------------===//
995+
// prefix(while:)
996+
//===----------------------------------------------------------------------===//
997+
998+
SequenceTypeTests.test("prefix(while:)/dispatch") {
999+
var tester = SequenceLog.dispatchTester([OpaqueValue(1)])
1000+
tester.prefix { _ in return false }
1001+
expectCustomizable(tester, tester.log.prefixWhile)
1002+
}
1003+
9841004
//===----------------------------------------------------------------------===//
9851005
// suffix()
9861006
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)