Swiftのパターンマッチの使い方12選

SwiftのパターンマッチのロゴとコードのスクリーンショットSwift
この記事は約22分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Swiftのパターンマッチは、コード内での値や型の判別を柔軟に行うための強力なツールとして広く利用されています。

初心者から上級者まで、この記事を通してSwiftのパターンマッチの基本から応用までを詳しく学びましょう。

徹底的な理解を目指す方のために、具体的なサンプルコードとその解説を12点取り揃えました。

●Swiftのパターンマッチとは

Swiftのパターンマッチは、与えられた値やデータ構造が特定のパターンに合致するかどうかをチェックするための機能です。

これにより、簡潔に条件を指定してデータを分析し、必要な処理を適用することが可能となります。

○パターンマッチの基本概念

Swiftでのパターンマッチは主にswitch文やif-case文などを利用して実行されます。

これにより、特定の条件や型、範囲に合致するかどうかを詳しく調査し、それに応じた処理を実施することができます。

例えば、整数が特定の範囲に入っているかを判定する場合や、列挙型の特定のケースに合致するかを調べる場面などでこのパターンマッチが役立ちます。

また、複雑なデータ構造やオブジェクトが特定の条件を満たすかどうかを簡潔に調べることも可能です。

Swiftのパターンマッチの利点としては、次のような点が挙げられます。

  1. コードの可読性が向上する:パターンマッチを用いることで、複雑な条件分岐をシンプルに書くことができ、他の開発者がコードを読む際の負担を軽減することができます。
  2. ミスを防ぐ:明確な条件を指定することで、間違った条件による不具合を防ぐことが可能です。
  3. 柔軟性が増す:複数の条件を一度にチェックすることができるため、新たな条件を追加する際も簡単に対応できます。

これらの特長を理解することで、Swiftのパターンマッチを効果的に使用するための土台を築くことができるでしょう。

●Swiftでのパターンマッチの使い方

Swiftのパターンマッチは、あるデータが特定のパターンに一致するかを調べる際に使用する機能であり、この技術はコードの可読性や保守性を高めるために非常に重要です。

ここでは、Swiftでのパターンマッチの具体的な使い方を、サンプルコードと共に詳細に解説します。

○サンプルコード1:基本的なパターンマッチ

Swiftでの最も一般的なパターンマッチは、switch文を使用して行います。

このコードでは、整数の値を判定して、それぞれの値に応じたメッセージを表示するコードを表しています。

この例では、整数10を判定して、一致するケースのメッセージを出力しています。

let number = 10
switch number {
case 0:
    print("数字は0です。")
case 10:
    print("数字は10です。")
default:
    print("数字は0でも10でもありません。")
}

このコードを実行すると、「数字は10です。」というメッセージが出力されます。

Swiftのswitch文は、他のプログラミング言語とは異なり、break文が不要で、最初に一致したcaseのブロックのみが実行されます。

○サンプルコード2:タプルとの組み合わせ

タプルは複数の値をまとめて扱うためのデータ構造で、パターンマッチと組み合わせることで、複数の値を同時に判定することができます。

このコードでは、xとyの2つの整数の値をタプルとしてまとめ、その値の組み合わせに応じてメッセージを表示するコードを表しています。

この例では、xが0、yが10の場合のメッセージを出力しています。

let coordinates = (x: 0, y: 10)
switch coordinates {
case (0, 0):
    print("原点にあります。")
case (_, 0):
    print("X軸上にあります。")
case (0, _):
    print("Y軸上にあります。")
case (let x, 10) where x > 5:
    print("Y座標が10で、X座標が5より大きい位置にあります。")
default:
    print("特定の位置にはありません。")
}

このコードを実行すると、「Y軸上にあります。」というメッセージが出力されます。

また、_ は任意の値にマッチするワイルドカードとして使用され、letを使うことで、マッチした値を変数に束縛して利用することができます。

○サンプルコード3:where句を使用した条件付きマッチ

Swiftのパターンマッチには、特定の条件を満たす場合のみマッチさせる「where句」を使用する方法があります。

where句を用いることで、より詳細な条件を設定して、特定のパターンに一致する時だけ処理を行うことができます。

このコードでは、where句を使って特定の条件を満たす場合のみマッチさせる方法を表しています。

この例では、整数の配列から偶数のみを取り出す操作を行っています。

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

