【Swift】公式ドキュメントのIteratorProtocolを読む

# この記事は何？
SwiftのIteratorProtocolについて調べました。
主に、[開発者ドキュメント](https://developer.apple.com/documentation/swift/iteratorprotocol)
より翻訳した内容になります。

# Declaration
宣言

```swift
protocol IteratorProtocol<Element>
```

# Overview
概要

Swiftにおいて、「連続して、値を一つずつ取り出せるデータ構造」を**シーケンス**といいます。
`Sequence`プロトコルに適合する型のインスタンスは`for-in`ループを使って、要素ごとに処理を反復できます。
その際、シーケンスに対して「一つずつ値を取り出す役割」を果たすインスタンスが**イテレータ**です。
インスタンスの型が`IteratorProtocol`プロトコルに適合する場合、`makeIterator()`メソッドでイテレータを取得できます。


`IteratorProtocol`プロトコルは`Sequence`プロトコルと密接に関わっています。
イテレータは反復処理を追跡して、シーケンスを進めながら要素を一つずつ返します。

配列、セット、または他のコレクションやシーケンスで`for-in`ループを使用すると、その型のイテレータを使用しています。
Swiftは`for-in`ループを実行する際、シーケンスまたはコレクションのイテレータを内部的に使用します。

シーケンスのイテレータを直接使用すると、`for-in`ループを使用して「そのシーケンス上で反復処理する」のと同じ順序で要素にアクセスできます。
以下の例では、`for-in`ループを利用して配列の各要素を表示します。

```swift:
let animals = ["Antelope", "Butterfly", "Camel", "Dolphin"]
for animal in animals {
    print(animal)
}
// Prints "Antelope"
// Prints "Butterfly"
// Prints "Camel"
// Prints "Dolphin"
```

内部的には、Swiftは`animals`配列のイテレータを使用して配列の構成要素をループしています。

```swift:
var animalIterator = animals.makeIterator()
while let animal = animalIterator.next() {
    print(animal)
}
// Prints "Antelope"
// Prints "Butterfly"
// Prints "Camel"
// Prints "Dolphin"
```

`animals.makeIterator()`メソッドは「配列のイテレータのインスタンス」を返します。
そして、`while`ループはイテレータの`next()`メソッドを繰り返し呼び出し、取得した各要素を`animal`にバインドし、`next()`メソッドが`nil`を返すと終了します。

# Using Iterators Directly
イテレータを使用するには

`for-in`ループはSwiftでシーケンスのデータを横断するためのより慣用的なアプローチです。
したがって、わざわざ、イテレータを直接使用する必要はないでしょう。
ただし、一部のアルゴリズムでは直接、イテレータを使用する必要があるかもしれません。

1つの例として、`reduce1(_:)`メソッドを挙げます。
`reduce1(_:)`メソッドは標準ライブラリの`reduce(_:_:)`メソッドと同様に、初期値と結合クロージャを受け取ります。
そして、「シーケンスの最初の要素」を初期値として使用します。

以下は、`reduce1(_:)`メソッドの実装です。
シーケンスの残りの部分をループする前に「初期値を取得する」ために、シーケンスのイテレータを直接使用しています。

```swift:
extension Sequence {
    func reduce1(_ nextPartialResult: (Element, Element) -> Element) -> Element?
    {
        var i = makeIterator()
        guard var accumulated = i.next() else {
            return nil
        }

        while let element = i.next() {
            accumulated = nextPartialResult(accumulated, element)
        }
        return accumulated
    }
}
```

`Reduce1(_:)`メソッドは、特定の種類のシーケンス操作をより簡単にします。
先ほど紹介した`animals`配列を例として、シーケンス内で最も長い文字列を見つける方法は次のとおりです。

```swift:
let longestAnimal = animals.reduce1 { current, element in
    if current.count > element.count {
        return current
    } else {
        return element
    }
}
print(longestAnimal)
// Prints Optional("Butterfly")
```

# Using Multiple Iterators
複数のイテレータを使用するには

単一のシーケンスで複数のイテレータ(または`for-in`ループ)を使用するときは、その具体的な型を知っているか、シーケンスが`collection`プロトコルにも制限されているため、特定のシーケンスが繰り返し反復をサポートすることを知っていることを確認してください。
Whenever you use multiple iterators (or for-in loops) over a single sequence, be sure you know that the specific sequence supports repeated iteration, either because you know its concrete type or because the sequence is also constrained to the Collection protocol.

コピーするのではなく、シーケンスの`makeIterator()`メソッドをその都度呼び出して、別個のイテレータを取得します。
イテレータをコピーすることは安全ですが、同じイテレータの`next()`メソッドを呼び出してコピーを進めると、そのイテレータの他のコピーが無効になる恐れがあります。
この点で`for-in`ループは安全です。

# Adding IteratorProtocol Conformance to Your Type
独自の型をIteratroProtocolに適合させるには

`IteratorProtocol`に適合するためのイテレータは、簡単に実装できます。
関連するシーケンスのステップを1つ進めて、現在の要素を返す`next()`メソッドを宣言します。
シーケンスを最後まで進めたら、`next()`メソッドは`nil`を返します。

例として、独自の`Countdown`型シーケンスを考えます。
カウントダウンのシーケンスを開始する整数で初期化したら、ゼロまで反復処理できます。
`Countdown`構造体の定義はわずかです。
`Sequence`プロトコルに必要な開始カウントと`makeIterator()`メソッドのみが含まれています。

```swift:
struct Countdown: Sequence {
    let start: Int

    func makeIterator() -> CountdownIterator {
        return CountdownIterator(self)
    }
}
```

`makeIterator()`メソッドは、独自に定義した`CountdownIterator`型のイテレータを返します。
`CountdownIterator`型は、「反復中のカウントダウンシーケンス」と「値を返した回数」の両方を追跡します。

```swift:
struct CountdownIterator: IteratorProtocol {
    let countdown: Countdown    // 反復中のシーケンス
    var times = 0               // 値を返した回数

    // 初期化時に「反復中のシーケンス」を受け取る
    init(_ countdown: Countdown) {
        self.countdown = countdown
    }

    // シーケンスを進める
    mutating func next() -> Int? {
        let nextNumber = countdown.start - times  // 開始が10で3回目なら、次は7
        // 次の数が0より大きくなければ、nilを返して終了
        guard nextNumber > 0 else { return nil }

        // 次の数が0より大きいなら「値を返した回数」をインクリメントして、「次の数」を返す
        times += 1
        return nextNumber
    }
}
```

`CountdownIterator`インスタンスで`next()`メソッドが呼び出されるたびに、新しい次の値を計算し、ゼロに達したかどうかを確認し、イテレータが終了した場合は`nil`を、そうでなければ「シーケンスの要素」を返します。

カウントダウンのシーケンスを作成して反復処理するには、`CountdownIterator`型インスタンスに対して反復処理を行います。

```swift:
let threeTwoOne = Countdown(start: 3)
for count in threeTwoOne {
    print("\(count)...")
}
// Prints "3..."
// Prints "2..."
// Prints "1..."
```