制御構文 - Ryuya-777/note GitHub Wiki

目次

  1. if-let文とguard文

    1. if-let文
    2. guard文
    3. if文との使い分け
  2. switch文

    1. 基本の型
    2. ケースの網羅性チェック
    3. defaultキーワード
    4. defaultの注意事項
    5. whereキーワード
    6. break文
    7. ラベル
    8. fallthrough文
  3. 繰り返し

    1. for-in
    2. while
    3. repeat-while
    4. break文
    5. continue文
    6. ラベル
  4. マッチパターン

  5. マッチパターンが使える場所

    • if文
    • guard文
    • for-in文
    • while文

if-let文とguard文

if-let文

if-let文はOptionalの型の値の有無に応じて分岐を行い
値がある場合と無い場合の処理を分岐できる。
下記はオプショナルバインディングの条件分岐

let OptionalA = Optional(1)

if let a = OptionalA {
    print("値は\(a)です")
}else{
  print("値は存在しません")
}

//"値は1です"

if-letでは複数のOptional型の値を同時に取り出すことができる。

let OptionalA = Optional("a")
let OptionalB = Optional("b")

if let a = OptionalA, let b = OptionalB {
    print("値は\(a)\(b)です")
}
else{
  print("どちらかの値が存在しません")
}
//値はaとbです

guard文

条件不成立の時に早期退出を可能にする条件分岐。
エラー文をすぐ出したい時なんかに使えそう。

func test() {
    let value = -1
    
    guard value >= 0 else{  //elseを書く
        print("値は0未満です")
        return
    }
}

test() //値は0未満です

使い方1

func test(_ price:Int) {
    guard price >= 500 else{  //elseを書く
        print("500円以上を選択してください。")
        return
    }
   //gurd文で以降の値は保証されている
    print("合計金額は\(price)です")
}

test(600)//合計金額は600です

if文との使い分け

2択の場合はguard文使った方がシンプルに書けるってこと。if文は多分岐の場合に使う
下記では両方Int?であれば足し算を行い、どちらかがnilであればnilを返すと同時に値を持っていない引数を出力する。

if文の場合

func add(_ optionalA: Int?, _ optionalB: Int?) -> Int? {
    let a: Int  //初期値
    if let unwrappedA = optionalA { //値が入っていればtrue
        a = unwrappedA
    } else {
        print("第1引数に値が入っていません")
        return nil //値がなければnilを返す
    }

    let b: Int
    if let unwrappedB = optionalB { //値が入っていればtrue
        b = unwrappedB
    } else {
        print("第2引数に値が入っていません")
        return nil //値がなければnilを返す
    }

    return a + b
}

add(Optional(1), Optional(2)) // 3

guard文の場合

func add(_ optionalA: Int?, _ optionalB: Int?) -> Int? {
    guard let a = optionalA else {
        print("第1引数に値が入っていません")
        return nil
    }

    guard let b = optionalB else {
        print("第2引数に値が入っていません")
        return nil
    }

    return a + b
}

add(Optional(1), Optional(2)) // 3

switch文

基本の型

一度マッチするとマッチングは終了する。以降のパターンはスキップする。
if文やguard文は条件式にはbool型の値しか指定できないが、switch文はどんな型でも指定できる。
複数パターンのエラー文とか使えそう

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

Intが「正、0、負」の3パターン分岐

let a = 1

switch a {
case Int.min..<0:
    print("aは負の値です")
case 1..<Int.max:
    print("aは正の値です")
default:
    print("aは0です")
}
//aは正の値です

ケースの網羅性チェック

列挙型のパターン毎にswicth処理する場合は、switch文で列挙型のパターンを全て網羅する必要がある。

enum SomeEnum {
    case foo
    case bar
    case baz
}

let foo = SomeEnum.foo

 switch foo {
 case .foo:
     print(".foo")
 case .bar:
     print(".bar")
 
 // .bazが想定されていないため網羅的ではなく、コンパイルエラーとなる
//エラー文: Switch must be exhaustive (switchは網羅的であるべきです)
 }

制御式が取り得る全ての値をケースで記述する必要がある。
Bool型の場合はtrue・falseの2のパターンを記述する。

let a = true

switch a {
case true:
    print("true")
case false:
    print("false")
}
//true

defaultキーワード

defaultキーワードで定義した文は他のいずれかのケースにもマッチしない場合に処理されるので、
網羅性を保証する役割をになっている。

enum SomeEnum {
    case foo
    case bar
    case baz
}

let baz = SomeEnum.baz

switch baz {
case .foo:
    print(".foo")
case .bar:
    print(".bar")
default:
    // .bazの場合はこのケースに入るため網羅的となる
    print("Default")
}

