Swiftのenumの使い方を学んでみた

#この記事について

[THE SWIFT PROGRAMMING LANGUAGE SWIFT5.5](https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html)を参考・引用しております。

間違っていたら指摘お願いします。

# enumの基礎編

### 書き方①

```swift:enumの基本的な書き方
enum CompassPoint { 
    case north
    case south
    case east
    case west
}

var directionToHead = CompassPoint.west 
directionToHead = .east
```

enum（列挙型）は、関連する値のグループに共通の型を定義し、コード内で型安全な方法でこれらの値を扱うことができます。

上記の例で言うと、
`north`,`south`,`east`,`west`の値を`CompassPoint`という任意の共通の型で定義し、グループ化しています。

それにより、
`CompassPoint.west`という書き方ができるようになります。

私は、「CompassPointグループのwest君」みたいな覚え方をしております。

`var directionToHead = CompassPoint.west `
変数にenumで定義した型の値を代入すると、変数はその型を覚えてくれます。

`directionToHead = .east`
それにより、次に同じ型の値を代入する際は、省略した書き方をすることができます。


### 書き方②

```swift:1つのcaseに複数の値を設定した書き方
enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune //1行で表示もできる
}
```
「enumの書き方①」では、1つのcaseに1つの値を設定していましたが、実は1つのcaseに複数の値を設定することができます。

# enumの応用篇

### switchとenumで値をマッチングさせる
```swift:switchとeunmを使用した書き方
directionToHead = .south

switch directionToHead { 
case .north:
    print("Lots of planets have a north")
case .south:
    print("Watch out for penguins")
case .east:
    print("Where the sun rises")
case .west:
    print("Where the skies are blue")
default:
    print("Not a safe place for humans")
}
```

switchとenumを組み合わせることで、enumの値に合わせて処理を実行することができます。

値がない場合は、`default`内の処理が実行されます。

### enumにCaseIterableプロトコルを実装し、値をプロパティーとして扱う
```swift:enumとプロトコルを使用した書き方
enum Beverage: CaseIterable { 
    case coffee,tea,juice
}

let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")

for beverage in Beverage.allCases {
    print(beverage)
}
```

上記では、enumに`CaseIterable`プロトコルを実装させ、caseの値をプロパティーとして扱えるようにしています。

プロパティーと扱えることにより、
`allCases.count`でcaseの数を数えたり、
`for beverage in Beverage.allCases`で、caseの値をループで出力できるようになります。

### enumに代入した値を再利用

```swift

enum Barcode {
    case upc(Int,Int,Int,Int)
    case qrCode(String)
}

var productBarcode = Barcode.upc(8, 85909,51226,3)

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
    print("QR code: \(productCode).")
}
```

`case upc(Int,Int,Int,Int)`
`case qrCode(String)`
enumのcaseでは上記の書き方ができます。
これは、各caseの値の型を定義しております。

「Barcodeグループのupc君がint型の値を4つ保持できる」と覚えると良いでしょう


`var productBarcode = Barcode.upc(8, 85909,51226,3)`
上記のように値を代入することができます。

enum、定義済みのcase、switchを使うことで、
enumに代入した値をswitchのcaseの処理内で再利用することができます。

# enum応用篇②

### 生の値を定義

```swift:生の値を定義する

//明示的に生の値を定義する
enum ASCIIControlCharcter: Character { 
    case tab = "\t"
    case lineFeed = "\n"
    case carriageReturn = "\r"
}

//暗黙的に生の値を定義する
enum PlanetA: Int {
    case mercury = 1, venus, earth, mars,jupiter,saturn,uranus, neptune 
//venusは2になる。
//earthは3になる
}
```

ややこしいですが、enum（列挙型）は、関連する値のグループに共通の型を定義する以外にも他のことができます。

それは、「生の値」を定義することです。

上記の例だとCharacter型を使って
値に`tab`を、
生の値に`"\t"`を定義しています。

Int型を使えば、暗黙的に生の値を定義することができます。

「生の値」を定義する際には、他のcaseの生の値も全て同じ型である必要があります。


```swift:生の値から値を引っ張り出し、マッチング
let positionToFind = 11
if let somePlanet = PlanetA(rawValue: positionToFind) { //オプショナルを外している
    switch somePlanet {
    case .earth:
        print("Mostly harmless")
    default:
        print("Not a safe place for humans")
    }
} else {
    print("There isn't a planet at position \(positionToFind)")
}

```
値と生の値の関係性について、
連想配列のキーと値の関係性に似ているような感じがします。


`rawValue`で生の値を定義することで、値を引っ張り出すことができます。ただし、オプショナル型なので`if let`などでアンラップする必要があります。

### 再帰的なenum

```swift:indirectでcaseが再帰的であると明示する
enum ArithmeticExpression {
    case number(Int)
    indirect case addtion(ArithmeticExpression,ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression,ArithmeticExpression)
}
```
再帰的な列挙（enum）とは、1つ以上の列挙ケースの関連する値として、その列挙の別のインスタンスを持つ列挙のことです。列挙ケースの前に indirect と書くことで、再帰的であることを示し、コンパイラに必要なインダイレクトの層を挿入するように指示します。


```swift:すべてのcaseが再帰的であると明示する
indirect enum ArithmeticExpressionA {
    case number(Int)//普通の数字
    case addition(ArithmeticExpressionA, ArithmeticExpressionA) //加算
    case multiplication(ArithmeticExpressionA, ArithmeticExpressionA) //乗算
}
```
enumの前にindirectを書くことで、すべてのcaseが再帰的であると明示することができます。

```swift:再帰的なenumの使用例
//ArithmeticExpressionAの値を再帰的に利用する
let five = ArithmeticExpressionA.number(5)
let four = ArithmeticExpressionA.number(4)
let sum = ArithmeticExpressionA.addition(five, four)　
let product = ArithmeticExpressionA.multiplication(sum, ArithmeticExpressionA.number(2))

// switchで値をアンラップする
func evaluate(_ expression: ArithmeticExpression) -> Int {
    switch expression {
    case let .number(value):
        return value
    case let .addition(left, right):
        return evaluate(left) + evaluate(right)
    case let .multiplication(left, right):
        return evaluate(left) * evaluate(right)
    }
}
print(evaluate(product))
// Prints "18"
```
上記の処理だと、
変数`sum`が同じenumである`five`と`four`を持っています。
さらに、変数`product`は、同じenumである`sum`と`ArithmeticExpressionA.number(2)`を持っています。

このように、enumが同じenumを持つことを再帰的なenumと呼ばれています。