忘備録-Swiftのパターン

趣味でIOSアプリ開発をかじっていた自分が、改めてSwiftを勉強し始めた際に、曖昧に理解していたところや知らなかったところをメモしています。

## 参考文献

この記事は以下の書籍の情報を参考にして執筆しました。

- [詳解 Swift 第5版](https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3Swift-%E7%AC%AC5%E7%89%88-%E8%8D%BB%E5%8E%9F-%E5%89%9B%E5%BF%97/dp/481560407X)

##switch文でwhere節を使う

```swift
func trashDate(weekly: Int, weekday: String){
  let week = (weekly, weekday)    // タプル型
  switch week {
  case (_, "月曜日"):
    print("燃えるゴミの日")
  case (let i, "木曜日") where i == 1 || i == 3 :    // where節の条件に一致する場合処理を実行
    print("燃えないごみの日")
  case let (i, j):
    print("第\(i)週 \(j)")
  }
}
trashDate(weekly: 4, weekday: "月曜日")    // 燃えるゴミの日
trashDate(weekly: 2, weekday: "木曜日")    // 第2週 木曜日
trashDate(weekly: 1, weekday: "木曜日")    // 燃えないごみの日
trashDate(weekly: 99, weekday: "hoge")    // 第99週 hoge
```

##switch文でオプショナル型を使う

```swift

func  ageConfirmation(people: (String, Int?)) {
  switch people {
  case let (name, age?) where age >= 20:
    print("\(name),成人済み")
  case let (name, age?) where age < 20 && age >= 0:
    print("\(name),未成年")
  case (let name, nil) :
    print("\(name),年齢不詳")
  default:
    break
  }
}
ageConfirmation(people: ("hoge", 20))    //hoge,成人済み
ageConfirmation(people: ("fuga", 8))    //fuga,未成年
ageConfirmation(people: ("foo", nil))    //foo,年齢不詳
ageConfirmation(people: ("bar", -8))
```

##列挙型のメソッド

```swift
enum Coin {
  case front, back
  func reverse() -> Coin{
    switch self {
    case .front:
      return .back
    case .back:
      return .front
    }
  }
}

let coin = Coin.back
print(coin)    // back
print(coin.reverse())    // front

```

##値型の列挙型

```swift
enum Coin: Int {
  case front = 0, back    //backは1になる
  func reverse() -> Coin{
    switch self {
    case .front:
      return .back
    case .back:
      return .front
    }
  }
}
let coin = Coin.back
print(coin)    // back
print(coin.rawValue)    // 1
print(coin.reverse().rawValue)    //0
```


##列挙型のケースを全て含むコレクション

```swift
enum Coin: Int, CaseIterable {
  case front = 0, back    //backは1になる
}

print(Coin.allCases)    // [CodeTest.Coin.front, CodeTest.Coin.back]
for elm in Coin.allCases{
  print(elm)    // front back
}
```

##if-case文

```swift
let p = Payment.クレジット(120, true)

// pがクレジットカードかを知りたい
if p == .クレジット{    // error
  print("クレカ")
}

//  switch文でも書けるが少し面倒
switch p {
case .クレジット:
  print("クレカ")
default:
  break
}

//  if-caseを使う
if case .クレジット = p {
  print("クレカ")
}

```

##for-in文でcaseパターンを使う場合

```swift
enum Payment {
  case クレジット(_ 残高:Int, _ 使用可能: Bool)
  case アプリ(_ 残高: Int, _ 還元率: Double = 0.02)
  case 現金
}

let paylist: [Payment] = [.クレジット(120, true), .アプリ(1120),
                              .現金, .クレジット(12000, true), .クレジット(99999, false),
                              .クレジット(1200, true)]
    
// 1100円以上の残高があり、使用可能なクレジットカードを取り出す
for case let .クレジット(money, b) in paylist where b == true && money >= 1100 {
  print(money)
}

// このように書くのと同じ
for t in paylist {
  switch t {
    case let .クレジット(money, b):
      if b == true && money >= 1100{
        print(money)
      }
    default:
      break
  }
}
```



##再帰的な列挙型

```swift
// 再帰的に呼び出すcaseの前にindirectをつける
enum メッセージ : CustomStringConvertible{    // プロトコルを採用
  case 文書(_ 差出人: String, _ 文章: String)
  case 添付(_ 差出人: String, _ データ: String)
  indirect case 転送(_ 差出人: String, メッセージ)
  var description: String{
    switch self {
    case let .文書(from, str):
      return from + " : " + str
    case let .添付(from, data):
      return from + " : " + data
    case let .転送(from, message):
      return from + " : " + " <- \(message)"
    }
  }
}

// XcodeでFIXしたとき
indirect enum メッセージ : CustomStringConvertible{    // プロトコルを採用
  case 文書(_ 差出人: String, _ 文章: String)
  case 添付(_ 差出人: String, _ データ: String)
  case 転送(_ 差出人: String, メッセージ)
  var description: String{
    switch self {
    case let .文書(from, str):
      return from + " : " + str
    case let .添付(from, data):
      return from + " : " + data
    case let .転送(from, message):
      return from + " : " + " <- \(message)"
    }
  }
}
let m1 = メッセージ.文書("hoge", "Hello")
let m2 = メッセージ.転送("fuga", m1)
let m3 = メッセージ.転送("foo", m2)
print(m1)    // hoge : Hello
print(m2)    // fuga :  <- hoge : Hello
print(m3)    // foo :  <- fuga :  <- hoge : Hello
```

##パターンマッチ演算子
Swiftのパターンマッチ : 
スクリプト言語などが持っている文字列と正規表現のマッチングではない。 
どの構造が何に対応するかに関してのルールに基づくデータ構造の対応づけ

~= : パターンマッチ演算子

```swift
let hoge = Coin.front
print(hoge ~= Coin.front)    // true
```

パターンマッチ演算子のルールを追加
ただし、パターンマッチ演算子の新たな定義を追加すると、switchやif-case文の挙動に直接影響するので必要性について考えること。

```swift
enum Coin: Int {
  case front, back
  func reverse() -> Coin{
    switch self {
    case .front:
      return .back
    case .back:
      return .front
    }
  }
}
// パターンマッチ演算子の定義を追加
func ~= (c: Coin, s: String) -> Bool{
  "\(c)" == s
}
let coin = Coin.front
print(coin ~= "front")    // true
print(coin.reverse() ~= "front")    // false
```