for number in numbers {
    switch number {
    case let x where x % 2 == 0:
        print(x)
    default:
        break
    }
}

このコードでは、numbersという整数の配列から、for文を用いて各要素を順番に取り出しています。

取り出した要素はswitch文に渡され、case let x where x % 2 == 0:という条件を満たす場合のみ、すなわち、取り出した要素が偶数の場合のみ、その数値をprint関数で出力します。

このコードを実行すると、偶数である2, 4, 6, 8, 10の5つの数値が順に出力されます。

○サンプルコード4:値束縛の活用

Swiftのパターンマッチには、マッチした値を新しい変数や定数に束縛する「値束縛」の機能も用意されています。

この機能を使うことで、マッチした値をその後の処理で使いやすくすることができます。

このコードでは、タプルを使って複数の値を持つデータを表し、それに対して値束縛を適用して特定の条件のデータを取り出す方法を表しています。

この例では、(名前, 年齢)のタプルから20歳以上の名前のみを取り出しています。

let persons = [("John", 25), ("Alice", 18), ("Bob", 30), ("Eve", 15)]

for person in persons {
    switch person {
    case let (name, age) where age >= 20:
        print(name)
    default:
        break
    }
}

このコードを実行すると、年齢が20歳以上であるJohnとBobの2人の名前が出力されます。

○サンプルコード5:列挙型との組み合わせ

Swiftのパターンマッチングは、特に列挙型(enum)と組み合わせることで、非常に強力なコードを書くことができます。

列挙型は、限定された選択肢の中から1つの値を持つことができるデータ型です。

そのため、パターンマッチングを用いて列挙型の異なるケースを効果的に処理することができます。

例として、天気の状態を表す列挙型を考えます。

この列挙型は、晴れ、曇り、雨、雪の4つのケースを持つとします。

下記のコードでは、列挙型を使って天気の状態を定義し、その後にswitch文とパターンマッチングを使用して各天気に応じたメッセージを出力しています。

enum 天気 {
    case 晴れ
    case 曇り
    case 雨
    case 雪
}

let 今日の天気 = 天気.雨

switch 今日の天気 {
case .晴れ:
    print("今日は晴れです。外出するのに最適な日です。")
case .曇り:
    print("今日は曇りです。洗濯物は乾きにくいかもしれません。")
case .雨:
    print("今日は雨です。傘を忘れずに持っていきましょう。")
case .雪:
    print("今日は雪です。足元に注意して、滑らないように気をつけましょう。")
}

このコードでは、天気という列挙型を使って、晴れ、曇り、雨、雪の4つのケースを定義しています。

そして、今日の天気という変数には、雨という値が格納されています。

switch文を使用して、今日の天気の値に応じて異なるメッセージをprint関数で出力しています。

例えば、今日の天気が雨の場合、”今日は雨です。傘を忘れずに持っていきましょう。”というメッセージが出力されます。

○サンプルコード6:オプショナル型とのマッチ

Swiftでは、オプショナル型は特定の値を持つか、または値を持たないかを表すための特殊な型です。

オプショナル型の値をチェックする際、パターンマッチングを使用してその有無や具体的な値を簡潔に確認することができます。

下記のコードでは、Intのオプショナル型の変数を定義し、その後にswitch文とパターンマッチングを使用してオプショナルの値の有無を判定しています。

var 数字: Int? = 42

switch 数字 {
case .some(let value):
    print("数字には\(value)という値が格納されています。")
case .none:
    print("数字には値が格納されていません。")
}

このコードでは、数字というオプショナル型の変数に42という値が格納されています。

switch文を使用して、数字が値を持っている場合(.some)と値を持っていない場合(.none)を区別しています。

このサンプルコードを実行すると、”数字には42という値が格納されています。”というメッセージが表示されることになります。

オプショナル型とパターンマッチングの組み合わせは、非常に頻繁に使用されるため、Swiftプログラムの中でよく見かける構文となっています。

○サンプルコード7:型パターンの使用

Swiftのパターンマッチングでは、特定の型に一致するかどうかを判定する「型パターン」も使用することができます。

これにより、異なる型のオブジェクトを一元的に処理する際に、その型に応じた処理を簡潔に分岐することができます。

下記のコードは、Any型の変数に格納された値が、Int型、String型、またはその他の型であるかを判定しています。

let 任意のデータ: Any = "Swift"

