Sponsor Link
環境&対象
- macOS Big Sur 11.2.1
- Xcode 12.4
- Swift5.3.2
記事中のコードは、Playground で動作します。
enum
enum は class, struct と並んで、3大主要構成要素 (first class citizen) の1つです。
ですが、あまり積極的に使われていない気がするので、少し凝った使い方を説明します。
enum 基本編
enum の定義
他の言語と同様に、特定の有限数の集合を表現できます。
以下は、Size と名付けた enum の定義です
enum Size {
case S, M, L
}
Size という型を使うことで、S,M,L の値のいずれかを持っていることを保証できます。
enum を使わないとすると Int 型を使用して定義し、1がSを表し、2、3がM,Lを表すという運用をすることになりますが、Size を表す型は Int 型であるために、4や5が設定されることを言語的には防止できません。
enum を使うことで、そのような制約を課すことができ、特定値の集合をもつような型を安全に使用することが可能となります。
enum への表現としての値の割り当て
C 言語等では、内部的に数値が割り振られて管理されますが、swift では、自動的には割り振られません。
明示的に数値を割り当てたい時には、以下のように定義します。
enum Size:Int, RawRepresentable{
case S = 1, M, L
}
Int に準拠すると宣言したことで、整数を割り振ることを宣言し、RawRepresentable に準拠すると宣言したことで、
以下のように、rawValue プロパティでアクセスすることができるようになります。
enum Size:Int, RawRepresentable{
case S = 1, M, L // S に 1 を割り当て、あとは列挙順に割り当てることを指定
}
let size = Size.M
print(size.rawValue) // -> 2
上記のようにすることで、rawValue を +1 とすることで、サイズアップという操作にできます。(ただし、L の時にどうするかを検討しなければいけません)
この例のように enum の値が数値で表されることで、数値演算として処理できるようになり便利なることもあります。
Equatable
型として使うには、同値判定が必要となるケースがあります。
Equatable に準拠すると宣言するだけで、== で 同値判定を行うことができます。
enum Size:Int, RawRepresentable, Equatable{
case S = 1, M, L
}
let size1 = Size.L
let size2 = Size.M
print(size1 == size2) // -> false
ちなみに、RawRepresentable と宣言せずとも、Equatable に準拠させることができます。
enum Size:Equatable{
case S, M, L
}
let size1 = Size.L
let size2 = Size.M
print(size1 == size2) // -> false
enum の associated value
Enum では特定値の集合を型として定義できるのですが、各値に対して、付加情報を追加することができます。associated value と呼ばれます。
associated value 付き enum の定義
以下は、Food という enum を定義し、種類(ピザ、ハンバーガー、おにぎり)を定義しています。associated value としてその量も保持できるようにしました。
enum Food {
enum Size {
case S, M, L
}
case pizza(Size)
case hamburger(Int)
case onigiri(Size,Int)
}
ピザはサイズ情報をもち、ハンバーガーは数を、おにぎりはサイズと数の情報を associated value として持っています。
このように、enum に属する値がそれぞれ異なるタイプの associated value を持つことができます。
Equatable
associated value を定義していても、準拠するだけで、Equatable になります。
enum Food:Equatable {
enum Size: Equatable {
case S, M, L
}
case pizza(Size)
case hamburger(Int)
case onigiri(Size,Int)
}
let food1 = Food.pizza(.L)
let food2 = Food.pizza(.M)
let food3 = Food.hamburger(3)
let food4 = Food.pizza(.M)
print(food1 == food2) // -> false
print(food1 == food3) // -> false
print(food2 == food4) // -> true
associated value へのアクセス(switch)
Associated value を指定するときは、コンストラクタで指定するためわかりやすいと思います。
let food1 = Food.pizza(.L)
Associated value へのアクセスは、以下のようになります。
food を受け取り1人分の食事として多すぎないかをチェックする関数を以下のように定義しました。
func checkTooMuch(food:Food) -> Bool{
switch food {
case .pizza(let size): // pizza の size 情報を受け取る
return size == .L
case .hamburger(let num): // hamburger の num 情報を受け取る
return num >= 3
case .onigiri(let size,let num): // onigiri の size と num を受け取る
switch size {
case .L:
return num > 1
default:
return num > 2
}
}
}
上記のように switch 文で受け取る際に、 associated value を変数に受け取ることができます。
switch 文で enum を使って分岐するときには、全ケースを列挙するか default を指定しないとコンパイルエラーになります。
この仕様は思わぬエラーを防止するために、非常に便利なのですが、すでに どの enum であるかの前提があり、associated value を受け取りたいだけの時には、少し面倒です。
associated value は、次のような形でもアクセスすることができます。
associated value へのアクセス (条件節)
以下のように書くことで、すべてのケースを網羅することなく、特定の enum 値であることを確認してアクセスすることができます。
以下は、先の switch 文で記述した関数と同じ動作をする関数です。
func checkTooMuch(food:Food) -> Bool{
if case Food.pizza(let size) = food { // pizza の size を取得
return size == Food.Size.L
}
if case Food.hamburger(let num) = food, // hamburger の num を取得
num >= 3 {
return true
}
if case Food.onigiri(let size, let num) = food { // onigiri の size と num を取得
switch size {
case .L:
return num > 1
default:
return num > 2
}
}
return false
}
上記は、if 文で書いている例になっていますが、guard でも同様に使用することができます。
guard の条件節に記述した変数定義は、guard 内で使用することはできません。
Swift おすすめ本
Swift を深く理解するには、以下の本がおすすめです。
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
Sponsor Link