From 1632b5dd5862bb2d377be79d5063d2161a7559e5 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Fri, 17 Aug 2018 13:18:03 -0400 Subject: [PATCH] Add Zip (#23) --- Overture.xcodeproj/project.pbxproj | 12 + README.md | 80 ++- Sources/Overture/Optional.swift | 20 + Sources/Overture/ZipOptional.swift | 155 +++++ Sources/Overture/ZipSequence.swift | 987 +++++++++++++++++++++++++++++ Tests/OvertureTests/ZipTests.swift | 70 ++ 6 files changed, 1321 insertions(+), 3 deletions(-) create mode 100644 Sources/Overture/ZipOptional.swift create mode 100644 Sources/Overture/ZipSequence.swift create mode 100644 Tests/OvertureTests/ZipTests.swift diff --git a/Overture.xcodeproj/project.pbxproj b/Overture.xcodeproj/project.pbxproj index f937614..6edd602 100644 --- a/Overture.xcodeproj/project.pbxproj +++ b/Overture.xcodeproj/project.pbxproj @@ -21,6 +21,9 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + DC5E715C21065C6900ED239B /* ZipOptional.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC5E715B21065C6900ED239B /* ZipOptional.swift */; }; + DCF8237021267FD4000A7F94 /* ZipSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF8236F21267FD4000A7F94 /* ZipSequence.swift */; }; + DCF82372212684BE000A7F94 /* ZipTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF82371212684BE000A7F94 /* ZipTests.swift */; }; OBJ_44 /* Chain.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* Chain.swift */; }; OBJ_45 /* Compose.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Compose.swift */; }; OBJ_46 /* Concat.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Concat.swift */; }; @@ -64,6 +67,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + DC5E715B21065C6900ED239B /* ZipOptional.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZipOptional.swift; sourceTree = ""; }; + DCF8236F21267FD4000A7F94 /* ZipSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZipSequence.swift; sourceTree = ""; }; + DCF82371212684BE000A7F94 /* ZipTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZipTests.swift; sourceTree = ""; }; OBJ_11 /* Chain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Chain.swift; sourceTree = ""; }; OBJ_12 /* Compose.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Compose.swift; sourceTree = ""; }; OBJ_13 /* Concat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Concat.swift; sourceTree = ""; }; @@ -126,6 +132,8 @@ OBJ_21 /* Setters.swift */, OBJ_22 /* Uncurry.swift */, OBJ_23 /* With.swift */, + DC5E715B21065C6900ED239B /* ZipOptional.swift */, + DCF8236F21267FD4000A7F94 /* ZipSequence.swift */, OBJ_24 /* Zurry.swift */, ); name = Overture; @@ -151,6 +159,7 @@ OBJ_32 /* PipeTests.swift */, OBJ_33 /* UncurryTests.swift */, OBJ_34 /* WithTests.swift */, + DCF82371212684BE000A7F94 /* ZipTests.swift */, ); name = OvertureTests; path = Tests/OvertureTests; @@ -277,6 +286,7 @@ buildActionMask = 0; files = ( OBJ_44 /* Chain.swift in Sources */, + DCF8237021267FD4000A7F94 /* ZipSequence.swift in Sources */, OBJ_45 /* Compose.swift in Sources */, OBJ_46 /* Concat.swift in Sources */, OBJ_47 /* Curry.swift in Sources */, @@ -287,6 +297,7 @@ OBJ_53 /* Sequence.swift in Sources */, OBJ_54 /* Setters.swift in Sources */, OBJ_55 /* Uncurry.swift in Sources */, + DC5E715C21065C6900ED239B /* ZipOptional.swift in Sources */, OBJ_56 /* With.swift in Sources */, OBJ_57 /* Zurry.swift in Sources */, ); @@ -309,6 +320,7 @@ OBJ_77 /* ConcatTests.swift in Sources */, OBJ_78 /* CurryTests.swift in Sources */, OBJ_79 /* FlipTests.swift in Sources */, + DCF82372212684BE000A7F94 /* ZipTests.swift in Sources */, OBJ_80 /* PipeTests.swift in Sources */, OBJ_81 /* UncurryTests.swift in Sources */, OBJ_82 /* WithTests.swift in Sources */, diff --git a/README.md b/README.md index 978ab77..b78f7f6 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ A library for function composition. - [`prop`](#prop) - [`over` and `set`](#over-and-set) - [`mprop`, `mver`, and `mut`](#mprop-mver-and-mut) + - [`zip`](#zip) - [FAQ](#faq) - [Installation](#installation) - [🎶 Prelude](#-prelude) @@ -309,6 +310,79 @@ let request = with(URLRequest(url: url), concat( )) ``` +### `zip` and `zip(with:)` + +This is a function that Swift ships with! Unfortunately, it's limited to pairs of sequences. Overture defines `zip` to work with up to ten sequences at once, which makes combining several sets of related data a snap. + +```swift +let ids = [1, 2, 3] +let emails = ["blob@pointfree.co", "blob.jr@pointfree.co", "blob.sr@pointfree.co"] +let names = ["Blob", "Blob Junior", "Blob Senior"] + +zip(ids, emails, names) +// [ +// (1, "blob@pointfree.co", "Blob"), +// (2, "blob.jr@pointfree.co", "Blob Junior"), +// (3, "blob.sr@pointfree.co", "Blob Senior") +// ] +``` + +It's common to immediately `map` on zipped values. + +``` swift +struct User { + let id: Int + let email: String + let name: String +} + +zip(ids, emails, names).map(User.init) +// [ +// User(id: 1, email: "blob@pointfree.co", name: "Blob"), +// User(id: 2, email: "blob.jr@pointfree.co", name: "Blob Junior"), +// User(id: 3, email: "blob.sr@pointfree.co", name: "Blob Senior") +// ] +``` + +Because of this, Overture provides a `zip(with:)` helper, which takes a tranform function up front and is curried, so it can be composed with other functions using `pipe`. + +``` swift +zip(with: User.init)(ids, emails, names) +``` + +Overture also extends the notion of `zip` to work with optionals! It's an expressive way of combining multiple optionals together. + +``` swift +let optionalId: Int? = 1 +let optionalEmail: String? = "blob@pointfree.co" +let optionalName: String? = "Blob" + +zip(optionalId, optionalEmail, optionalName) +// Optional<(Int, String, String)>.some((1, "blob@pointfree.co", "Blob")) +``` + +And `zip(with:)` lets us transform these tuples into other values. + +``` swift +zip(with: User.init)(optionalId, optionalEmail, optionalName) +// Optional.some(User(id: 1, email: "blob@pointfree.co", name: "Blob")) +``` + +Using `zip` can be an expressive alternative to `let`-unwrapping! + +``` swift +let optionalUser = zip(with: User.init)(optionalId, optionalEmail, optionalName) + +// vs. + +let optionalUser: User? +if let id = optionalId, let email = optionalEmail, let name = optionalName { + optionalUser = User(id: id, email: email, name: name) +} else { + optionalUser = nil +} +``` + ## FAQ - **Should I be worried about polluting the global namespace with free functions?** @@ -336,7 +410,7 @@ let request = with(URLRequest(url: url), concat( If you use [Carthage](https://github.com/Carthage/Carthage), you can add the following dependency to your `Cartfile`: ``` ruby -github "pointfreeco/swift-overture" ~> 0.2 +github "pointfreeco/swift-overture" ~> 0.3 ``` ### CocoaPods @@ -344,7 +418,7 @@ github "pointfreeco/swift-overture" ~> 0.2 If your project uses [CocoaPods](https://cocoapods.org), just add the following to your `Podfile`: ``` ruby -pod 'Overture', '~> 0.2' +pod 'Overture', '~> 0.3' ``` ### SwiftPM @@ -353,7 +427,7 @@ If you want to use Overture in a project that uses [SwiftPM](https://swift.org/p ``` swift dependencies: [ - .package(url: "https://github.com/pointfreeco/swift-overture.git", from: "0.2.0") + .package(url: "https://github.com/pointfreeco/swift-overture.git", from: "0.3.0") ] ``` diff --git a/Sources/Overture/Optional.swift b/Sources/Overture/Optional.swift index 8a6cab4..88830be 100644 --- a/Sources/Overture/Optional.swift +++ b/Sources/Overture/Optional.swift @@ -22,3 +22,23 @@ public func map( return { try $0.map(transform) } } + + +/// Transforms a pair of optionals into an optional pair. +/// +/// - Parameters: +/// - a: An optional value. +/// - b: Another optional value. +/// - Returns: An optional pair of values. +public func zip(_ a: A?, _ b: B?) -> (A, B)? { + guard let a = a, let b = b else { return nil } + return (a, b) +} + +/// Transforms a pair of optionals into a new optional value. +/// +/// - Parameter transform: A transform function. +/// - Returns: A transformed optional value. +public func zip(with transform: @escaping (A, B) -> C) -> (A?, B?) -> C? { + return { zip($0, $1).map(transform) } +} diff --git a/Sources/Overture/ZipOptional.swift b/Sources/Overture/ZipOptional.swift new file mode 100644 index 0000000..b3a6de9 --- /dev/null +++ b/Sources/Overture/ZipOptional.swift @@ -0,0 +1,155 @@ +public func zip( + _ a: A?, + _ b: B?, + _ c: C? + ) + -> (A, B, C)? { + return zip(zip(a, b), c).map { ($0.0, $0.1, $1) } +} + +public func zip( + with transform: @escaping (A, B, C) -> D + ) + -> (A?, B?, C?) -> D? { + return { zip($0, $1, $2).map(transform) } +} + +public func zip( + _ a: A?, + _ b: B?, + _ c: C?, + _ d: D? + ) + -> (A, B, C, D)? { + return zip(zip(a, b), c, d).map { ($0.0, $0.1, $1, $2) } +} + +public func zip( + with transform: @escaping (A, B, C, D) -> E + ) + -> (A?, B?, C?, D?) -> E? { + return { zip($0, $1, $2, $3).map(transform) } +} + +public func zip( + _ a: A?, + _ b: B?, + _ c: C?, + _ d: D?, + _ e: E? + ) + -> (A, B, C, D, E)? { + return zip(zip(a, b), c, d, e).map { ($0.0, $0.1, $1, $2, $3) } +} + +public func zip( + with transform: @escaping (A, B, C, D, E) -> F + ) + -> (A?, B?, C?, D?, E?) -> F? { + return { zip($0, $1, $2, $3, $4).map(transform) } +} + +public func zip( + _ a: A?, + _ b: B?, + _ c: C?, + _ d: D?, + _ e: E?, + _ f: F? + ) + -> (A, B, C, D, E, F)? { + return zip(zip(a, b), c, d, e, f).map { ($0.0, $0.1, $1, $2, $3, $4) } +} + +public func zip( + with transform: @escaping (A, B, C, D, E, F) -> G + ) + -> (A?, B?, C?, D?, E?, F?) -> G? { + return { zip($0, $1, $2, $3, $4, $5).map(transform) } +} + +public func zip( + _ a: A?, + _ b: B?, + _ c: C?, + _ d: D?, + _ e: E?, + _ f: F?, + _ g: G? + ) + -> (A, B, C, D, E, F, G)? { + return zip(zip(a, b), c, d, e, f, g).map { ($0.0, $0.1, $1, $2, $3, $4, $5) } +} + +public func zip( + with transform: @escaping (A, B, C, D, E, F, G) -> H + ) + -> (A?, B?, C?, D?, E?, F?, G?) -> H? { + return { zip($0, $1, $2, $3, $4, $5, $6).map(transform) } +} + +public func zip( + _ a: A?, + _ b: B?, + _ c: C?, + _ d: D?, + _ e: E?, + _ f: F?, + _ g: G?, + _ h: H? + ) + -> (A, B, C, D, E, F, G, H)? { + return zip(zip(a, b), c, d, e, f, g, h).map { ($0.0, $0.1, $1, $2, $3, $4, $5, $6) } +} + +public func zip( + with transform: @escaping (A, B, C, D, E, F, G, H) -> I + ) + -> (A?, B?, C?, D?, E?, F?, G?, H?) -> I? { + return { zip($0, $1, $2, $3, $4, $5, $6, $7).map(transform) } +} + +public func zip( + _ a: A?, + _ b: B?, + _ c: C?, + _ d: D?, + _ e: E?, + _ f: F?, + _ g: G?, + _ h: H?, + _ i: I? + ) + -> (A, B, C, D, E, F, G, H, I)? { + return zip(zip(a, b), c, d, e, f, g, h, i).map { ($0.0, $0.1, $1, $2, $3, $4, $5, $6, $7) } +} + +public func zip( + with transform: @escaping (A, B, C, D, E, F, G, H, I) -> J + ) + -> (A?, B?, C?, D?, E?, F?, G?, H?, I?) -> J? { + return { zip($0, $1, $2, $3, $4, $5, $6, $7, $8).map(transform) } +} + +public func zip( + _ a: A?, + _ b: B?, + _ c: C?, + _ d: D?, + _ e: E?, + _ f: F?, + _ g: G?, + _ h: H?, + _ i: I?, + _ j: J? + ) + -> (A, B, C, D, E, F, G, H, I, J)? { + return zip(zip(a, b), c, d, e, f, g, h, i, j).map { ($0.0, $0.1, $1, $2, $3, $4, $5, $6, $7, $8) } +} + +public func zip( + with transform: @escaping (A, B, C, D, E, F, G, H, I, J) -> K + ) + -> (A?, B?, C?, D?, E?, F?, G?, H?, I?, J?) -> K? { + return { zip($0, $1, $2, $3, $4, $5, $6, $7, $8, $9).map(transform) } +} diff --git a/Sources/Overture/ZipSequence.swift b/Sources/Overture/ZipSequence.swift new file mode 100644 index 0000000..b4be696 --- /dev/null +++ b/Sources/Overture/ZipSequence.swift @@ -0,0 +1,987 @@ +public func zip( + _ a: A, + _ b: B, + _ c: C + ) + -> Zip3Sequence { + return Zip3Sequence(a, b, c) +} + +public func zip( + _ a: A, + _ b: B, + _ c: C, + _ d: D + ) + -> Zip4Sequence { + return Zip4Sequence(a, b, c, d) +} + +public func zip( + _ a: A, + _ b: B, + _ c: C, + _ d: D, + _ e: E + ) + -> Zip5Sequence { + return Zip5Sequence(a, b, c, d, e) +} + +public func zip( + _ a: A, + _ b: B, + _ c: C, + _ d: D, + _ e: E, + _ f: F + ) + -> Zip6Sequence { + return Zip6Sequence(a, b, c, d, e, f) +} + +public func zip( + _ a: A, + _ b: B, + _ c: C, + _ d: D, + _ e: E, + _ f: F, + _ g: G + ) + -> Zip7Sequence { + return Zip7Sequence(a, b, c, d, e, f, g) +} + +public func zip( + _ a: A, + _ b: B, + _ c: C, + _ d: D, + _ e: E, + _ f: F, + _ g: G, + _ h: H + ) + -> Zip8Sequence { + return Zip8Sequence(a, b, c, d, e, f, g, h) +} + +public func zip( + _ a: A, + _ b: B, + _ c: C, + _ d: D, + _ e: E, + _ f: F, + _ g: G, + _ h: H, + _ i: I + ) + -> Zip9Sequence { + return Zip9Sequence(a, b, c, d, e, f, g, h, i) +} + +public func zip( + _ a: A, + _ b: B, + _ c: C, + _ d: D, + _ e: E, + _ f: F, + _ g: G, + _ h: H, + _ i: I, + _ j: J + ) + -> Zip10Sequence { + return Zip10Sequence(a, b, c, d, e, f, g, h, i, j) +} + +public struct Zip3Sequence< + A: Sequence, + B: Sequence, + C: Sequence +>: Sequence { + internal let _a: A + internal let _b: B + internal let _c: C + + public init(_ a: A, _ b: B, _ c: C) { + _a = a + _b = b + _c = c + } + + public struct Iterator: IteratorProtocol { + internal var _baseStreamA: A.Iterator + internal var _baseStreamB: B.Iterator + internal var _baseStreamC: C.Iterator + internal var _reachedEnd: Bool = false + + internal init( + _ iteratorA: A.Iterator, + _ iteratorB: B.Iterator, + _ iteratorC: C.Iterator + ) { + _baseStreamA = iteratorA + _baseStreamB = iteratorB + _baseStreamC = iteratorC + } + + public typealias Element = ( + A.Element, + B.Element, + C.Element + ) + + public mutating func next() -> Element? { + if _reachedEnd { + return nil + } + + guard + let a = _baseStreamA.next(), + let b = _baseStreamB.next(), + let c = _baseStreamC.next() + else { + _reachedEnd = true + return nil + } + + return (a, b, c) + } + } + + public func makeIterator() -> Iterator { + return Iterator( + _a.makeIterator(), + _b.makeIterator(), + _c.makeIterator() + ) + } +} + +func zip< + A: Sequence, + B: Sequence, + C: Sequence, + D + >( + with f: @escaping (A.Element, B.Element, C.Element) -> D + ) + -> (A, B, C) -> [D] { + return { zip($0, $1, $2).map(f) } +} + +func zip< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E + >( + with f: @escaping (A.Element, B.Element, C.Element, D.Element) -> E + ) + -> (A, B, C, D) -> [E] { + return { zip($0, $1, $2, $3).map(f) } +} + +func zip< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E: Sequence, + F + >( + with f: @escaping (A.Element, B.Element, C.Element, D.Element, E.Element) -> F + ) + -> (A, B, C, D, E) -> [F] { + return { zip($0, $1, $2, $3, $4).map(f) } +} + +func zip< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E: Sequence, + F: Sequence, + G + >( + with f: @escaping (A.Element, B.Element, C.Element, D.Element, E.Element, F.Element) -> G + ) + -> (A, B, C, D, E, F) -> [G] { + return { zip($0, $1, $2, $3, $4, $5).map(f) } +} + +func zip< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E: Sequence, + F: Sequence, + G: Sequence, + H + >( + with f: @escaping (A.Element, B.Element, C.Element, D.Element, E.Element, F.Element, G.Element) -> H + ) + -> (A, B, C, D, E, F, G) -> [H] { + return { zip($0, $1, $2, $3, $4, $5, $6).map(f) } +} + +func zip< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E: Sequence, + F: Sequence, + G: Sequence, + H: Sequence, + I + >( + with f: @escaping (A.Element, B.Element, C.Element, D.Element, E.Element, F.Element, G.Element, H.Element) -> I + ) + -> (A, B, C, D, E, F, G, H) -> [I] { + return { zip($0, $1, $2, $3, $4, $5, $6, $7).map(f) } +} + +func zip< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E: Sequence, + F: Sequence, + G: Sequence, + H: Sequence, + I: Sequence, + J + >( + with f: @escaping (A.Element, B.Element, C.Element, D.Element, E.Element, F.Element, G.Element, H.Element, I.Element) -> J + ) + -> (A, B, C, D, E, F, G, H, I) -> [J] { + return { zip($0, $1, $2, $3, $4, $5, $6, $7, $8).map(f) } +} + +func zip< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E: Sequence, + F: Sequence, + G: Sequence, + H: Sequence, + I: Sequence, + J: Sequence, + K + >( + with f: @escaping (A.Element, B.Element, C.Element, D.Element, E.Element, F.Element, G.Element, H.Element, I.Element, J.Element) -> K + ) + -> (A, B, C, D, E, F, G, H, I, J) -> [K] { + return { zip($0, $1, $2, $3, $4, $5, $6, $7, $8, $9).map(f) } +} + +public struct Zip4Sequence< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence +>: Sequence { + internal let _a: A + internal let _b: B + internal let _c: C + internal let _d: D + + public init(_ a: A, _ b: B, _ c: C, _ d: D) { + _a = a + _b = b + _c = c + _d = d + } + + public struct Iterator: IteratorProtocol { + internal var _baseStreamA: A.Iterator + internal var _baseStreamB: B.Iterator + internal var _baseStreamC: C.Iterator + internal var _baseStreamD: D.Iterator + internal var _reachedEnd: Bool = false + + internal init( + _ iteratorA: A.Iterator, + _ iteratorB: B.Iterator, + _ iteratorC: C.Iterator, + _ iteratorD: D.Iterator + ) { + _baseStreamA = iteratorA + _baseStreamB = iteratorB + _baseStreamC = iteratorC + _baseStreamD = iteratorD + } + + public typealias Element = ( + A.Element, + B.Element, + C.Element, + D.Element + ) + + public mutating func next() -> Element? { + if _reachedEnd { + return nil + } + + guard + let a = _baseStreamA.next(), + let b = _baseStreamB.next(), + let c = _baseStreamC.next(), + let d = _baseStreamD.next() + else { + _reachedEnd = true + return nil + } + + return (a, b, c, d) + } + } + + public func makeIterator() -> Iterator { + return Iterator( + _a.makeIterator(), + _b.makeIterator(), + _c.makeIterator(), + _d.makeIterator() + ) + } +} + +public struct Zip5Sequence< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E: Sequence +>: Sequence { + internal let _a: A + internal let _b: B + internal let _c: C + internal let _d: D + internal let _e: E + + public init(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E) { + _a = a + _b = b + _c = c + _d = d + _e = e + } + + public struct Iterator: IteratorProtocol { + internal var _baseStreamA: A.Iterator + internal var _baseStreamB: B.Iterator + internal var _baseStreamC: C.Iterator + internal var _baseStreamD: D.Iterator + internal var _baseStreamE: E.Iterator + internal var _reachedEnd: Bool = false + + internal init( + _ iteratorA: A.Iterator, + _ iteratorB: B.Iterator, + _ iteratorC: C.Iterator, + _ iteratorD: D.Iterator, + _ iteratorE: E.Iterator + ) { + _baseStreamA = iteratorA + _baseStreamB = iteratorB + _baseStreamC = iteratorC + _baseStreamD = iteratorD + _baseStreamE = iteratorE + } + + public typealias Element = ( + A.Element, + B.Element, + C.Element, + D.Element, + E.Element + ) + + public mutating func next() -> Element? { + if _reachedEnd { + return nil + } + + guard + let a = _baseStreamA.next(), + let b = _baseStreamB.next(), + let c = _baseStreamC.next(), + let d = _baseStreamD.next(), + let e = _baseStreamE.next() + else { + _reachedEnd = true + return nil + } + + return (a, b, c, d, e) + } + } + + public func makeIterator() -> Iterator { + return Iterator( + _a.makeIterator(), + _b.makeIterator(), + _c.makeIterator(), + _d.makeIterator(), + _e.makeIterator() + ) + } +} + +public struct Zip6Sequence< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E: Sequence, + F: Sequence +>: Sequence { + internal let _a: A + internal let _b: B + internal let _c: C + internal let _d: D + internal let _e: E + internal let _f: F + + public init(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F) { + _a = a + _b = b + _c = c + _d = d + _e = e + _f = f + } + + public struct Iterator: IteratorProtocol { + internal var _baseStreamA: A.Iterator + internal var _baseStreamB: B.Iterator + internal var _baseStreamC: C.Iterator + internal var _baseStreamD: D.Iterator + internal var _baseStreamE: E.Iterator + internal var _baseStreamF: F.Iterator + internal var _reachedEnd: Bool = false + + internal init( + _ iteratorA: A.Iterator, + _ iteratorB: B.Iterator, + _ iteratorC: C.Iterator, + _ iteratorD: D.Iterator, + _ iteratorE: E.Iterator, + _ iteratorF: F.Iterator + ) { + _baseStreamA = iteratorA + _baseStreamB = iteratorB + _baseStreamC = iteratorC + _baseStreamD = iteratorD + _baseStreamE = iteratorE + _baseStreamF = iteratorF + } + + public typealias Element = ( + A.Element, + B.Element, + C.Element, + D.Element, + E.Element, + F.Element + ) + + public mutating func next() -> Element? { + if _reachedEnd { + return nil + } + + guard + let a = _baseStreamA.next(), + let b = _baseStreamB.next(), + let c = _baseStreamC.next(), + let d = _baseStreamD.next(), + let e = _baseStreamE.next(), + let f = _baseStreamF.next() + else { + _reachedEnd = true + return nil + } + + return (a, b, c, d, e, f) + } + } + + public func makeIterator() -> Iterator { + return Iterator( + _a.makeIterator(), + _b.makeIterator(), + _c.makeIterator(), + _d.makeIterator(), + _e.makeIterator(), + _f.makeIterator() + ) + } +} + +public struct Zip7Sequence< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E: Sequence, + F: Sequence, + G: Sequence +>: Sequence { + internal let _a: A + internal let _b: B + internal let _c: C + internal let _d: D + internal let _e: E + internal let _f: F + internal let _g: G + + public init(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G) { + _a = a + _b = b + _c = c + _d = d + _e = e + _f = f + _g = g + } + + public struct Iterator: IteratorProtocol { + internal var _baseStreamA: A.Iterator + internal var _baseStreamB: B.Iterator + internal var _baseStreamC: C.Iterator + internal var _baseStreamD: D.Iterator + internal var _baseStreamE: E.Iterator + internal var _baseStreamF: F.Iterator + internal var _baseStreamG: G.Iterator + internal var _reachedEnd: Bool = false + + internal init( + _ iteratorA: A.Iterator, + _ iteratorB: B.Iterator, + _ iteratorC: C.Iterator, + _ iteratorD: D.Iterator, + _ iteratorE: E.Iterator, + _ iteratorF: F.Iterator, + _ iteratorG: G.Iterator + ) { + _baseStreamA = iteratorA + _baseStreamB = iteratorB + _baseStreamC = iteratorC + _baseStreamD = iteratorD + _baseStreamE = iteratorE + _baseStreamF = iteratorF + _baseStreamG = iteratorG + } + + public typealias Element = ( + A.Element, + B.Element, + C.Element, + D.Element, + E.Element, + F.Element, + G.Element + ) + + public mutating func next() -> Element? { + if _reachedEnd { + return nil + } + + guard + let a = _baseStreamA.next(), + let b = _baseStreamB.next(), + let c = _baseStreamC.next(), + let d = _baseStreamD.next(), + let e = _baseStreamE.next(), + let f = _baseStreamF.next(), + let g = _baseStreamG.next() + else { + _reachedEnd = true + return nil + } + + return (a, b, c, d, e, f, g) + } + } + + public func makeIterator() -> Iterator { + return Iterator( + _a.makeIterator(), + _b.makeIterator(), + _c.makeIterator(), + _d.makeIterator(), + _e.makeIterator(), + _f.makeIterator(), + _g.makeIterator() + ) + } +} + +public struct Zip8Sequence< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E: Sequence, + F: Sequence, + G: Sequence, + H: Sequence +>: Sequence { + internal let _a: A + internal let _b: B + internal let _c: C + internal let _d: D + internal let _e: E + internal let _f: F + internal let _g: G + internal let _h: H + + public init(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H) { + _a = a + _b = b + _c = c + _d = d + _e = e + _f = f + _g = g + _h = h + } + + public struct Iterator: IteratorProtocol { + internal var _baseStreamA: A.Iterator + internal var _baseStreamB: B.Iterator + internal var _baseStreamC: C.Iterator + internal var _baseStreamD: D.Iterator + internal var _baseStreamE: E.Iterator + internal var _baseStreamF: F.Iterator + internal var _baseStreamG: G.Iterator + internal var _baseStreamH: H.Iterator + internal var _reachedEnd: Bool = false + + internal init( + _ iteratorA: A.Iterator, + _ iteratorB: B.Iterator, + _ iteratorC: C.Iterator, + _ iteratorD: D.Iterator, + _ iteratorE: E.Iterator, + _ iteratorF: F.Iterator, + _ iteratorG: G.Iterator, + _ iteratorH: H.Iterator + ) { + _baseStreamA = iteratorA + _baseStreamB = iteratorB + _baseStreamC = iteratorC + _baseStreamD = iteratorD + _baseStreamE = iteratorE + _baseStreamF = iteratorF + _baseStreamG = iteratorG + _baseStreamH = iteratorH + } + + public typealias Element = ( + A.Element, + B.Element, + C.Element, + D.Element, + E.Element, + F.Element, + G.Element, + H.Element + ) + + public mutating func next() -> Element? { + if _reachedEnd { + return nil + } + + guard + let a = _baseStreamA.next(), + let b = _baseStreamB.next(), + let c = _baseStreamC.next(), + let d = _baseStreamD.next(), + let e = _baseStreamE.next(), + let f = _baseStreamF.next(), + let g = _baseStreamG.next(), + let h = _baseStreamH.next() + else { + _reachedEnd = true + return nil + } + + return (a, b, c, d, e, f, g, h) + } + } + + public func makeIterator() -> Iterator { + return Iterator( + _a.makeIterator(), + _b.makeIterator(), + _c.makeIterator(), + _d.makeIterator(), + _e.makeIterator(), + _f.makeIterator(), + _g.makeIterator(), + _h.makeIterator() + ) + } +} + +public struct Zip9Sequence< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E: Sequence, + F: Sequence, + G: Sequence, + H: Sequence, + I: Sequence +>: Sequence { + internal let _a: A + internal let _b: B + internal let _c: C + internal let _d: D + internal let _e: E + internal let _f: F + internal let _g: G + internal let _h: H + internal let _i: I + + public init(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I) { + _a = a + _b = b + _c = c + _d = d + _e = e + _f = f + _g = g + _h = h + _i = i + } + + public struct Iterator: IteratorProtocol { + internal var _baseStreamA: A.Iterator + internal var _baseStreamB: B.Iterator + internal var _baseStreamC: C.Iterator + internal var _baseStreamD: D.Iterator + internal var _baseStreamE: E.Iterator + internal var _baseStreamF: F.Iterator + internal var _baseStreamG: G.Iterator + internal var _baseStreamH: H.Iterator + internal var _baseStreamI: I.Iterator + internal var _reachedEnd: Bool = false + + internal init( + _ iteratorA: A.Iterator, + _ iteratorB: B.Iterator, + _ iteratorC: C.Iterator, + _ iteratorD: D.Iterator, + _ iteratorE: E.Iterator, + _ iteratorF: F.Iterator, + _ iteratorG: G.Iterator, + _ iteratorH: H.Iterator, + _ iteratorI: I.Iterator + ) { + _baseStreamA = iteratorA + _baseStreamB = iteratorB + _baseStreamC = iteratorC + _baseStreamD = iteratorD + _baseStreamE = iteratorE + _baseStreamF = iteratorF + _baseStreamG = iteratorG + _baseStreamH = iteratorH + _baseStreamI = iteratorI + } + + public typealias Element = ( + A.Element, + B.Element, + C.Element, + D.Element, + E.Element, + F.Element, + G.Element, + H.Element, + I.Element + ) + + public mutating func next() -> Element? { + if _reachedEnd { + return nil + } + + guard + let a = _baseStreamA.next(), + let b = _baseStreamB.next(), + let c = _baseStreamC.next(), + let d = _baseStreamD.next(), + let e = _baseStreamE.next(), + let f = _baseStreamF.next(), + let g = _baseStreamG.next(), + let h = _baseStreamH.next(), + let i = _baseStreamI.next() + else { + _reachedEnd = true + return nil + } + + return (a, b, c, d, e, f, g, h, i) + } + } + + public func makeIterator() -> Iterator { + return Iterator( + _a.makeIterator(), + _b.makeIterator(), + _c.makeIterator(), + _d.makeIterator(), + _e.makeIterator(), + _f.makeIterator(), + _g.makeIterator(), + _h.makeIterator(), + _i.makeIterator() + ) + } +} + +public struct Zip10Sequence< + A: Sequence, + B: Sequence, + C: Sequence, + D: Sequence, + E: Sequence, + F: Sequence, + G: Sequence, + H: Sequence, + I: Sequence, + J: Sequence +>: Sequence { + internal let _a: A + internal let _b: B + internal let _c: C + internal let _d: D + internal let _e: E + internal let _f: F + internal let _g: G + internal let _h: H + internal let _i: I + internal let _j: J + + public init(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I, _ j: J) { + _a = a + _b = b + _c = c + _d = d + _e = e + _f = f + _g = g + _h = h + _i = i + _j = j + } + + public struct Iterator: IteratorProtocol { + internal var _baseStreamA: A.Iterator + internal var _baseStreamB: B.Iterator + internal var _baseStreamC: C.Iterator + internal var _baseStreamD: D.Iterator + internal var _baseStreamE: E.Iterator + internal var _baseStreamF: F.Iterator + internal var _baseStreamG: G.Iterator + internal var _baseStreamH: H.Iterator + internal var _baseStreamI: I.Iterator + internal var _baseStreamJ: J.Iterator + internal var _reachedEnd: Bool = false + + internal init( + _ iteratorA: A.Iterator, + _ iteratorB: B.Iterator, + _ iteratorC: C.Iterator, + _ iteratorD: D.Iterator, + _ iteratorE: E.Iterator, + _ iteratorF: F.Iterator, + _ iteratorG: G.Iterator, + _ iteratorH: H.Iterator, + _ iteratorI: I.Iterator, + _ iteratorJ: J.Iterator + ) { + _baseStreamA = iteratorA + _baseStreamB = iteratorB + _baseStreamC = iteratorC + _baseStreamD = iteratorD + _baseStreamE = iteratorE + _baseStreamF = iteratorF + _baseStreamG = iteratorG + _baseStreamH = iteratorH + _baseStreamI = iteratorI + _baseStreamJ = iteratorJ + } + + public typealias Element = ( + A.Element, + B.Element, + C.Element, + D.Element, + E.Element, + F.Element, + G.Element, + H.Element, + I.Element, + J.Element + ) + + public mutating func next() -> Element? { + if _reachedEnd { + return nil + } + + guard + let a = _baseStreamA.next(), + let b = _baseStreamB.next(), + let c = _baseStreamC.next(), + let d = _baseStreamD.next(), + let e = _baseStreamE.next(), + let f = _baseStreamF.next(), + let g = _baseStreamG.next(), + let h = _baseStreamH.next(), + let i = _baseStreamI.next(), + let j = _baseStreamJ.next() + else { + _reachedEnd = true + return nil + } + + return (a, b, c, d, e, f, g, h, i, j) + } + } + + public func makeIterator() -> Iterator { + return Iterator( + _a.makeIterator(), + _b.makeIterator(), + _c.makeIterator(), + _d.makeIterator(), + _e.makeIterator(), + _f.makeIterator(), + _g.makeIterator(), + _h.makeIterator(), + _i.makeIterator(), + _j.makeIterator() + ) + } +} diff --git a/Tests/OvertureTests/ZipTests.swift b/Tests/OvertureTests/ZipTests.swift new file mode 100644 index 0000000..e783725 --- /dev/null +++ b/Tests/OvertureTests/ZipTests.swift @@ -0,0 +1,70 @@ +import XCTest +import Overture + +final class ZipTests: XCTestCase { + func testZipOptional() { + guard + let (a, b, c, d, e, f, g, h, i, j) = zip( + Int?.some(1), + Int?.some(2), + Int?.some(3), + Int?.some(4), + Int?.some(5), + Int?.some(6), + Int?.some(7), + Int?.some(8), + Int?.some(9), + Int?.some(10) + ) + else { return XCTFail() } + + XCTAssertEqual(1, a) + XCTAssertEqual(2, b) + XCTAssertEqual(3, c) + XCTAssertEqual(4, d) + XCTAssertEqual(5, e) + XCTAssertEqual(6, f) + XCTAssertEqual(7, g) + XCTAssertEqual(8, h) + XCTAssertEqual(9, i) + XCTAssertEqual(10, j) + } + + func testZipSequence() { + let zipped = zip( + [1, 2], + [3, 4], + [5, 6], + [7, 8], + [9, 10], + [11, 12], + [13, 14], + [15, 16], + [17, 18], + [19, 20] + ) + let array = Array(zipped) + let fst = array[0] + XCTAssertEqual(1, fst.0) + XCTAssertEqual(3, fst.1) + XCTAssertEqual(5, fst.2) + XCTAssertEqual(7, fst.3) + XCTAssertEqual(9, fst.4) + XCTAssertEqual(11, fst.5) + XCTAssertEqual(13, fst.6) + XCTAssertEqual(15, fst.7) + XCTAssertEqual(17, fst.8) + XCTAssertEqual(19, fst.9) + let snd = array[1] + XCTAssertEqual(2, snd.0) + XCTAssertEqual(4, snd.1) + XCTAssertEqual(6, snd.2) + XCTAssertEqual(8, snd.3) + XCTAssertEqual(10, snd.4) + XCTAssertEqual(12, snd.5) + XCTAssertEqual(14, snd.6) + XCTAssertEqual(16, snd.7) + XCTAssertEqual(18, snd.8) + XCTAssertEqual(20, snd.9) + } +}