switch 任意のデータ {
case let intValue as Int:
    print("\(intValue)はInt型です。")
case let stringValue as String:
    print("\"\(stringValue)\"はString型です。")
default:
    print("その他の型です。")
}

このコードでは、任意のデータという変数に”Swift”という文字列が格納されています。

switch文と型パターンを用いて、この変数の型がInt型、String型、またはその他の型であるかを判定しています。

このサンプルコードを実行すると、”SwiftはString型です。”というメッセージが表示されることになります。

●パターンマッチの応用例

Swiftのパターンマッチは、基本的な使い方だけでなく、さまざまな応用例も存在します。

今回は、Swiftでのパターンマッチの応用例について、サンプルコードとともに詳しく解説していきます。

特に複雑なデータ構造や関数との組み合わせなど、日常的に出くわす可能性のある具体的な状況を元に、その使い方を深堀りしていきます。

○サンプルコード8:複雑なデータ構造のマッチ

Swiftでは、複数のデータ構造を組み合わせて使うことがよくあります。

タプルや配列、辞書など、これらを組み合わせて使う際にパターンマッチを利用すると、効率的にデータの内容を抽出することができます。

このコードでは、タプル内の配列を使って、特定のデータ構造のマッチングを行います。

この例では、生徒の名前と試験の点数の配列を持ったタプルを使っています。

let 生徒データ: (名前: String, 試験点数: [Int]) = ("山田太郎", [85, 90, 78, 65])

switch 生徒データ {
case let (名前, [試験1, 試験2, _, _]) where 試験1 >= 80 && 試験2 >= 80:
    print("\(名前)は初めの2つの試験で80点以上を取得しています。")
default:
    print("\(名前)の試験の結果に特筆すべき点はありません。")
}

このコードでは、タプルの中の配列を分解して、最初の2つの試験の点数を取得しています。

where句を使って、その2つの試験の点数が80点以上かどうかを判定しています。

結果として、「山田太郎は初めの2つの試験で80点以上を取得しています。」というメッセージが表示されることになります。

○サンプルコード9:関数との組み合わせ

関数の戻り値や引数としてのパターンマッチも非常に強力です。

関数とパターンマッチを組み合わせることで、コードの可読性を向上させ、冗長な処理を省略することが可能となります。

下記のコードは、関数の引数として渡されたタプルに対して、パターンマッチを適用しています。

func 季節を判定する(月日: (月: Int, 日: Int)) {
    switch 月日 {
    case (3...5, _):
        print("春です。")
    case (6...8, _):
        print("夏です。")
    case (9...11, _):
        print("秋です。")
    case (12, _), (1...2, _):
        print("冬です。")
    default:
        print("入力された月日は無効です。")
    }
}

季節を判定する(月日: (6, 15))

この関数は、引数として月と日のタプルを受け取り、その月日に応じて季節を判定しています。

結果として、「夏です。」というメッセージが表示されることになります。

○サンプルコード10:switch文内での利用

Swiftのパターンマッチングは非常に柔軟で、switch文内での利用が特に一般的です。

このような利用方法は、特に複数の条件を持つ場面や、異なるケースごとの処理を記述する場面で役立ちます。

今回は、switch文の中でパターンマッチングを活用する方法についてのサンプルコードを通じて、具体的な使い方を詳しく解説します。

enum Vehicle {
    case car(String)
    case bicycle(String)
    case bus(Int)
}

let myVehicle = Vehicle.car("Toyota")

switch myVehicle {
case .car(let brand):
    print("私の車は\(brand)です。")
case .bicycle(let type):
    print("私の自転車は\(type)の型です。")
case .bus(let number):
    print("私が乗っているバスの番号は\(number)番です。")
}

このコードでは、Vehicleという列挙型を使って、車、自転車、バスの三つの異なるケースを表しています。

そして、switch文を使用して、それぞれのケースに対する処理を定義しています。

この例では、それぞれのケースの値をパターンマッチングを用いて取り出し、メッセージとして出力しています。

このサンプルコードの実行結果としては、「私の車はToyotaです。」というメッセージが出力されます。

なぜなら、myVehicleの値としてVehicle.car("Toyota")が代入されているからです。

○サンプルコード11:配列や辞書との組み合わせ

Swiftにおけるパターンマッチングは、単純な値や型だけでなく、配列や辞書などのコレクション型との組み合わせでも非常に有用です。

ここでは、配列や辞書を用いたパターンマッチングの方法を解説します。

