この記事は何?
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 == b
a < b
b < 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 "
は、通常の浮動小数点値と比較してより小さいし、大きいし、等しくない値として評価されます。
例外的な値は、厳密な順序に沿う必要はありません。