Swiftのswitch文

今回の記事ではSwiftの条件分岐文の一つであるSwitch文についてまとめていきます。
Swiftの条件分岐文は、条件の真偽によって分岐を判定するif文や、条件の成立しない場合に処理の早期退出を行い条件の成立を保証するguard文などがありますが、今回扱うswitch文はパターンマッチングによる条件分岐を行います。

switch文の基本文法

switch文とは、判定値に対してパターンマッチングにより処理の条件を分岐させる制御構文です。
switch文のフォーマットは以下の通りです。

switch 制御式 {
case パターン1 :
    制御式がパターン1にマッチした場合の処理
case パターン2 :
    制御式がパターン2にマッチした場合の処理
....
case パターンn :
    制御式がパターンnにマッチした場合の処理
default :
    制御式がどのパターンにもマッチしなかった場合の処理
}

各パターンをcaseキーワードで定義し、どのパターンにもマッチしなかった場合の処理をdefaultキーワードで定義します。
caseキーワードによるパターンは任意の数だけ定義することができます。
switch文では上から順に評価され、マッチしたパターンの処理が実行された後、それ以降のパターンはスキップします。
switch文はif文やguard文と違い、Bool型以外の値を判定することができ、一つの制御式に対して複数のケースに分岐できるという特徴があります。

// 利用例
let hello = "hello"

switch hello {
case "good morning" :
    print("おはよう")
case "hello" :
    print("こんにちは")
case "good evening" :
    print("こんばんは")
case "good night" :
    print("おやすみ")
default :
    print("英語の挨拶ではない")
}

// >>> こんにちは

ケース網羅性のチェック

コンパイラによる網羅性チェック

Swiftのswitch文はコンパイラによって、制御式のパターンの網羅性をチェックしています。
制御式が取りうる値がどのパターンにもマッチしない可能性がある場合、コンパイルエラーとなります。

// 網羅性チェックの例
// 制御式oneはSampleEnum型のため、.one .two .three の値を取りうる
enum SampleEnum {
    case one
    case two
    case three
}

let one = SampleEnum.one

switch one {
case .one :
    print("1")
case .two :
    print("2")
case .three :
    print("3")
}

// >>> 1

上記の例の場合、列挙型のSampleEnum型の値を制御式に受け取っているためパターン網羅するために.one .two .three のパターンをcaseで定義しているためコンパイルエラーとはなりません。

// 網羅性チェックの例
// 制御式oneはSampleEnum型のため、.one .two .three の値を取りうる
enum SampleEnum {
    case one
    case two
    case three
}

let one = SampleEnum.one

// .threeのパターンを網羅していないのでコンパイルエラーとなる
switch one {
case .one :
    print("1")
case .two :
    print("2")
}

上記の場合、.threeのパターンを網羅していないためコンパイルエラーとなります。

defaultキーワードによる網羅性の保証

どのパターンにも当てはまらない場合の処理を記述するdefaultキーワードを用いることで網羅性を保証することができます。

// 網羅性チェックの例
// 制御式oneはSampleEnum型のため、.one .two .three の値を取りうる
enum SampleEnum {
    case one
    case two
    case three
}

let one = SampleEnum.one

// .threeのパターンを網羅していないがdefaultで網羅性を保証している
switch one {
case .one :
    print("1")
case .two :
    print("2")
default :
    print("default")
}

whereキーワードによる条件の追加

whereキーワードを利用することでケースにマッチする条件を追加することができます。
whereキーワードの文法は以下の通りです。

switch 制御文 {
case パターン1 where 条件式 :
    制御式がパターン1にマッチし、かつ条件式を満たす場合の処理
default :
    制御式がどのパターンにもマッチしなかった場合の処理
}

下記の例では、最初のcaseで値が存在するパターンに対して、whereキーワードを利用して値が10以上の場合の条件を追加しています。

// 利用例
let optionalA: Int? = 3

