Implementation of Collection Versions of Zip

Sure, but the compound type doesn't have to be a tuple. e.g. here's a very rough sketch of a minimal implementation of Zip2Collection with an enum index (forgive the poorly chosen names and any errors):

struct Zip2Collection<S1: Collection, S2: Collection>: Collection {
  private var s1: S1
  private var s2: S2
  
  typealias Element = (S1.Element, S2.Element)
  
  enum Zip2CollectionIndex: Comparable {
    case pair(S1.Index, S2.Index)
    case end
    
    static func < (lhs: Zip2CollectionIndex, rhs: Zip2CollectionIndex) -> Bool {
      switch (lhs, rhs) {
      case (.end, .end): return false
      case (.end, _): return false
      case (_, .end): return true
      case let (.pair(lhsi1, lhsi2), .pair(rhsi1, rhsi2)):
        guard (lhsi1 < rhsi1) == (lhsi2 < rhsi2) else { fatalError("index mismatch")}
        return lhsi1 < rhsi1
      }
    }
  }
  
  typealias Index = Zip2CollectionIndex
  
  var startIndex: Index { return .pair(s1.startIndex, s2.startIndex) }
  var endIndex: Index { return .end }
  
  subscript(index: Index) -> Element {
    guard case let .pair(i1, i2) = index else { fatalError("invalid index") }
    return (s1[i1], s2[i2])
  }
  
  func index(after i: Index) -> Index {
    switch i {
    case .end: fatalError("can't advance endIndex")
    case let .pair(i1, i2):
      let nexti1 = s1.index(after: i1)
      let nexti2 = s2.index(after: i2)
      if nexti1 == s1.endIndex || nexti2 == s2.endIndex {
        return .end
      }
      return .pair(nexti1, nexti2)
    }
  }
  
  init(_ s1: S1, _ s2: S2) {
    self.s1 = s1
    self.s2 = s2
  }
}

A real implementation would want to hide the index type better, so invalid indices can't be directly constructed, and do a lot of specialisation for performance reasons.

2 Likes