defaultの注意事項

列挙型の制御式に対してはデフォルトケースを記述するのは好ましくない。
理由としては、列挙型に新たなcaseが追加された場合に、それが自動的にデフォルトケースにマッチしてしまう。
列挙型の制御式は全てのcaseを網羅するのが定石パターン。

whereキーワード

ケースにマッチする条件を追加できる。&&と何が違う?
下記は.some(let a)にはマッチしてるが、weher a > 10にはマッチしていないのでデフォケースが出力される。

let optionalA: Int? = 1

switch optionalA {
case .some(let a) where a > 10: //「some(let a)」はOptionalAを定数aに代入してる。
    print("10より大きい値\(a)が存在します")
default:
    print("値が存在しない、もしくは10以下です")
}
//"値が存在しない、もしくは10以下です"

break文

ケース実行を中断する
通常、途中で処理を中断したい時以外はbreak文必要ない
デフォルトケースで何も処理をしたくない場合は下記のようにbreakで中断する

let a = 1

switch a {
case 1:
    print("実行される")
    break
    print("実行されない")
default:
    break
}

ラベル

break文の処理対象の指定

ラベルの書き方

 ラベル名: switch文

使い所としては、switch文をネストする場合はラベルが必要
ラベルを記述しないと実行前にコンパイルエラーが出る。(swiftは実行する前から自動でコンパイルしてくれてるのでエラーになる)

let value = 11 as Any

outerSwitch: switch value {
case let int as Int:
    let description: String //初期値
    switch int {
    case 1, 3, 5, 7, 9:
        description = "奇数" 
    case 2, 4, 6, 8, 10:
        description = "偶数" 
    default:
        print("対象外の値です") //Int型 かつ 1-10以外の値の場合
        break outerSwitch  //breakするラベルを指定。
    }
    print("値は\(description)です") //ラベル名:outerSwitch のswitch分がbreakされてるから「description」が未初期化でもコンパイルエラーが発生しない
default:
    print("対象外の型の値です") //Int型以外の場合の処理
}
//結果: 対象外の値です

fallthrough文

switch文の処理を終了し、次のケースを実行する制御構文

let a = 1

switch a {
case 1:
    print("case 1")
    fallthrough
case 2:
    print("case 2")
default:
    print("default")
}

//結果: case 1 case 2

繰り返し

繰り返し文の種類

繰り返し処理には代表的な「for-in文」と[while文]がある。
for-inはシークエンスの要素数によって繰り返しの終了を決定する。
while文は継続条件の評価によって繰り返しの終了を決定する。

for-in

要素名として指定した名前の定数を通じて要素にアクセスできる。

var array = [配列]
for 要素名(定数) in 配列などシーケンス型データ{
  要素ごとにくり返し処理される
}

使用例

let array = [1, 2, 3]

for element in array {
    print(element)
} // 結果: 1,2,3

辞書型の要素をfor-in文で列挙する場合、要素の型は(Key, Value)のタプル型になる。

let dictionary = ["a": 1, "b": 2]

for (key, value) in dictionary { //let (key, value) = ("a" , 1)のタプル型になる
    print("Key: \(key), Value: \(value)")
}
// 結果: Key: a, Value: 1    Key: b, Value: 2

while

継続条件による繰り返し

while 条件式{
 条件式が成立する間は処理が繰り返される
}
var a = 1

while a < 4 {
    print(a)
    a += 1
}
// 結果: 1,2,3

repeat-while

初回実行を保証する繰り返し処理
while文の場合

var a = 1

while a < 4 {
    print(a)
    a += 1
}
// 結果: 値が出力されない

repeat-whileの場合
1回目の処理が出力できる。(Intだと比較演算子を<=に変えればいい気がする、、)

var a = 1

repeat {
    print(a)
    a += 1
} while a < 1

実行文の中断

break文やcountinue文を使うことで、実行を中断できる。
break文は繰り返し全体を中断させる。
continue文は現在の処理のみ中断し、後続の処理は実行する。
上記2つの文はラベルを使用することで、制御対象を選択できる。

break文

繰り返しの終了
下記は2が見つかった時点で処理を中断しているプログラム。

var containsTwo = false
let array = [1, 2, 3]

for element in array {
    if element == 2 {
        containsTwo = true
        break
    }
    print("element: \(element)")
}
print("containsTwo: \(containsTwo)")
//結果: element: 1
//      containsTwo: true

continue文

繰り返しの継続
下記のコードは奇数の場合はoddsに追加、偶数の場合はprint出力する。

var odds = [Int]()
let array = [1, 2, 3]