switch optionalA {
case .some(let a) where a >= 10 :
    print("10以上の値が存在します。")
case .some :
    print("10未満の値が存在します。")
default :
    print("値が存在しません。")
}

// >>> 10未満の値が存在します。

break文によるケース処理の中断

break文

break文はケース処理を中断するための文です。
break文が記述された場合、ケース内の以降の処理は実行されずswitch文から抜け出します。

// 利用例
let hello = "hello"

switch hello {
case "good morning" :
    print("おはよう")
    break
    print("break後です。")
case "hello" :
    print("こんにちは")
    break
    print("break後です。")
case "good evening" :
    print("こんばんは")
    break
    print("break後です。")
default :
    print("英語の挨拶ではない")
    break
    print("break後です。")
}

// >>> こんにちは
// break後のprint()は実行されない

break文は必須ではありませんが、何も処理が存在しないケースの場合は、ケース内には必ず一つ以上の文を記述する必要があるためbreak文のみを記述する必要があります。

let num = 1

// defaultでは何も処理を行わない
switch num {
case 0 :
    print("zero")
case 2 :
    print("two")
default :
    break
}

// >>>

ラベルによるbreak文の制御

switch文にはラベルを付けることができ、ラベルを指定することでbreak文の制御対象を指定することができます。
ラベルの付け方は以下の通りです。

ラベル名 : switch 制御式 {
switch文の条件分岐式
}

switch文が入れ子になっている場合などでラベルによるbreak文の制御が必要になります。
下記の例では外側のswitch文で値の存在をチェックし、存在するパターンの処理として値を制御式としたswitch文を入れ子にして記述しています。
中のswitch文でdefaultのパターンの処理になった場合、print()でケースに当てはまらないことを出力後に外側のswitch文を対象とするようbreak文を制御することでswitch文全体を中断して処理を抜けています。

// 利用例
let optionalGreeting: String! = "good evening"

// ラベル"Exist"のswitch文
Exist : switch optionalGreeting {
case .some :
    let greeting = optionalGreeting!
    var japaneseGreeting = ""
    // ラベル"JapaneseGreeting"のswitch文
    JapaneseGreeting : switch greeting {
    case "good morning" :
        japaneseGreeting = "おはよう"
    case "hello" :
        japaneseGreeting = "こんにちは"
    case "good evening" :
        japaneseGreeting = "こんばんは"
    case "good night" :
        japaneseGreeting = "おやすみ"
    default :
        print("英語の挨拶ではない")
        // Existのswitch文を中断する
        break Exist
    }
    print("日本語で\(japaneseGreeting)です")
default :
    print("値が存在しません。")
}

// >>> 英語の挨拶ではない

fallthrough文による次のケースの処理への移動

fallthrough文は現在のケース処理を中断し、後続のケース処理に移る際に利用します。
Swiftのswitch文では特に何も指定しない場合、パターンマッチによりケースの処理を実行した後は後続のケースの処理はスキップするようになっています。
他の言語のswitch文のような制御構文では、break文のような中断する処理を明記しない場合に後続ケースの処理を全て行う場合もあるのですが、Swiftでは明示的に後続処理を行うことを記述するように設計されています。

// 利用例
let hello = "hello"

switch hello {
case "good morning" :
    print("おはよう")
case "hello" :
    fallthrough
case "good evening" :
    print("こんにちは")
case "good night" :
    print("おやすみ")
default :
    print("英語の挨拶ではない")
}

// >>> こんにちは

上記の利用例ではcaseの”hello”でfallthrough文を利用しており、case”good evening”の処理を実行しています。

まとめ

switch文はif文やguard文とは違い、制御式に対して複数のパターンマッチングによる条件分岐を記述することができる条件分岐文です。
そのため、場合によってはif文やguard文よりもスマートに記述することが可能です。
基本文法から制御方法までしっかりと把握し適切な場所で利用していきましょう。
ここまで読んでいただきありがとうございました。
それでは、また。