Swift 2のパターンマッチ構文集(ほぼ翻訳)

[Match Me if you can: Swift Pattern Matching in Detail.](http://appventure.me/2015/08/20/swift-pattern-matching-in-detail/)が良記事で、ちょうど僕もSwift2のキャッチアップ中だったので、写経しながら翻訳ぽい記事を書きました。(翻訳許可もいただいています)

冒頭のイントロ的部分は飛ばして、[Advanced Pattern Matching](http://appventure.me/2015/08/20/swift-pattern-matching-in-detail/#sec-3)から書きます。

# 様々なパターンマッチング

7つのパターンを紹介します。
`switch`だけではなく`if`・`guard`・`for`を組み合わせて表現します。


## 1. ワイルドカードパターン

ワイルドカードパターンは、マッチした値を無視します。
`let _ = fn()`の`_`と同様です。
単に`_`で受けると`nil`を含む全ての値にマッチしてしまうので、オプショナルの値をマッチするには、`_?`として区別します。


```swift
let p: String? = nil
switch p {
case _?: print("Has String")
case nil: print("No String")
}
```

`enum`および`tuple`を受けることが出来ます。


```swift
switch (15, "example", 3.14) {
case (_, _, let pi): print("pi: \(pi)")
}
```

## 2. Identifierパターン

具体的な値をマッチさせます。

```swift
switch 5 {
case 5: print("5")
}
```

## 3. 値バインディングパターン

`let`・`var`で値を受けるものと同じで、その省略記法です。


```swift
switch (4, 5) {
case let(x, y): print("\(x) \(y)")
}
```

## 4. Tupleパターン

以下の例ではこのような処理になります。

1. ageを展開
2. jobが非nilである時にマッチさせるが値自体は不要
3. payloadはNSDictionary型であるが、同様に値自体は不要



```swift
let age = 23
let job: String? = "Operator"
let payload: AnyObject = NSDictionary()

switch (age, job, payload) {
case (let age, _?, _ as NSDictionary):
    print(age)
default:
    break
}
```

## 5. 列挙型caseパターン

パターンマッチは、`enum`の扱いに優れています。
下記のように、ステートが短い箇所では`struct`の代用にもなります。


```swift
enum Entities {
    case Soldier(x: Int, y: Int)
    case Tank(x: Int, y: Int)
    case Player(x: Int, y: Int)
}

for e in [Entities.Player(x: 1, y: 2), Entities.Tank(x: 3, y: 4)] {
    switch e {
    case let .Soldier(x, y):
        print("soldier, x: \(x), y: \(y)" )
    case let .Tank(x, y):
        print("tank, x: \(x), y: \(y)" )
    case let .Player(x, y):
        print("player, x: \(x), y: \(y)" )
    }
}
```

## 6. タイプキャストパターン

`is`では型判定のみでキャスト結果は無視されます。
`as`で変数に割り当てることで、キャスト結果の参照が可能となります。

```swift
let a: Any = 5
switch a {
case is Int: print(a + 1) // コンパイルエラー(aはAnyのまま)
case let n as Int: print(n + 1) // nで受けつつキャストしているのでコンパイル通る
default: break
}
```

## 7. エクスプレッションパターン

値を`~=`で受けてマッチします。



```swift
switch 5 {
case 0...10: print("In range 0-10")
default: break
}
```

(訳注: 以下のような比較方法を今回初めて知りました)

```swift
0...10 ~= 5 // true
```

`~=`をオーバーライドすれば、下記のようなstructのマッチも可能となります。

```swift
struct Soldier {
    let hp: Int
    let x: Int
    let y: Int
}

func ~= (pattern: Int, value: Soldier) -> Bool {
    return pattern == value.hp
}

let soldier = Soldier(hp: 99, x: 10, y: 10)
switch soldier {
case 0: print("dead soldier")
default: break
}
```

[Custom pattern matching in Swift · Episteme and Techne](http://austinzheng.com/2014/12/17/custom-pattern-matching/)などにさらに詳しく書いてあります。

# switch文のFallthrough・Break・Label

swiftの`switch`はデフォルトでは`fallthrough`しないので注意です。
逆に、`break`を書かずともbreakしてくれます。
`fallthrough`させたい場合は明示的に書く必要があります。

```swift
switch 5 {
case 5:
    print("Is 5")
    fallthrough
default:
    print("Is a number")
}
// Will print: "Is 5" "Is a number"
```

このように早期returnぽくcase式を抜けたい時に、`break`すると良いですね。

```swift
func getSystemUser(userId: Int) -> String? { return "hoge" }
func insertIntoRemoteDB(userData: String) { print(userData) }

let userType = "system"
let userID = 10
switch (userType, userID)  {
case ("system", _):
    guard let userData = getSystemUser(userID) else { break }
    print("user info: \(userData)")
    insertIntoRemoteDB(userData)
default: ()
}
```

また、このように`gameLoop`というlabelを付けておくと、switch文の中で、whileループを抜けられたり、その継続を出来たりします。


```swift
func state() -> State { return .GameOver }
func calculateNextState() { "calculateNextState" }

gameLoop: while true {
    switch state() {
    case .Waiting: continue gameLoop
    case .Done: calculateNextState()
    case .GameOver: break gameLoop
    }
}
```

# 活用例

## オプショナル

[オプショナルをアンラップするのは色々な方法があります](http://appventure.me/2014/06/13/swift-optionals-made-simple/)が、そのうちの一つがパターンマッチを利用したものです。


```swift
func secretMethod() -> String? { return "secret" }

// Swift 2より前
var result: String? = secretMethod()
switch result {
case .None:
    print("is nothing")
case let a:
    print("\(a) is a value") // -> "Optional(secret is a value)"
}

// Swift 2から可能な書き方
switch result {
case nil:
    print("is nothing")
case let a?:
    print("\(a) is a value") // -> "secret is a value"
}
```

## タイプマッチ

Swiftで型が強くなりましたが、Objective-CのAPIなど使っていると、こういう型が混ざったNSArrayなどを受け取ることがあります。

```swift
let u = NSArray(array: [NSString(string: "String1"), NSNumber(int: 20), NSNumber(int: 40)])
```

それをこのように型でマッチしつつハンドリングできます。


```swift
for x in u {
    switch x {
    case is NSString:
    print("string")
    case is NSNumber:
    print("number")
    default:
    print("Unknown types")
    }
}
```

## グレード分けをrangeで 

0から100点のスコアをrangeでのマッチを用いてグレードに分ける例です。

```swift
let aGrade = 84

switch aGrade {
case 90...100: print("A")
case 80...90: print("B")
case 70...80: print("C")
case 60...70: print("D")
case 0...60: print("F")
default:
    print("Incorrect Grade")
}
```

## 単語を使用数に応じてフィルター

使用数が3より多いものをフィルターする処理の例です。
`map`と`filter`で処理出来ます。

```swift
let wordFreqs = [("k", 5), ("a", 7), ("b", 3)]

let res = wordFreqs.filter({ (e) -> Bool in
    if e.1 > 3 {
        return true
    } else {
        return false
    }
}).map { $0.0 }
print(res) // ["k", "a"]
```

さらに、`flatmap`を使うと非nilの要素を返してくれるので、これだけで良いです。

```swift
let res = wordFreqs.flatMap { (e) -> String? in
    switch e {
    case let (s, t) where t > 3: return s
    default: return nil
    }
}
print(res)
```

## フォルダ探索

パターンマッチを活用して、特定の名前・拡張子のファイルをフィルターして処理する例です。

```swift
guard let enumerator = NSFileManager.defaultManager().enumeratorAtPath("/customers/2014/")
else { return }

for url in enumerator {
    switch (url.pathComponents, url.pathExtension) {

    // psd files from customer1, customer2
    case (let f, "psd") 
        where f.contains("customer1") 
        || f.contains("customer2"): print(url)

    // blend files from customer2
    case (let f, "blend") where f.contains("customer2"): print(url)

    // all jpg files
    case (_, "jpg"): print(url)

    default: ()
    }
}
```

## フィボナッチ数列

再帰処理をswitch-case・パターンマッチによって実現しています。

```swift
func fibonacci(i: Int) -> Int {
    switch(i) {
    case let n where n <= 0: return 0
    case 1: return 1
    case let n: return fibonacci(n - 1) + fibonacci(n - 2)
    }
}

print(fibonacci(8))
```

## レガシーAPIの値展開

こういう型の緩いレガシーAPIを扱わなくてはいけなくなった時の例です。

```swift
func legacyAPI(id: Int) -> [String: AnyObject] {
    return ["type": "system", "department": "Dark Arts", "age": 57,
        "name": ["voldemort", "Tom", "Marvolo", "Riddle"]]
}
```

このようにマッチする型の値を受け取りつつ、処理出来ます。


```swift
func createSystemUser(name: String, dep: String) {}

let item = legacyAPI(4)
switch (item["type"], item["department"], item["age"], item["name"]) {
case let (sys as String, dep as String, age as Int, name as [String]) where
    age < 1980 &&
        sys == "system":
    createSystemUser(name.count == 2 ? name.last! : name.first!, dep: dep ?? "Corp")
default:()
}
```


# if・for・guardを使ったパターン

Swiftのドキュメントは、全てのパターンは、if・for・guardで表現可能と言及しています。
これは、値バインディング・タプル・タイプキャストパターンをif・for・guardで記述した例です。


```swift
// This is just a collection of keywords that compiles. This code makes no sense
func valueTupleType(a: (Int, Any)) -> Bool {
    // guard case Example
    guard case let (x, _ as String) = a else { return false}
    print(x)
    
    // for case example
    for case let (a, _ as String) in [a] {
        print(a)
    }
    
    // if case example
    if case let (x, _ as String) = a {
        print("if", x)
    }
    
    // switch case example
    switch a {
    case let (a, _ as String):
        print(a)
        return true
    default: return false
    }
}
let u: Any = "a"
let b: Any = 5
print(valueTupleType((5, u)))
print(valueTupleType((5, b)))
// 5, 5, "if 5", 5, true, false
```

# for case文

Swift 2で、switchの表現力が上がったために、パターンマッチングはより大事になりました。

例えば、オプショナル型の配列をフィルタして非オプショナル型の配列として返す関数はこう書けます。

```swift
func nonnil<T>(array: [T?]) -> [T] {
    var result: [T] = []
    for case let x? in array {
        result.append(x)
    }
    return result
}

print(nonnil(["a", nil, "b", "c", nil]))
```

このように要素の値を受け取りつつさらにその値を判定して処理するコードが、こう簡潔に書けます。

```swift
enum Entity {
    enum EntityType {
        case Soldier
        case Player
    }
    case Entry(type: EntityType, x: Int, y: Int, hp: Int)
}

for case let Entity.Entry(t, x, y, _) in [Entity.Entry(type: .Player, x: 1, y: 2, hp: 3)]
    where x > 0 && y > 0 {
        print("t: \(t), x: \(x), y: \(y)")
}
```

hpが0より多いSoldierがまだ残っていればゲームオーバーじゃないと判定するコードの例です。

```swift
func gameOver() -> Bool {
    for case Entity.Entry(.Soldier, _, _, let hp) in [Entity.Entry(type: .Soldier, x: 1, y: 2, hp: 30)]
        where hp > 0 {return false}
    return true
}
print(gameOver()) // false
```

# guard case文

まずはguard単体の簡単な使用例です。

```swift
func example(a: String?) {
    guard let a = a else { return }
    print(a) // このスコープでは、aが非オプショナルのString
}
example("yes")
```

guard caseでパターンマッチしつつ条件外の時はゼロを返して、それ以降のスコープではマッチした値を使って処理する例です。

```swift
let MAX_HP = 100

func healthHP(entity: Entity) -> Int {
    guard case let Entity.Entry(.Player, _, _, hp) = entity 
    where hp < MAX_HP 
    else { return 0 }
    return MAX_HP - hp
}

print("Soldier", healthHP(Entity.Entry(type: .Soldier, x: 10, y: 10, hp: 79)))
print("Player", healthHP(Entity.Entry(type: .Player, x: 10, y: 10, hp: 57)))

// Prints:
"Soldier 0"
"Player 43"
```

# if case文

guardと似てますが、その逆にif caseで囲ったスコープでマッチされた値を使って処理します。

```swift
func move(entity: Entity, xd: Int, yd: Int) -> Entity {
    if case Entity.Entry(let t, let x, let y, let hp) = entity
    where (x + xd) < 1000 &&
        (y + yd) < 1000 {
    return Entity.Entry(type: t, x: (x + xd), y: (y + yd), hp: hp)
    }
    return entity
}
print(move(Entity.Entry(type: .Soldier, x: 10, y: 10, hp: 79), xd: 30, yd: 500))
// prints: Entry(main.Entity.EntityType.Soldier, 40, 510, 79)
```

# 制限

エクスプレッションパターン(`~=`で受けるやつ)はtupleに対して使えません。

また、`Scala`の`unapply`に対応するものもありません。
unapplyとは、`init`の逆に、`init`で受ける引数を返す定義をし、それをクラスのマッチに使える、というものです。

現状コンパイルエラーになりますが、擬似コードを書くと、こういうイメージです。

```swift
struct Imaginary {
   let x: Int
   let y: Int
   func unapply() -> (Int, Int) {
     // implementing this method would then in theory provide all the details needed to destructure the vars
     return (self.x, self.y)
   }
}
// this, then, would unapply automatically and then match
guard case let Imaginary(x, y) = anImaginaryObject else { break }
```

---

# 関連

- [Swift 2: Control Flow Pattern Matching Examples · Episteme and Techne](http://austinzheng.com/2015/09/23/pmatch-control-flow/)
    - こちらも似た内容の良い記事でした

# 最後に

Swift 2で特にパターンマッチ周りの言語仕様が増えて追い切れていませんでしたが、本記事書く過程で、かなり身に付きました。
特に、今一人iOSアプリ開発なので、コードレビューで人のコード見たり指摘されたりする機会が無いので、こういうキャッチアップ大事だなと改めて思いました。
良い感じに活用して、簡潔かつ意図の分かりやすいコードを書くように努めたいです(　´･‿･｀)