まず、基本的な配列のマッチングを考えてみましょう。

let fruits = ["apple", "banana", "cherry"]

switch fruits {
case ["apple", _, _]:
    print("最初の要素はappleです。")
case [_, "banana", _]:
    print("2番目の要素はbananaです。")
default:
    print("一致するパターンはありませんでした。")
}

このコードでは、配列fruitsを使って、switch文でのマッチングを行っています。

アンダースコア_は、その位置の要素の値には関心がないことを表します。

この例では、配列の最初の要素が”apple”であるかどうかをチェックしています。

このコードを実行すると、”最初の要素はappleです。”というメッセージが出力されます。

次に、辞書を用いたパターンマッチングの例を見てみましょう。

let student = ["name": "Taro", "age": 20] as [String : Any]

switch student {
case ["name": "Taro", "age": 20]:
    print("学生はTaroさん、20歳です。")
case ["name": let name, "age": _]:
    print("\(name)さんの年齢は特定されていません。")
default:
    print("一致するパターンはありませんでした。")
}

このコードでは、辞書studentの内容に基づいて、switch文でのマッチングを行っています。

キー”age”の値に関心がない場合は、アンダースコア_を使用します。

一方、キー”name”の値に関心がある場合は、letを使用してその値を変数nameに束縛しています。

このコードを実行すると、”学生はTaroさん、20歳です。”というメッセージが出力されます。

○サンプルコード12:自作型とのマッチング

Swiftでは、ユーザーが独自に定義した型に対してもパターンマッチングを適用することができます。

これにより、自作のデータ構造やオブジェクトに対する処理をより柔軟に、かつ明確に記述することができます。

例として、学生を表す構造体Studentと、その学生が所属する学年を表す列挙型Gradeを考えてみましょう。

enum Grade {
    case freshman, sophomore, junior, senior
}

struct Student {
    let name: String
    let grade: Grade
}

let taro = Student(name: "Taro", grade: .junior)

このStudent型のインスタンスに対して、パターンマッチングを用いた処理を行ってみます。

switch taro {
case Student(name: "Taro", grade: .freshman):
    print("Taroさんは1年生です。")
case Student(name: "Taro", grade: .junior):
    print("Taroさんは3年生です。")
default:
    print("Taroさんの学年は判定できませんでした。")
}

このコードでは、switch文を使用して、taroというStudent型のインスタンスの内容に基づいてマッチングを行っています。

このように、Swiftのパターンマッチングは、自作型や列挙型、構造体といった複雑なデータ型に対しても適用することができます。

このコードを実行すると、”Taroさんは3年生です。”というメッセージが出力されます。

●注意点と対処法

Swiftのパターンマッチングは非常に強力な機能であり、多くのシチュエーションでコードの可読性や簡潔性を向上させることができます。

しかし、この機能を利用する際に知っておくべき注意点や、特定のシチュエーションでの対処方法がいくつか存在します。

○エラーの可能性とその対処

□網羅性のないパターンマッチ

Swiftのswitch文は網羅的であることが求められます。

これは、全ての可能性を考慮したケースがswitch文内に記述されている必要があるということです。

この網羅性がない場合、コンパイラはエラーを出力します。

enum Fruit {
    case apple, banana, orange
}

let myFruit = Fruit.apple

switch myFruit {
case .apple:
    print("リンゴです")
case .banana:
    print("バナナです")
}

このコードでは、Fruitという列挙型にorangeというケースが存在しているにも関わらず、switch文内でそのケースに関する処理が書かれていません。

このため、コンパイラからエラーが出力されます。

対処法として、全てのケースを網羅するか、default節を使用して未網羅のケースに対する処理を記述することでこのエラーを回避できます。

□値の不一致による実行時エラー

パターンマッチングを使用する際、予期しない値が来た場合に実行時エラーが発生する可能性があります。

let numberTuple: (Int, Int) = (3, 4)

switch numberTuple {
case (1, _):
    print("最初の数字は1です")
case (_, 2):
    print("2番目の数字は2です")
}

このコードは、どのケースにもマッチしないため、実行時エラーが発生します。

対処法として、default節を使用して、どのケースにもマッチしない場合の処理を記述することで、このような実行時エラーを防ぐことができます。

○パフォーマンス上の考慮点

パターンマッチングは非常に強力なツールですが、大量のケースや複雑な条件でのマッチングを行う場合、パフォーマンス上の影響を受ける可能性があります。

