An implementation of zip that creates a sequence of three-item tuples from the combination of three sequences. Much like the standard library's `zip` function for a pair of sequences.
/// Creates a sequence of tuple-3s built out of three underlying sequences. | |
/// Based on Zip2Seqence from the Swift Standard Library. | |
/// https://github.com/apple/swift/blob/9361a6b66f6f8351e89c090f604d7e1f42e2a045/stdlib/public/core/Zip.swift | |
/// | |
/// - Parameters: | |
/// - sequence1: The first sequence or collection to zip. | |
/// - sequence2: The second sequence or collection to zip. | |
/// - sequence3: The third sequence or collection to zip. | |
/// - Returns: A sequence of tuple triples, where the elements of each triplet are | |
/// corresponding elements of `sequence1`, `sequence2`, and `sequence3`. | |
@_inlineable | |
public func zip<Sequence1, Sequence2, Sequence3>( | |
_ sequence1: Sequence1, _ sequence2: Sequence2, _ sequence3: Sequence3 | |
) -> Zip3Sequence<Sequence1, Sequence2, Sequence3> { | |
return Zip3Sequence(sequence1, sequence2, sequence3) | |
} | |
/// A sequence of triplets built out of three underlying sequences. | |
@_fixed_layout | |
public struct Zip3Sequence<Sequence1: Sequence, Sequence2: Sequence, Sequence3: Sequence> { | |
internal let _sequence1: Sequence1 | |
internal let _sequence2: Sequence2 | |
internal let _sequence3: Sequence3 | |
public init(_ sequence1: Sequence1, _ sequence2: Sequence2, _ sequence3: Sequence3) { | |
(_sequence1, _sequence2, _sequence3) = (sequence1, sequence2, sequence3) | |
} | |
} | |
extension Zip3Sequence { | |
/// An iterator for `Zip2Sequence`. | |
@_fixed_layout | |
public struct Iterator { | |
internal var _baseStream1: Sequence1.Iterator | |
internal var _baseStream2: Sequence2.Iterator | |
internal var _baseStream3: Sequence3.Iterator | |
internal var _reachedEnd: Bool = false | |
/// Creates an instance around a triplet of underlying iterators. | |
internal init( | |
_ iterator1: Sequence1.Iterator, | |
_ iterator2: Sequence2.Iterator, | |
_ iterator3: Sequence3.Iterator | |
) { | |
(_baseStream1, _baseStream2, _baseStream3) = (iterator1, iterator2, iterator3) | |
} | |
} | |
} | |
extension Zip3Sequence.Iterator: IteratorProtocol { | |
/// The type of element returned by `next()`. | |
public typealias Element = (Sequence1.Element, Sequence2.Element, Sequence3.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`. | |
public mutating func next() -> Element? { | |
// The next() function needs to track if it has reached the end. If we | |
// didn't, and the first sequence is longer than the second, then when we | |
// have already exhausted the second sequence, on every subsequent call to | |
// next() we would consume and discard one additional element from the | |
// first sequence, even though next() had already returned nil. | |
if _reachedEnd { | |
return nil | |
} | |
guard let element1 = _baseStream1.next(), | |
let element2 = _baseStream2.next(), | |
let element3 = _baseStream3.next() else { | |
_reachedEnd = true | |
return nil | |
} | |
return (element1, element2, element3) | |
} | |
} | |
extension Zip3Sequence: Sequence { | |
public typealias Element = (Sequence1.Element, Sequence2.Element, Sequence3.Element) | |
/// Returns an iterator over the elements of this sequence. | |
public func makeIterator() -> Iterator { | |
return Iterator( | |
_sequence1.makeIterator(), | |
_sequence2.makeIterator(), | |
_sequence3.makeIterator() | |
) | |
} | |
} | |
let seq1 = [1, 2, 3, 4, 5] | |
let seq2 = [2, 4, 6, 8, 10] | |
let seq3 = [10, 20, 30, 40, 50] | |
let result = zip(seq1, seq2, seq3) | |
result.forEach { print($0) } | |
/* | |
(1, 2, 10) | |
(2, 4, 20) | |
(3, 6, 30) | |
(4, 8, 40) | |
(5, 10, 50) | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment