【Swift】Comparableプロトコルのヘルプドキュメントを読む

# この記事は何？
SwiftプログラミングにおけるComparableプロトコルについて、公式のヘルプドキュメントを翻訳および解説します。
参考: [Comparable | Apple Developer Documentation](https://developer.apple.com/documentation/swift/comparable)

以下の概念を理解しておくと、より理解が深まります。
* エクステンション
* 型メソッド
* 型の制約

# Comparableプロトコル

`Comparable`プロトコルに適合する型の値は、関係演算子`<`, `<=`, `>=`, `>`を使って互いの大小関係を比較できます。

# 宣言

```swift
protocol Comparable
```

# 概要

`Comparable`プロトコルは、数値や文字列のような固有の順序を持つ型に使用されます。
標準ライブラリに定義されている型は、その多くが`Comparable`プロトコルに適合しています。
独自に定義した型のインスタンスを関係演算子で比較したり、`Comparable`型用に設計された標準ライブラリのメソッドで使用したりする場合は、その独自型を`Comparable`に適合させてください。

以下、関係演算子の最も身近な例として、数値を比較します。

```swift
let currentTemp = 73

if currentTemp >= 90 {
    print("It's a scorcher!")
} else if currentTemp < 65 {
    print("Might need a sweater today.")
} else {
    print("Seems like picnic weather!")
}
// Prints "Seems like picnic weather!"
```

`Comparable`型を使用する場合、特殊な方法でシーケンスやコレクションを操作できます。
例えば、配列の要素が`Comparable`に適合している場合、引数を使用せずに`sort()`メソッドを呼び出して配列の要素を昇順にソートすることができます。

```swift
var measurements = [1.1, 1.5, 2.9, 1.2, 1.5, 1.3, 1.2]
measurements.sort()
print(measurements)
// Prints "[1.1, 1.2, 1.2, 1.3, 1.5, 1.5, 2.9]"
```

# Comparableプロトコルへの適合

`Comparable`に適合する型は、小なり演算子`<`と等価演算子`==`を実装しています。
これらの演算子は、型の値に対して厳密な総順序を課し、任意の二つの値`a`、`b`に対して、常に以下のひとつだけを「真である」とします。

* `a == b`
* `a < b`
* `b < a`

さらに、以下の条件が成立しなければならない。

* `a < a` は常に偽である (非反射性)
* `a < b` は `!(b < a)` を意味する (非対称性)
* `a < b` かつ `b < c` は `a < c` を意味する (Transitivity)

カスタム型を`Comparable`に適合させるには、`<`および`==`演算子を静的メソッドとして実装します。
なお、等価演算子`==`は、`Comparable`が継承する`Equatable`プロトコルの要件です。
他の関係演算子、標準ライブラリによってデフォルト実装が提供されます。
そのため、プログラマーがコーディングすることなく、独自定義する型のインスタンスは `!=`, `>`, `<=`, `>=` を使用できます。

次の例は、ある日付の「年、月、日」を保持する`Date`構造体の実装です。

```swift
struct Date {
    let year: Int
    let month: Int
    let day: Int
}
```

`Date`型を`Comparable`に適合させるには、プロトコルの採用を宣言します。
そして、`<`演算子関数を実装します。

```swift: Date型のエクステンション
extension Date: Comparable {
    static func < (lhs: Date, rhs: Date) -> Bool {
        if lhs.year != rhs.year {
            return lhs.year < rhs.year
        } else if lhs.month != rhs.month {
            return lhs.month < rhs.month
        } else {
            return lhs.day < rhs.day
        }
    }
```

この関数は、比較の結果を決定するために、日付の非特定プロパティのうち最も小さいものを使用します。
例えば、互いの`year`プロパティは等しいが、`month`プロパティが等しくない場合、`month`プロパティが小さい方を全体として小さい日付とします。

次に、`Equatable`プロトコルから継承した要件である`==`演算子関数を実装します。

```swift: Date型エクステンションの続き
    static func == (lhs: Date, rhs: Date) -> Bool {
        return lhs.year == rhs.year && 
               lhs.month == rhs.month && 
               lhs.day == rhs.day
    }
}
```

2つの`Date`型インスタンスは、対応する各プロパティが同じ値だった場合に「互いに等しい」とみなします。

`Date`型が`Comparable`に適合したことで、この型のインスタンスを任意の関係演算子で比較できるようになりました。
次の例では、「月面着陸の日付」と、デビッド・ボウイの名曲「Space Oddity」の発売日を比較します。

```swift
let spaceOddity = Date(year: 1969, month: 7, day: 11)   // July 11, 1969
let moonLanding = Date(year: 1969, month: 7, day: 20)   // July 20, 1969
if moonLanding > spaceOddity {
    print("Major Tom stepped through the door first.")
} else {
    print("David Bowie was following in Neil Armstrong's footsteps.")
}
// Prints "Major Tom stepped through the door first."
```

ここでは、上例で実装した`<`演算子ではなく、標準ライブラリが提供する`>`演算子を使用していることに注目してください。

:::note info
メモ
`Comparable`プロトコルに適合する型は「例外的に扱われる値のサブセット」を含むことがあります。
つまり、`Comparable`プロトコルの目的にとって「意味のある引数」から外れた領域にある値です。
例えば、浮動小数点型（`FloatingPoint.nan`）のための特殊な `"not a number "`は、通常の浮動小数点値と比較してより小さいし、大きいし、等しくない値として評価されます。
例外的な値は、厳密な順序に沿う必要はありません。
:::