【Swift】列挙型について

###1.はじめに
---
Swiftにはまだ紹介していない、`列挙型`**という非常に重要な記法**があります。
`列挙型`は利用すれば大きな効果を発揮する場面が非常に多いので、特にSwift初心者の方はぜひ私の記事を見ていってください。
なお列挙型には`共用型の列挙型`というものもありますが、こちらについては次回解説したいと思います。


###2.列挙型とは
---
Swiftでの`列挙型`ではそれ独自のメソッドを定義できるなど、C言語での`列挙型`に比べるとかなり拡張されたものになっています。
以下にシンプルな例を示します。

```Swift:
enum Direction {
    case up
    case down
    case right
    case left
}
```

これで`列挙型Direction`を定義することができました。
upやdownなどは`列挙ケース`(`ケース`)と呼びます。ケース名は小文字開始のキャメルケースで記述するのが通例です。

また先ほどの例は次のように書くこともできます。

```Swift:
enum Direction {
    case up, down, right, left
}
```

ここで宣言した`列挙ケース`は変数や定数に代入できます。
ケース名はグローバルではないため、列挙型の名前と「.」を使って記述できます。
ただし使われる列挙型が明らかな場合は列挙型名を省略することができます。
つまり**ケース名が単独で使われることはないため、他の列挙型のケース名と重複しても問題はありません**。例を示します。

```Swift:
let d = Direction.up          
var x : Direction = .right    // 型が決まっていれば"."から記述できる
d == x                        // これらは違う値(false)
```

###3.メソッドの定義
---
C言語と違いSwiftでは`列挙型`の定義にメソッドを含めて、型全体としての動作や機能を表すことができます。
たとえば、先ほど挙げた例の４つの方向を示す`列挙型`で、それぞれの方向から時計回りで90°開店した向きを知りたいとします。これは次のようなメソッドで定義できます。

```Swift:
enum Direction {
    case up, down, right, left
    func clockwise() -> Direction {
        switch self {
            case .up:    return .right
            case .down:  return .left
            case .right: return .down
            case .left:  return .up
        }
    }
}
```

たとえばこのメソッドは次のように使うことができます。

```Swift:
let d = Direction.up
d.clockwise() == Direction.down              // false
d.clockwise().clockwise() == Direction.down  // true
```

このように、`列挙型`のケースとそれに依存した手続きを一体のものとして定義することができます。

###4.値型の列挙型
---
Swiftの`列挙型`には、ここまでで説明してきた`シンプルな列挙型`に加えて、**全ケースが同じデータ型の何らかの値を持つようになっている「値型」**があります。
`値型の列挙型`では、すべての`列挙ケース`が、同じ型の互いに異なる値を持つように定義できます。
値の型を、その`列挙型`の`実体型`呼び、各ケースに割り当てられた値を`実体値`と呼びます。
新しく定義する型名の後に、「:」と`実体型`を記述します。
それぞれのケースには値を記述することができますが、ここで記述できるのは整数、実数、Bool値、または文字列リテラルだけです。
従って実体型になることができるのは、**これらの値で初期化ができる型**ということになります。次の概要を確認してください。

```
enum 型名：実体型 {
    case ケース名 = リテラル
    case ケース名 = リテラル
    ... ...
```

`実体型`が整数の場合、リテラルで値を指定しなかったケースは、1つ前のケースの値に1を加えたものになります。
先頭のケースにリテラルがない場合、値は0となります。
値は自由に指定できますが、同じ値を持つケースがないようにしなければなりません。

`実体型`が文字列で、リテラルを指定しない場合、ケース名の文字列が`実体型`の値となります。
整数、文字列以外の型が`実体型`の場合、各ケースに対して必ずリテラルを記述し、それぞれが異なる値を持つようにしなければなりません。

`列挙型`とその`実体型`の間で、型を相互に変換することができます。
次の例を確認してください。以前例で取り上げたDirection型を、Int型を実体型とするように定義します。

```Swift:
enum Direction : Int {
    case up = 0, down, right, left
}
```

最初のupの`実体値`を0にしたので、それ以降のdown、right、leftはそれぞれ1、2、3の値を持つことになります。
このような`列挙型`のインスタンスをいくつか生成したとして、それぞれが持つ実体値を取り出すために、rawValueというプロパティを使うことができます。

