LoginSignup
24
25

More than 5 years have passed since last update.

Swiftのenumは最小完全ハッシュ値を持っていた.

Posted at

enumを

Enum IntEnum: Int { case A = 0xA000, B = 0xB000, C = 0xC000 }
Enum StrEnum: String { case A = "Alfa", B = "Bravo", C = "Charlie" }

と定義したとき,IntEnum.A.hashValueStrEnum.B.hashValueはそれぞれ0xA000"Alfa"のハッシュ値になるのかと思いきや,さにあらず.

println(\(IntEnum.A.hashValue))     // 0
println(\(IntEnum.B.hashValue))     // 1
println(\(IntEnum.C.hashValue))     // 2

なんと,列挙した順に値が付けられていました.連番,最小完全ハッシュですね,これ.
StrEnumの方ももちろん同様.3万個以上列挙しても,もちろん同様でした.(限界まで確かめたかったのですがコンパイルが終わらないので断念.)

enumをキーとしたコンパクトなディクショナリー

このhashValueを使わない手はありません.もろに配列のインデックスに使えます.
これは,enumをDictionaryのkeyに指定するのコメント欄の議論の延長ですが,hashValueを使うとさらにシンプルに辞書を実装できます.ほんの一つ足りないものがあるため,protocolを定義してextensionします.

protocol FastEnumKey: Hashable {
    class var count: Int {get}
}

というプロトコルを定義します.countとは要は列挙の個数です.
そして例えば,上のIntEnumFastEnumKeyプロトコルに準拠させます.

extension IntEnum: FastEnumKey {
    static var count: Int { return 3 }
}

この場合,列挙数は3と決め打ちです.extensionによって後からcaseを追加することは不可能なので決め打ちで問題ないと思います.
あとは,EnumDictionaryを組み込みのディクショナリーに似せて軽く実装するだけ.このとき,KeyのタイプをFastEnumKeyに制約します.(以下のコードはここからほぼ引用してます.)

struct EnumDictionary<Key: FastEnumKey, Value> {
    var values = [Value?](count: Key.count, repeatedValue: nil)

    subscript(key: Key) -> Value? {
        get {
            return values[key.hashValue]  // hashValueは配列のインデックスである.
         }
        set {
             values[key.hashValue] = newValue!
        }
    }
}

hashValue値がそのまま配列のインデックスに使えるのでとてもシンプル.
簡単とはいえ,いちいちextensionしなければならないのが難点ですが,列挙の個数だけは今のSwiftのリフレクションを使っても取得できないので仕方ありません.

ところで,enumに設定した値はどこにあるの?

enumではfromRawtoRawというメソッドで値<==>列挙変換ができますが,この変換テーブルはメモリ上に隠されているみたいです.enumのサイズが

sizeofValue(StrEnum.A) == 1

なのです.(caseが255個以下ならサイズは1, それ以上なら2になることは確かめた.)
そしてその1バイトはhashValue値になっていました.設定した値をプロパティとして包含してるわけではないんですね.
変換テープルのアドレスが分かれば,上のcountは取得できそうなのですけどね.ちょっと残念です.

24
25
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
24
25