Skip to content

[SR-1516][stdlib] Implement SE-0045 prefix(while:) and drop(while:) #3600

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,9 @@ internal func _product<C1 : Collection, C2 : Collection>(
return '''
C : %(protocol)s,
CollectionWithEquatableElement : %(protocol)s,
CollectionWithEquatableElement.SubSequence : Collection,
CollectionWithEquatableElement.SubSequence.Iterator.Element
== CollectionWithEquatableElement.Iterator.Element,
C.SubSequence : %(protocol)s,
C.SubSequence.Iterator.Element == C.Iterator.Element,
C.SubSequence.Index == C.Index,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ extension TestSuite {
C.Indices.Index == C.Index,
C.Indices.SubSequence == C.Indices,
CollectionWithEquatableElement.Iterator.Element : Equatable,
CollectionWithEquatableElement.SubSequence : Collection,
CollectionWithEquatableElement.SubSequence.Iterator.Element
== CollectionWithEquatableElement.Iterator.Element,
CollectionWithComparableElement.Iterator.Element : Comparable {

var testNamePrefix = testNamePrefix
Expand Down Expand Up @@ -705,6 +708,9 @@ self.test("\(testNamePrefix).partition/InvalidOrderings") {
C.Indices.Index == C.Index,
C.Indices.SubSequence == C.Indices,
CollectionWithEquatableElement.Iterator.Element : Equatable,
CollectionWithEquatableElement.SubSequence : Collection,
CollectionWithEquatableElement.SubSequence.Iterator.Element
== CollectionWithEquatableElement.Iterator.Element,
CollectionWithComparableElement.Iterator.Element : Comparable {

var testNamePrefix = testNamePrefix
Expand Down Expand Up @@ -858,6 +864,9 @@ self.test("\(testNamePrefix).partition/DispatchesThrough_withUnsafeMutableBuffer
C.Indices.Index == C.Index,
C.Indices.SubSequence == C.Indices,
CollectionWithEquatableElement.Iterator.Element : Equatable,
CollectionWithEquatableElement.SubSequence : Collection,
CollectionWithEquatableElement.SubSequence.Iterator.Element
== CollectionWithEquatableElement.Iterator.Element,
CollectionWithComparableElement.Iterator.Element : Comparable,
CollectionWithComparableElement.SubSequence.Indices.Iterator.Element == CollectionWithComparableElement.Index {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,10 @@ extension TestSuite {
C.Indices.Iterator.Element == C.Index,
C.Indices.Index == C.Index,
C.Indices.SubSequence == C.Indices,
CollectionWithEquatableElement.Iterator.Element : Equatable {
CollectionWithEquatableElement.Iterator.Element : Equatable,
CollectionWithEquatableElement.SubSequence : Collection,
CollectionWithEquatableElement.SubSequence.Iterator.Element
== CollectionWithEquatableElement.Iterator.Element {

var testNamePrefix = testNamePrefix

Expand Down Expand Up @@ -1182,7 +1185,10 @@ self.test("\(testNamePrefix).OperatorPlus") {
C.Indices.Iterator.Element == C.Index,
C.Indices.Index == C.Index,
C.Indices.SubSequence == C.Indices,
CollectionWithEquatableElement.Iterator.Element : Equatable {
CollectionWithEquatableElement.Iterator.Element : Equatable,
CollectionWithEquatableElement.SubSequence : Collection,
CollectionWithEquatableElement.SubSequence.Iterator.Element
== CollectionWithEquatableElement.Iterator.Element {

var testNamePrefix = testNamePrefix

Expand Down Expand Up @@ -1311,7 +1317,10 @@ self.test("\(testNamePrefix).removeLast(n: Int)/whereIndexIsBidirectional/remove
C.Indices.Iterator.Element == C.Index,
C.Indices.Index == C.Index,
C.Indices.SubSequence == C.Indices,
CollectionWithEquatableElement.Iterator.Element : Equatable {
CollectionWithEquatableElement.Iterator.Element : Equatable,
CollectionWithEquatableElement.SubSequence : Collection,
CollectionWithEquatableElement.SubSequence.Iterator.Element
== CollectionWithEquatableElement.Iterator.Element {

var testNamePrefix = testNamePrefix

Expand Down
41 changes: 41 additions & 0 deletions stdlib/private/StdlibCollectionUnittest/CheckSequenceType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1463,6 +1463,9 @@ extension TestSuite {
resiliencyChecks: CollectionMisuseResiliencyChecks = .all
) where
SequenceWithEquatableElement.Iterator.Element : Equatable,
SequenceWithEquatableElement.SubSequence : Sequence,
SequenceWithEquatableElement.SubSequence.Iterator.Element
== SequenceWithEquatableElement.Iterator.Element,
S.SubSequence : Sequence,
S.SubSequence.Iterator.Element == S.Iterator.Element,
S.SubSequence.SubSequence == S.SubSequence {
Expand Down Expand Up @@ -1615,6 +1618,25 @@ self.test("\(testNamePrefix).dropLast/semantics/negative") {
_ = s.dropLast(-1)
}

//===----------------------------------------------------------------------===//
// drop(while:)
//===----------------------------------------------------------------------===//

self.test("\(testNamePrefix).drop(while:)/semantics").forEach(in: findTests) {
test in
let s = makeWrappedSequenceWithEquatableElement(test.sequence)
let closureLifetimeTracker = LifetimeTracked(0)
let remainingSequence = s.drop {
_blackHole(closureLifetimeTracker)
return $0 != wrapValueIntoEquatable(test.element)
}
let remaining = Array(remainingSequence)
let expectedSuffix = test.sequence.suffix(
from: test.expected ?? test.sequence.endIndex)
expectEqual(expectedSuffix.count, remaining.count)
expectEqualSequence(expectedSuffix.map(wrapValueIntoEquatable), remaining)
}

//===----------------------------------------------------------------------===//
// prefix()
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1654,6 +1676,25 @@ self.test("\(testNamePrefix).prefix/semantics/negative") {
_ = s.prefix(-1)
}

//===----------------------------------------------------------------------===//
// prefix(while:)
//===----------------------------------------------------------------------===//

self.test("\(testNamePrefix).prefix(while:)/semantics").forEach(in: findTests) {
test in
let s = makeWrappedSequenceWithEquatableElement(test.sequence)
let closureLifetimeTracker = LifetimeTracked(0)
let remainingSequence = s.prefix {
_blackHole(closureLifetimeTracker)
return $0 != wrapValueIntoEquatable(test.element)
}
let expectedPrefix = test.sequence.prefix(
upTo: test.expected ?? test.sequence.endIndex)
let remaining = Array(remainingSequence)
expectEqual(expectedPrefix.count, remaining.count)
expectEqualSequence(expectedPrefix.map(wrapValueIntoEquatable), remaining)
}

//===----------------------------------------------------------------------===//
// suffix()
//===----------------------------------------------------------------------===//
Expand Down
16 changes: 16 additions & 0 deletions stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ public class SequenceLog {
public static var first = TypeIndexed(0)
public static var dropFirst = TypeIndexed(0)
public static var dropLast = TypeIndexed(0)
public static var dropWhile = TypeIndexed(0)
public static var prefixWhile = TypeIndexed(0)
public static var prefixMaxLength = TypeIndexed(0)
public static var suffixMaxLength = TypeIndexed(0)
public static var split = TypeIndexed(0)
Expand Down Expand Up @@ -262,11 +264,25 @@ public struct ${Self}<
Log.dropLast[selfType] += 1
return base.dropLast(n)
}

public func drop(
while predicate: @noescape (Base.Iterator.Element) throws -> Bool
) rethrows -> SubSequence {
Log.dropWhile[selfType] += 1
return try base.drop(while: predicate)
}

public func prefix(_ maxLength: Int) -> SubSequence {
Log.prefixMaxLength[selfType] += 1
return base.prefix(maxLength)
}

public func prefix(
while predicate: @noescape (Base.Iterator.Element) throws -> Bool
) rethrows -> SubSequence {
Log.prefixWhile[selfType] += 1
return try base.prefix(while: predicate)
}

public func suffix(_ maxLength: Int) -> SubSequence {
Log.suffixMaxLength[selfType] += 1
Expand Down
2 changes: 2 additions & 0 deletions stdlib/public/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ set(SWIFTLIB_ESSENTIAL
CString.swift
CTypes.swift
DebuggerSupport.swift
DropWhile.swift.gyb
EmptyCollection.swift
Equatable.swift
ErrorType.swift
Expand Down Expand Up @@ -88,6 +89,7 @@ set(SWIFTLIB_ESSENTIAL
OutputStream.swift
Pointer.swift
Policy.swift
PrefixWhile.swift.gyb
Print.swift
RandomAccessCollection.swift
Range.swift.gyb
Expand Down
38 changes: 38 additions & 0 deletions stdlib/public/core/Collection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1319,6 +1319,25 @@ extension Collection {
offsetBy: numericCast(amount), limitedBy: endIndex) ?? endIndex
return self[startIndex..<end]
}

/// Returns a subsequence by skipping elements while `predicate` returns
/// `true` and returning the remaining elements.
///
/// - Parameter predicate: A closure that takes an element of the
/// sequence as its argument and returns `true` if the element should
/// be skipped or `false` if it should be included. Once the predicate
/// returns `false` it will not be called again.
Copy link
Contributor

@gribozavr gribozavr Jul 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add algorithmic complexity documentation in every doc comment. (O(N))

///
/// - Complexity: O(*n*), where *n* is the length of the collection.
public func drop(
while predicate: @noescape (Iterator.Element) throws -> Bool
) rethrows -> SubSequence {
var start = startIndex
while try start != endIndex && predicate(self[start]) {
formIndex(after: &start)
}
return self[start..<endIndex]
}

/// Returns a subsequence, up to the specified maximum length, containing
/// the initial elements of the collection.
Expand All @@ -1344,6 +1363,25 @@ extension Collection {
offsetBy: numericCast(maxLength), limitedBy: endIndex) ?? endIndex
return self[startIndex..<end]
}

/// Returns a subsequence containing the initial elements until `predicate`
/// returns `false` and skipping the remaining elements.
///
/// - Parameter predicate: A closure that takes an element of the
/// sequence as its argument and returns `true` if the element should
/// be included or `false` if it should be excluded. Once the predicate
/// returns `false` it will not be called again.
///
/// - Complexity: O(*n*), where *n* is the length of the collection.
public func prefix(
while predicate: @noescape (Iterator.Element) throws -> Bool
) rethrows -> SubSequence {
var end = startIndex
while try end != endIndex && predicate(self[end]) {
formIndex(after: &end)
}
return self[startIndex..<end]
}

/// Returns a subsequence, up to the given maximum length, containing the
/// final elements of the collection.
Expand Down
Loading