```Swift:
let a = Direction.right
let i = a.rawValue                // i = 2(Int)
let k = Direction.down.rawValue   // k = 1(Int)
```

逆に、`実体型`の値からそれに対応する`列挙型`のインスタンスを得るには、イニシャライザinit?(rawValue:)を使います。
`実体型`の値に対応する`列挙ケース`が存在しない場合には正しい値が返せないので、このイニシャライザは`失敗のあるイニシャライザ`です。以下の例を見てください。

```Swift:
let b : Direction? = Direction(rawValue:3)
b! == Direction.left                  // true
if let c = Direction(rawValue:2) {    // オプショナル束縛構文
    print("OK \(c.rawValue)")         // "OK 2"
}
```

値型の`列挙型`を使って、先ほど書いた概要を書き直してみます。
方向を表すケースを時計回りの順に定義すれば、switch文ですべてを列挙する必要はなくなります。
関数の中でインスタンス自体を表す必要がありますが、このためには予約語のselfを使います。例を確認してください。

```Swift:
enum Direction : Int {
    case up = 0, right, down, left       // 時計回りに並べ直す
    func clockwise() -> Direction {
        let t = (self.rawValue + 1) % 4  // selfはインスタンス自体
        return Direction(rawValue:t)!    // nilにはならない
```

###5.列挙型に対するメソッドとプロパティ
---
`列挙型`にもプロパティは定義できますが、構造体と異なり、**定義できるのは計算型のプロパティだけ**です。１つ例を挙げます。

```Swift:
enum Direction : Int {
    case up = 0, right, down, left  // 時計回り
    /* ... 中略 ... */
    var horizontal: Bool {          // get節のみのプロパティ定義
        switch self {
        case .right, .left: return true
        default: return false
        }
    }
    mutating func turnBack() {
        self = Direction(rawValue:((self.rawValue + 2) % 4))!
    }
}
```

ここまでで例として挙げてきたDirection型について、値が水平方向、右か左を表している場合はtrueを返す計算型のプロパティhorizontalを定義しています。
この例ではさらに、`列挙型`に対してmutating属性を持つメソッドを定義する例も示しています。

構造体とは異なり、自分自身を表す`self`に別の値を代入することで、変数に格納されている`列挙型自体の値`を変更します。
値が定数に格納されている場合には利用できません。
ここではDirection型に、正反対の方向に反転するメソッドturnBackを追加しています。

実行例を見てみましょう。

```Swift:
var d = Direction.left
print(d.rawValue)    // 3が出力される
d.turnBack()
print(d.rawValue)    // 1が出力される(3の反対方向)
print(d.horizontal)  // trueが出力される
```

###6.列挙型のイニシャライザとタイプメソッド
---
`列挙型`にも、構造体と同様にタイプメソッド、タイププロパティ、イニシャライザを定義することができます。
タイプメソッドとタイプメソッドは、関数定義、あるいは変数定義の先頭にキーワード`static`を記述します。
イニシャライザでは、`self`に何を代入するのかを定義することになります。
自動的に用意されるイニシャライザinit?(rawValue:)を再定義することも可能です。以下の例を確認してください。

```Swift:
enum Direction : Int {
    case up = 0, right, down, left
    static var defaultDirection = Direction.up
    init() {
        self = Direction.defaultDirection
    }
    // ... 中略 ...
}
```

ここではdefaultDirectionという変数を用意し、イニシャライザinit()を使ってインスタンスを生成した際には、この値を初期値とするようにしています。

実行例を示します。

```Swift:
Direction.defaultDirection = .right    // 右向きを初期値にする
let e = Direction()                    // イニシャライザを使う
let g = Direction(rawValue:1)          // 値型の列挙型のイニシャライザ
print(e == g)                          // true
```

###7.おわりに
---
今回はSwiftにおいて非常に重要な記法である、`列挙型`について解説しました。
次回は`共用型の列挙型`について記事を書きますので、来週記事がUPされましたらそちらもぜひご覧ください。
ここまで読んでくださった方ありがとうございました。