□複数の条件を組み合わせたマッチング

多くの条件や複雑なマッチングを行う場合、その条件の順序や組み合わせによっては、パフォーマンスが劣化する可能性があります。

let values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

for value in values {
    switch value {
    case let x where x % 2 == 0 && x % 3 == 0:
        print("\(x)は2と3の両方で割り切れます")
    case let x where x % 2 == 0:
        print("\(x)は2で割り切れます")
    case let x where x % 3 == 0:
        print("\(x)は3で割り切れます")
    default:
        print("\(value)はどちらでも割り切れません")
    }
}

このコードでは、リスト内の各数字が2や3で割り切れるかどうかをチェックしています。

このような複雑な条件を持つマッチングは、特に大量のデータに対して行う場合、パフォーマンスへの影響を考慮する必要があります。

対処法として、条件の順序を適切に調整することで、最も頻繁にマッチする条件を先にチェックするようにすると、パフォーマンスの向上が期待できます。

また、不要な条件は削除する、または複数の条件をまとめるなどして、マッチングのシンプル化を図ることも効果的です。

●カスタマイズ方法

Swiftのパターンマッチはその基本的な使い方だけでなく、カスタマイズを行うことでさらに高度なマッチングが可能です。

カスタマイズを学ぶことで、Swiftのパターンマッチをより深く、効率的に活用できるようになります。

今回は、より複雑なマッチングを実現するためのカスタマイズ方法を中心に、サンプルコードとともに詳細に説明していきます。

○より複雑なマッチングを実現するためのカスタマイズ

Swiftのパターンマッチは非常に強力な機能であり、基本的な使い方だけでなく、独自のカスタマイズを行うことで様々なシチュエーションでのマッチングを行うことができます。

ここでは、カスタマイズを活用して複雑なマッチングを実現するためのサンプルコードと、その詳細な説明を交えた解説をしていきます。

□拡張型を使用したカスタムパターンマッチ

このコードでは、拡張型を使用してカスタムパターンマッチを実現するコードを表しています。

この例では、Int型に特定の条件を満たすかどうかを判断するカスタムパターンを追加しています。

extension Int {
    static func isEven(_ value: Int) -> Bool {
        return value % 2 == 0
    }
}

let number = 4

switch number {
case _ where Int.isEven(number):
    print("偶数です")
default:
    print("奇数です")
}

上記のサンプルコードでは、Int型の拡張としてisEvenメソッドを定義し、その中で数値が偶数かどうかを判断しています。

switch文内で、このカスタムパターンを使用して数値が偶数か奇数かを出力するようにしています。

このサンプルを実行すると、「偶数です」という結果が得られます。

□既存の型にカスタマイズを加えたパターンマッチ

このコードでは、既存の型にカスタマイズを加えて、特定の条件に合致するかどうかを判断するカスタムパターンを実現しています。

この例では、String型に特定のプレフィックスを持つかどうかを判断するカスタムパターンを追加しています。

extension String {
    static func hasPrefixSwift(_ value: String) -> Bool {
        return value.hasPrefix("Swift")
    }
}

let language = "SwiftUI"

switch language {
case _ where String.hasPrefixSwift(language):
    print("Swift関連の技術です")
default:
    print("Swift関連ではありません")
}

上記のサンプルコードでは、String型の拡張としてhasPrefixSwiftメソッドを定義し、文字列が”Swift”で始まるかどうかを判断しています。

switch文内で、このカスタムパターンを使用して文字列がSwift関連の技術かどうかを出力するようにしています。

このサンプルを実行すると、「Swift関連の技術です」という結果が得られます。

まとめ

Swiftのパターンマッチは、様々な条件や状況に柔軟に対応することができる強力な機能です。

基本的な使い方から、より高度なカスタマイズ方法まで、多岐にわたる方法でこの機能を活用することができます。

特に、拡張型を使用して独自のカスタムパターンを追加することで、さらに詳細な条件を持つマッチングを実現することができるのが大きな魅力です。

この記事を通して、Swiftのパターンマッチの基本的な使い方やカスタマイズの方法についての理解を深めることができたことでしょう。

日常のコーディングやプロジェクトでの実装において、この知識を活かし、効率的で読みやすいコードの実装を目指してください。

Swiftのパターンマッチをうまく活用することで、多様なシチュエーションでのプログラミングがよりスムーズに、そして効果的になることでしょう。