Created
December 28, 2017 18:49
-
-
Save proxpero/a6f35ffe24d09539174a2454f1d8fc06 to your computer and use it in GitHub Desktop.
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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