【Swift】タプルを用いたswitch文

###1.はじめに
---
今回はSwitch文の条件分岐にタプルの値を使う、通常とは少し違った使い方を紹介しようと思います。場面によっては有効な使い方だと思うので、ぜひご覧ください。

###2.タプルをcaseで使う
---
まず、タプルを定数のように考えてcaseラベルに置くようにすれば、次のように書くことができます。
ただし、タプルの型はすべて統一されている必要があり、この例では、変数dayが月と日からなる日付を表すタプルを持っていると考えます。
３つだけ書かれているいずれかの祝日に一致すれば表示されます。

```Swift:
switch day {
case (1,1): print("元旦")
case (2,11): print("建国記念の日")
case (5,3): print("憲法記念日")
default: break
}
```

ある範囲の日付を表すために、範囲演算子が使えます。例えば次の例を確認してください。

```Swift:
switch day {
case (1,1...5): // 1/1 ~ 1/5
    print("正月休み")

case (5,3): print("憲法記念日")

case (4,29), (5,2...6):
    print("連休")

default: break
}
```

値を列挙するだけでなく、タプルの要素を取り出して処理に使うこともできます。
このために、caseラベル内にlet、またはvarを記述して、case節の内部でだけ有効な定数、変数を宣言できます。

```Swift:
switch day {
case (5,3): print("憲法記念日")
case (8, let d): print("8/\(d)は夏休みです")
default: break
}
```
この例では、８月の日付ならいつでも夏休みになります。変数dを使う必要がなければ、「_」を使い次のように記述することもできます。

```Swift:
case (8, _): print("夏休み")
```

なお、キーワード付きのタプルをswitch文で使用することもできますが、値とcaseラベルで使っているキーワードが一致しない場合にはエラーになります。
タプルとcaseのラベルの対応付けのように、構造と値の一致を調べる処理一般を、Swiftでは**パターンマッチング**といいます。

###3.switch文でwhere節を使う
---
switch文で、タプルの要素に対して条件を付けることもできます。ここで、日付から曜日を計算する関数dayOfWeekを利用します。この関数は日曜なら０、月曜なら１、というように曜日を整数値で返します。なお、変数yearが西暦年を持つとします。
caseラベルに条件を付ける場合、ラベルと「:」の間に、予約語whereと条件を記述します。以下の例を確認してください。

```Swift:
switch day {
case (1, let d) where d >= 8 && d <= 14 && dayOfWeek(year,1,d) == 1:
    print("成人の日")

case (8, _): print("夏休み")

case (let m, let d) where dayOfWeek(year,m,d) == 0:
    print("\(m)/\(d)は日曜日")

default: break
}
```

ここでは、１月の第２月曜日が成人の日であることを条件にしています。
同様に、１年のいつでも日曜日であれば表示するようにしています。
このcaseラベルのように、タプルのすべての要素を定数に割り当てる場合、要素ごとにletを指定することもできますが、タプルの前に１つだけletを記述することもできます。

```Swift:
case let (m, d) where dayOfWeek(year,m,d) == 0:
    print("\(m)/\(d)は日曜日")
```

なお、caseラベルでletやvar、whereを使って条件を指定する方法は、タプルだけでなく、通常の整数などに対しても利用できます。

定数や変数を使うラベルは、別並べると「,」で区切って並べる際に制約があります。
これは、どのラベルの条件に一致したかによって、定数や変数が値を持たない可能性があるからです。
例えば以下のようにすることはできません。

```Swift:
switch t {
case (1, let y), (2, 2):
    print("\(y)")

case (2, _):
    fallthrough

case (let z, 2):
    print("\(z)")
default: break
}
```

caseラベルで定数や変数を使っているcase節へは、すぐ上の選択肢からfallthrough文で遷移することはできません。

ラベルを「,」で区切って並べた時、それらが定数や変数を含んでよい条件は、すべてのラベルが同じ名前、同じ型の定数もしくは変数を含むことです。
つまり、どんな場合でも定数もしくは変数の値が定まる必要があります。その点だけ気を付けてください。

###4.オプショナル型をswitch文で使う
---
switch文で場合分けしたい式がオプショナル型の値を含む場合も、caseのラベルで書き分けることができます。
例として名前と年齢からなるタプル型を考えます。ただし、年齢は不明の場合があるのでオプショナル型になっています。

```Swift:
typealias People = (String, Int?)
```

このような型をSwitch文で扱うには、caseパターンで「?」という記号を使います。

```Swift:
switch m {
case let (name, age?) where age >= 18:
    print("\(name), 免許取得可")

case let (name, (15...18)?):
    print("\(name), 高校生")

case (let name, nil):
    print("\(name), 年齢不明")

default: break
}
```

このように、対応する定数もしくは変数に「?」を付けると、オプショナル型がnil以外の値を持つときにマッチし、その後のwhere節や実行分では開示演算子を使うことなく値を参照できます。
また、nilを記述しておけば、オプショナル型がnilの値を持つ場合にマッチします。範囲を示すパターンは()で囲みます。

オプショナルであることを示す「?」は、タプルなどの他のパターンに利用することができます。
例えばPepole?型の変数opsをswitch文で使うとすれば、caseには次のように記述できます。

```Swift:
switch ops {            // opsはPeople?型
case let (name, 18?)? : // 開示した結果が(文字列, 18)ならマッチする
```

###5.おわりに
---
今回はタプルを用いたswitch文の利用法について書きました。次回は**列挙型**についての記事を書こうと思っているので、よろしければぜひご覧ください。
ここまで見てくださった方、ありがとうございました。