LoginSignup
1
2

More than 1 year has passed since last update.

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

Posted at

この記事は何?

SwiftプログラミングにおけるHashableプロトコルについて、公式のヘルプドキュメントを翻訳および解説します。
参考: Hashable | Apple Developer Documantation

宣言

protocol Hashable : Equatable

概要

Hashableプロトコルに適合した型は、セットや辞書のキーとして使用できます。
標準ライブラリの型は、その多くがHashableプロトコルに適合しています。
文字列、整数、浮動小数点数、ブール値、そしてセットもデフォルトでハッシュ化可能です。
オプショナル、配列、範囲などの他のいくつかの型は、実装する型パラメータが適合すると、自動的にハッシュ可能になります。

独自に定義した型もハッシュ化可能です。
例えば、関連値のない列挙型は、定義するだけで自動的にHashableプロトコルに適合します。
また、格納プロパティがすべてHashableな構造体や、関連値がすべてHashableな列挙型については、コンパイラが自動的にhash(into:)の実装を提供します。

「値をハッシュする」ことは、その値の本質的な構成要素をHasher型で表されるハッシュ関数に引き渡すことを意味します。
本質的な構成要素とは、その型が「どのようにEquatableを実装したか」に依存します。
互いに等しいインスタンス同士は同じ順序で、同じ値をhash(into:)メソッドのHasherに与える必要があります。

Hashableプロトコルへの適合

独自に定義した型をセットまたは辞書のキーとして使用するには、その型をHashableプロトコルに適合させます。
HashableプロトコルはEquatableを継承しているため、そのプロトコルの要件も満たす必要があります。

元となる型がHashableを採用した上で、以下に挙げる条件を満たしている場合、コンパイラはHashableへの適合に必要な実装を自動的に生成します。

  • 構造体の場合、すべての格納プロパティがHashableに適合している。
  • 列挙型の場合、すべての関連値がHashableに適合している(関連値のない列挙型は宣言なしでもHashableに適合します)。

型のHashable適合性をカスタマイズしたり、上記の基準を満たさない型にHashableを採用したり、既存の型を拡張してHashableに適合させるには、独自にhash(into:)メソッドを実装してください。

hash(into:)メソッドの実装では、提供されたHasher型インスタンスに対して、カスタム型の重要な構成要素とともにcombine(_:)メソッドを呼び出します。
HashableおよびEquatableプロトコルのセマンティック要件を確実に満たすために、型のEquatable適合性もカスタマイズすることをお勧めします。

例として、ボタンのグリッド位置を記述するGridPoint型を考えます。
以下では、最初にGridPoint型を宣言します。

/// x軸とy軸からなる平面座標における点のモデル
struct GridPoint {
    var x: Int
    var y: Int
}

次に、ユーザーがタップしたことのある位置を示す「グリッドポイントのセット」を作成します。
GridPoint型はまだHashableプロトコルに適合していないので、セットで使用することはできません。
Hashableに適合させるには等価演算子関数==を実装してから、hash(into:)メソッドも実装してください。

extension GridPoint: Hashable {
    static func == (lhs: GridPoint, rhs: GridPoint) -> Bool {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(x)
        hasher.combine(y)
    }
}

この例のhash(into:)メソッドは、提供されたハッシャーに「xyのプロパティ」をフィードします。
これらのプロパティは、等価演算子関数==で「互いに等しいかどうか」を比較するために使用されるものと同じです。

GridPointHashableプロトコルに適合できたので、タップされたグリッドポイントのセットを作成できるようになりました。

var tappedPoints: Set = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y: 1)]
let nextTap = GridPoint(x: 0, y: 1)
if tappedPoints.contains(nextTap) {
    print("Already tapped at (\(nextTap.x), \(nextTap.y)).")
} else {
    tappedPoints.insert(nextTap)
    print("New tap detected at (\(nextTap.x), \(nextTap.y)).")
}
// Prints "New tap detected at (0, 1).")
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2