この記事は何?
SwiftプログラミングにおけるComparableプロトコルについて、公式のヘルプドキュメントを翻訳および解説します。
参考: Comparable | Apple Developer Documentation
以下の概念を理解しておくと、より理解が深まります。
- エクステンション
- 型メソッド
- 型の制約
Comparableプロトコル
Comparableプロトコルに適合する型の値は、関係演算子<, <=, >=, >を使って互いの大小関係を比較できます。
宣言
protocol Comparable
概要
Comparableプロトコルは、数値や文字列のような固有の順序を持つ型に使用されます。
標準ライブラリに定義されている型は、その多くがComparableプロトコルに適合しています。
独自に定義した型のインスタンスを関係演算子で比較したり、Comparable型用に設計された標準ライブラリのメソッドで使用したりする場合は、その独自型をComparableに適合させてください。
以下、関係演算子の最も身近な例として、数値を比較します。
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()メソッドを呼び出して配列の要素を昇順にソートすることができます。
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 == ba < bb < a
さらに、以下の条件が成立しなければならない。
-
a < aは常に偽である (非反射性) -
a < bは!(b < a)を意味する (非対称性) -
a < bかつb < cはa < cを意味する (Transitivity)
カスタム型をComparableに適合させるには、<および==演算子を静的メソッドとして実装します。
なお、等価演算子==は、Comparableが継承するEquatableプロトコルの要件です。
他の関係演算子、標準ライブラリによってデフォルト実装が提供されます。
そのため、プログラマーがコーディングすることなく、独自定義する型のインスタンスは !=, >, <=, >= を使用できます。
次の例は、ある日付の「年、月、日」を保持するDate構造体の実装です。
struct Date {
let year: Int
let month: Int
let day: Int
}
Date型をComparableに適合させるには、プロトコルの採用を宣言します。
そして、<演算子関数を実装します。
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プロトコルから継承した要件である==演算子関数を実装します。
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」の発売日を比較します。
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."
ここでは、上例で実装した<演算子ではなく、標準ライブラリが提供する>演算子を使用していることに注目してください。
メモ
Comparableプロトコルに適合する型は「例外的に扱われる値のサブセット」を含むことがあります。
つまり、Comparableプロトコルの目的にとって「意味のある引数」から外れた領域にある値です。
例えば、浮動小数点型(FloatingPoint.nan)のための特殊な "not a number "は、通常の浮動小数点値と比較してより小さいし、大きいし、等しくない値として評価されます。
例外的な値は、厳密な順序に沿う必要はありません。