for element in array {
    if element % 2 == 1 { //奇数の場合
        odds.append(element) //変数oddsに追加
        continue        //for文の最初に戻る
    }
    print("even: \(element)") //偶数の場合は出力
}
print("odds: \(odds)")

//結果: even: 2  even: 4  odds: [1, 3, 5]

ラベル

swicth同様にラベルを使って制御対象を指定できる。
繰り返しのラベリング

ラベル名: 繰り返し文

break と continueのラベリング

break ラベル名
continue ラベル名

下記では内側のfor-inから外側のfor-inにbreakを実行している。

outside: for element in [1, 2, 3] {
    for nestedElement in [1, 2, 3] {
        print("element: \(element), nestedElement: \(nestedElement)")
        break outside
    }
}
//結果: "element: 1, nestedElement: 1

遅延実行

defer文

スコープ退出時の処理

var count = 0

func someFunction() -> Int {
    defer {
        count += 1
    }
    return count
}

someFunction() // 0
count // 1

マッチパターン

式パターン

〜=演算子による評価

let integer = 9

switch integer {
case 6:
    print("match: 6")
case 5...10: 
    print("match: 5...10")
default:
    print("default")
} //結果: match: 5...10

バリューバインディングパターン

どのような値にもマッチし、マッチ後は定数または変数に代入される。

let value = 3

switch value {
case let matchedValue:
    print(matchedValue)
} //結果: 3

オプショナルパターン

オプショナル型かどうかのマッチパターン

let optionalA = Optional(4)
switch optionalA {
case let a?: //Optional型であるなら
    print(a)
default:
    print("nil")
} //結果: 4

列挙型ケースパターン

列挙型のケースとの一致を評価するマッチパターン

enum Hemisphere {
    case northern
    case southern
}

let hemisphere = Hemisphere.northern

switch hemisphere {
case .northern:
    print("match: .northern")

case .southern:
    print("match: .southern")
} //結果: match: .northern
enum Hemisphere {
    case northern
    case southern
}

let hemisphere = Optional(Hemisphere.northern)

switch hemisphere {
case .northern:
    print("match: .northern")

case .southern:
    print("match: .southern")

case nil:
    print("nil")
}
//結果: match: .northern

連想値(辞書型の値)にバリューバインディングした場合、マッチした列挙型のケースの連想値を取り出せる。

enum Color {
    case rgb(Int, Int, Int)
    case cmyk(Int, Int, Int, Int)
}

let color = Color.rgb(100, 200, 255)

switch color {
case .rgb(let r, let g, let b):
    print(".rgb: (\(r), \(g), \(b))")

case .cmyk(let c, let m, let y, let k):
    print(".cmyk: (\(c), \(m), \(y), \(k))")
}
//結果: .rgb: (100, 200, 255)

is演算子による型キャスティングパターン

キャストできるかどうかのマッチパターン

let any: Any = 1

switch any {
case is String:
    print("match: String")
case is Int:
    print("match: Int")  //初期値がInt型だからAnyからIntへダウンキャスト可能なのでここがマッチ
default:
    print("default")
}
//結果: match: Int

as演算子による型キャスティングパターン

ダウンキャストに成功した場合にマッチするパターン

let any: Any = 1

switch any {
case let string as String:
    print("match: String(\(string))")
case let int as Int:
    print("match: Int(\(int))")
default:
    print("default")
}//結果: match: Int(1)

マッチパターンが使える場所

if文・guard文・for-in文・while文でも条件にcaseキーワードを指定するとマッチパターンが使える。
do-cathでも使える

6-1:if文

if case パターン = 制御式 {
 制御式がパターンにマッチした時の処理
}
let value = 9

if case 1...10 = value {
    print("1以上10以下の値です")
}//1以上10以下の値です

6-2:guard文

guard case パターン = 制御式 else{
  パターンにマッチしなかった場合の処理
 guard文が記述されているスコープの外に退出する必要がある
}
func someFunction() {
    let value = 9
    guard case 1...10 = value else {
        return
    }

    print("1以上10以下の値です")
}

someFunction()
//結果: 1以上10以下の値です

6-3:for-in文

for case パターン in 値の連続 {
  要素がマッチした場合の処理
}
let array = [1, 2, 3, 4]

for case 2...3 in array {
    print("2以上3以下の値です")
}
//結果: 2以上3以下の値です  2以上3以下の値です

6-4:while文

while case パターン = 制御式{
 制御式がパターンにマッチした場合の処理
}
var nextValue = Optional(1)
while case let value? = nextValue {
    print("value: \(value)")

    if value >= 3 {
        nextValue = nil
    } else {
        nextValue = value + 1
    }
}//value: 1   value: 2   value: 3
⚠️ **GitHub.com Fallback** ⚠️