Skip to content

Instantly share code, notes, and snippets.

@proxpero
Created December 28, 2017 18:49
Show Gist options
  • Save proxpero/a6f35ffe24d09539174a2454f1d8fc06 to your computer and use it in GitHub Desktop.
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.
/// 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