[Swift] 要素数が異なる配列のzip , 引数3つのzip

## Swiftの[`zip`関数](https://developer.apple.com/documentation/swift/zip(_:_:))の話です

よくあるサンプルだと、次の内容です。

```swift
let words = ["one", "two", "three", "four"]
let numbers = 1...4

zip(words, numbers).forEach { print($0) }

//("one", 1)
//("two", 2)
//("three", 3)
//("four", 4)
```

<br>

> タイトルに”配列”と書きましたが、`zip`が取り得る引数は Sequence 準拠型です。`Array`（配列）はその一つで、分かりやすさから、こう表現しました。
`Set`や`Dictionary`でも`zip`を使うことができます。


# 要素数が異なると どうなる？

例えば、次の場合です。

```swift
let words = ["one", "two", "three", "four"]
let numbers = 1...3

zip(words, numbers).forEach { print($0) }

//("one", 1)
//("two", 2)
//("three", 3)
```

3組のタプルしか得られません。これは、要素数の少ない方に揃うからです。

そもそも`zip`は、`Zip2Sequence`を返す関数です。
`Zip2Sequence`は、`zip`の引数で受け取った 2つの`Sequence`のイテレータを保持しています。

そして、`Zip2Sequence`のイテレータが、2つの`Sequence`のイテレータの「どれか」が終了した時点で、終了するためです。実際の[ソースコード](https://github.com/swiftlang/swift/blob/main/stdlib/public/core/Zip.swift)で確認します。


```swift:stdlib/public/core/Zip.swift（127行目から抜粋）
  public mutating func next() -> Element? {

    if _reachedEnd {
      return nil
    }

    guard let element1 = _baseStream1.next(),
          let element2 = _baseStream2.next() else {
      _reachedEnd = true
      return nil
    }

    return (element1, element2)
  }
```

`guard`文のところです。`zip`の引数で受け取った 2つの`Sequence`のイテレータから、`next()`（”次の要素”）を取得し、一つでも`nil`なら”終端”としてnilを返していますね。

## 要素数の大きい方に揃えたい

要素数の大きい方に揃えるために、要素数の少ない方は`nil`を返すことにします。
前出の例だと、⭐️の行が追加されるイメージです。

```swift
let words = ["one", "two", "three", "four"]
let numbers = 1...3

zip_x(words, numbers).forEach { print($0) }

//("one", 1)
//("two", 2)
//("three", 3)
//("four", nil)  ⭐️
```

実装はそれほど難しいことはなく、先ほどの`guard`文のところを書き換えて、「すべて」のイテレータが`nil`になったら、”終端”としてnilを返すように変更するだけです。

ただし、タプルの要素が`Optional`型になるため、関連する型定義（要素定義）も一緒に変更する必要があります。
コードは、次のテーマと一緒に提示します。

続いて、もう一つのテーマである『引数3つのzip』についての話です。

## 引数3つのzip

`zip`（のイテレータ）は タプル を使っているため、`zip`の引数を可変にすることは簡単ではありません。『可変要素数のタプル』が定義できないためです。

そこで、今回は、『要素数の大きい方に揃えた』『引数3つのzip』を定義してみます。3番目の引数は省略可能にしておきます。こうすることで、引数が2つでも使えますから。
（ネーミングがイマいちですが）

```swift:NullabeZip3.swift
@inlinable
public func nullabeZip3<Sequence1, Sequence2, Sequence3>(
    _ sequence1: Sequence1,
    _ sequence2: Sequence2,
    _ sequence3: Sequence3 = Array<Int>()
) -> NullabeZip3Sequence<Sequence1, Sequence2, Sequence3> {
    return NullabeZip3Sequence(sequence1, sequence2, sequence3)
}

@frozen
public struct NullabeZip3Sequence<Sequence1: Sequence, Sequence2: Sequence, Sequence3: Sequence> {
    @usableFromInline
    internal let _sequence1: Sequence1
    @usableFromInline
    internal let _sequence2: Sequence2
    @usableFromInline
    internal let _sequence3: Sequence3

    @inlinable
    internal init(_ sequence1: Sequence1, _ sequence2: Sequence2, _ sequence3: Sequence3) {
        (_sequence1, _sequence2, _sequence3) = (sequence1, sequence2, sequence3)
    }
}

extension NullabeZip3Sequence {
    @frozen
    public struct Iterator {
        @usableFromInline
        internal var _baseStream1: Sequence1.Iterator
        @usableFromInline
        internal var _baseStream2: Sequence2.Iterator
        @usableFromInline
        internal var _baseStream3: Sequence3.Iterator
        @usableFromInline
        internal var _reachedEnd: Bool = false
        
        @inlinable
        internal init(
            _ iterator1: Sequence1.Iterator,
            _ iterator2: Sequence2.Iterator,
            _ iterator3: Sequence3.Iterator
        ) {
            (_baseStream1, _baseStream2, _baseStream3) = (iterator1, iterator2, iterator3)
        }
    }
}

extension NullabeZip3Sequence.Iterator: IteratorProtocol {
    public typealias Element = (Sequence1.Element?, Sequence2.Element?, Sequence3.Element?)
    
    @inlinable
    public mutating func next() -> Element? {
        
        if _reachedEnd {
            return nil
        }

        let element1 = _baseStream1.next()
        let element2 = _baseStream2.next()
        let element3 = _baseStream3.next() 
        if element1 == nil && element2 == nil && element3 == nil {
            _reachedEnd = true
            return nil
        }
        
        return (element1, element2, element3)
    }
}

extension NullabeZip3Sequence: Sequence {
    public typealias Element = (Sequence1.Element?, Sequence2.Element?, Sequence3.Element?)
    
    @inlinable
    public __consuming func makeIterator() -> Iterator {
        return Iterator(
            _sequence1.makeIterator(),
            _sequence2.makeIterator(),
            _sequence3.makeIterator()
        )
    }
    
    @inlinable
    public var underestimatedCount: Int {
        return Swift.min(
            _sequence1.underestimatedCount,
            _sequence2.underestimatedCount,
            _sequence3.underestimatedCount
        )
    }
}

extension NullabeZip3Sequence: Sendable where Sequence1: Sendable,
                                       Sequence2: Sendable,
                                       Sequence3: Sendable { }
extension NullabeZip3Sequence.Iterator: Sendable where Sequence1.Iterator: Sendable,
                                                Sequence2.Iterator: Sendable,
                                                Sequence3.Iterator: Sendable { }
```

使い方は以下のとおり。

```swift:使い方
let words = ["one", "two", "three", "four"]
let numbers = 1...5
let bools = [true, false]

let ex1 = nullabeZip3(words, numbers, bools)
ex1.forEach { print($0) }
//(Optional("one"), Optional(1), Optional(true))
//(Optional("two"), Optional(2), Optional(false))
//(Optional("three"), Optional(3), nil)
//(Optional("four"), Optional(4), nil)
//(nil, Optional(5), nil)

let ex2 = nullabeZip3(words, numbers)
ex2.forEach { print($0) }
//(Optional("one"), Optional(1), nil)
//(Optional("two"), Optional(2), nil)
//(Optional("three"), Optional(3), nil)
//(Optional("four"), Optional(4), nil)
//(nil, Optional(5), nil)
```

オプショナル型のタプルのため、アンラップして参照する必要がありますが、持っておけばどこかで使える場面があるかも？

```swift:使い方(2)
extension Optional {
    var value: String {
        if let val = self { return "\(val)" }
        else { return "nil" }
    }
}

ex1.forEach { print("(\($0.0.value), \($0.1.value), \($0.2.value))") }
//(one, 1, true)
//(two, 2, false)
//(three, 3, nil)
//(four, 4, nil)
//(nil, 5, nil)

ex2.forEach { print("(\($0.0.value), \($0.1.value), \($0.2.value))") }
//(one, 1, nil)
//(two, 2, nil)
//(three, 3, nil)
//(four, 4, nil)
//(nil, 5, nil)
```

<br>

:::note
> 最初から引数を10個くらい用意して、3番目以降をオプション引数にすれば、事実上の可変引数`zip`と言えるかも知れません。

引数Nのzipソースコードを生成するスクリプトを公開している方がいらっしゃいました。

https://github.com/amomchilov/ZipNsequence
:::


## 可変引数のzip

『可変要素数のタプル』が定義できないため、タプルを使う限り`zip`の引数を可変にすることはできません。そこで、`Array<Any>`で返すようして、真の可変引数の`zip`を実装したいと思います。

ところが、未解決のコンパイルエラーのため、まだ完成していません。

作成中の`zip`の引数を`Array<any Sequence>`として、
引数で受け取った複数のSequenceから、`Iterator`が生成できないのです。

```swift
  //let _sequences: Array<any Sequence>
  var iterators = Array<IteratorProtocol>()
  for seq in _sequences {
      let ite = seq.makeIterator() //⭐️
      iterators.append(ite)
  }
```

⭐️<i>Inferred result type 'any IteratorProtocol' requires explicit coercion due to loss of generic requirements</i>

> ジェネリックを使わず`Array`限定なら書けるのですが、それじゃ意味が半減（以下）ですから、あくまで`Sequence`で実装したい。

<br>

制約の付け方ができていないためだとは思うのですが、付け方が分からず、まだまだ時間が掛かりそうです。


**以上**