diff --git a/stdlib/private/StdlibCollectionUnittest/CheckSequenceType.swift b/stdlib/private/StdlibCollectionUnittest/CheckSequenceType.swift index 3d58596c7db13..ef8b92439caff 100644 --- a/stdlib/private/StdlibCollectionUnittest/CheckSequenceType.swift +++ b/stdlib/private/StdlibCollectionUnittest/CheckSequenceType.swift @@ -17,6 +17,18 @@ internal enum TestError : Error { case error2 } +public struct AdjacentPairsTest { + public let expected: [(Int, Int)] + public let sequence: [Int] + public let loc: SourceLoc + + public init(expected: [(Int, Int)], sequence: [Int], file: String = #file, line: UInt = #line) { + self.expected = expected + self.sequence = sequence + self.loc = SourceLoc(file, line, comment: "adjacentPairs() test data") + } +} + public struct DropFirstTest { public var sequence: [Int] public let dropElements: Int @@ -438,6 +450,7 @@ public struct StartsWithTest { self.loc = SourceLoc(file, line, comment: "test data") } } + public struct ZipTest { public let expected: [(Int, Int32)] public let sequence: [Int] @@ -464,6 +477,14 @@ public struct ZipTest { } +public let adjacentPairsTests: [AdjacentPairsTest] = [ + AdjacentPairsTest(expected: [], sequence: []), + AdjacentPairsTest(expected: [], sequence: [0]), + AdjacentPairsTest(expected: [(1, 2)], sequence: [1, 2]), + AdjacentPairsTest(expected: [(1, 2), (2, 3)], sequence: [1, 2, 3]), + AdjacentPairsTest(expected: [(1, 2), (2, 3), (3, 4)], sequence: [1, 2, 3, 4]) +] + public let elementsEqualTests: [ElementsEqualTest] = [ ElementsEqualTest(true, [], [], [], []), diff --git a/stdlib/public/core/AdjacentPairs.swift b/stdlib/public/core/AdjacentPairs.swift new file mode 100644 index 0000000000000..d9f07c2a95c09 --- /dev/null +++ b/stdlib/public/core/AdjacentPairs.swift @@ -0,0 +1,115 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +/// A sequence of adjacent pairs of elements built from an underlying sequence. +/// +/// In an `AdjacentPairsSequence`, the elements of the *i*th pair are the *i*th +/// and *(i+1)*th elements of the underlying sequence. The following example +/// uses the `adjacentPairs()` method to iterate over adjacent pairs of integers: +/// +/// for pair in (1...5).adjacentPairs() { +/// print(pair) +/// } +/// // Prints "(1, 2)" +/// // Prints "(2, 3)" +/// // Prints "(3, 4)" +/// // Prints "(4, 5)" +@_fixed_layout +public struct AdjacentPairsSequence { + @usableFromInline + internal let _base: Base + + /// Creates an instance that makes pairs of adjacent elements from `base`. + @inlinable + public init(_base: Base) { + self._base = _base + } +} + +extension AdjacentPairsSequence { + /// An iterator for `AdjacentPairsSequence`. + @_fixed_layout + public struct Iterator { + @usableFromInline + internal var _base: Base.Iterator + + @usableFromInline + internal var _previousElement: Base.Element? + + /// Creates an instance around an underlying iterator. + @inlinable + internal init(_base: Base.Iterator) { + self._base = _base + self._previousElement = self._base.next() + } + } +} + +extension AdjacentPairsSequence.Iterator: IteratorProtocol { + /// The type of element returned by `next()`. + public typealias Element = (Base.Element, Base.Element) + + /// Advances to the next element and returns it, or `nil` if no next element + /// exists. + /// + /// Once `nil` has been returned, all subsequent calls return `nil`. + @inlinable + public mutating func next() -> Element? { + guard let previous = _previousElement, let next = _base.next() else { + return nil + } + _previousElement = next + return (previous, next) + } +} + +extension AdjacentPairsSequence: Sequence { + /// Returns an iterator over the elements of this sequence. + @inlinable + public func makeIterator() -> Iterator { + return Iterator(_base: _base.makeIterator()) + } + + /// A value less than or equal to the number of elements in the sequence, + /// calculated nondestructively. + /// + /// The default implementation returns 0. If you provide your own + /// implementation, make sure to compute the value nondestructively. + /// + /// - Complexity: O(1), except if the sequence also conforms to `Collection`. + /// In this case, see the documentation of `Collection.underestimatedCount`. + @inlinable + public var underestimatedCount: Int { + return Swift.max(0, _base.underestimatedCount - 1) + } +} + +extension Sequence { + /// Creates a sequence of adjacent pairs of elements from this sequence. + /// + /// In the `AdjacentPairsSequence` instance returned by this method, the elements of + /// the *i*th pair are the *i*th and *(i+1)*th elements of the underlying sequence. + /// The following example uses the `adjacentPairs()` method to iterate over adjacent + /// pairs of integers: + /// + /// for pair in (1...5).adjacentPairs() { + /// print(pair) + /// } + /// // Prints "(1, 2)" + /// // Prints "(2, 3)" + /// // Prints "(3, 4)" + /// // Prints "(4, 5)" + @inlinable + public func adjacentPairs() -> AdjacentPairsSequence { + return AdjacentPairsSequence(_base: self) + } +} diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index bc190531e8ca3..94e149c63f323 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -18,6 +18,7 @@ set(SWIFTLIB_ESSENTIAL ### PLEASE KEEP THIS LIST IN ALPHABETICAL ORDER ### # Some files can't be sorted alphabetically, see notes in the list below. + AdjacentPairs.swift Algorithm.swift ArrayBody.swift ArrayBuffer.swift diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index 306c1986b8a3b..15913bfa5433f 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -81,6 +81,7 @@ "ExistentialCollection.swift" ], "Lazy Views": [ + "AdjacentPairs.swift", "Algorithm.swift", "Flatten.swift", "FlatMap.swift", diff --git a/validation-test/stdlib/SequenceType.swift.gyb b/validation-test/stdlib/SequenceType.swift.gyb index 5e5cd675a95d1..fdd12db1ef193 100644 --- a/validation-test/stdlib/SequenceType.swift.gyb +++ b/validation-test/stdlib/SequenceType.swift.gyb @@ -1031,6 +1031,27 @@ SequenceTypeTests.test("Sequence/split/dispatch") { expectCustomizable(tester, tester.log.split) } +//===----------------------------------------------------------------------===// +// adjacentPairs() +//===----------------------------------------------------------------------===// + +SequenceTypeTests.test("adjacentPairs") { + typealias Element = (OpaqueValue, OpaqueValue) + func compareElements(_ lhs: Element, rhs: Element) -> Bool { + return lhs.0.value == rhs.0.value && lhs.1.value == rhs.1.value + } + + for test in adjacentPairsTests { + let s = MinimalSequence>( + elements: test.sequence.map(OpaqueValue.init)) + checkSequence( + test.expected.map { (OpaqueValue($0), OpaqueValue($1)) }, + s.adjacentPairs(), + stackTrace: SourceLocStack().with(test.loc), + sameValue: compareElements) + } +} + //===----------------------------------------------------------------------===// // zip() //===----------------------------------------------------------------------===//