制御構文 - Ryuya-777/note GitHub Wiki
-
- if文
- guard文
- for-in文
- while文
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です
条件不成立の時に早期退出を可能にする条件分岐。
エラー文をすぐ出したい時なんかに使えそう。
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です
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
一度マッチするとマッチングは終了する。以降のパターンはスキップする。
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キーワードで定義した文は他のいずれかのケースにもマッチしない場合に処理されるので、
網羅性を保証する役割をになっている。
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")
}
列挙型の制御式に対してはデフォルトケースを記述するのは好ましくない。
理由としては、列挙型に新たなcaseが追加された場合に、それが自動的にデフォルトケースにマッチしてしまう。
列挙型の制御式は全てのcaseを網羅するのが定石パターン。
ケースにマッチする条件を追加できる。&&と何が違う?
下記は.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で中断する
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型以外の場合の処理
}
//結果: 対象外の値です
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文は継続条件の評価によって繰り返しの終了を決定する。
要素名として指定した名前の定数を通じて要素にアクセスできる。
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 条件式{
条件式が成立する間は処理が繰り返される
}
var a = 1
while a < 4 {
print(a)
a += 1
}
// 結果: 1,2,3
初回実行を保証する繰り返し処理
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つの文はラベルを使用することで、制御対象を選択できる。
繰り返しの終了
下記は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
繰り返しの継続
下記のコードは奇数の場合は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
スコープ退出時の処理
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)
キャストできるかどうかのマッチパターン
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
ダウンキャストに成功した場合にマッチするパターン
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でも使える
if case パターン = 制御式 {
制御式がパターンにマッチした時の処理
}
let value = 9
if case 1...10 = value {
print("1以上10以下の値です")
}//1以上10以下の値です
guard case パターン = 制御式 else{
パターンにマッチしなかった場合の処理
guard文が記述されているスコープの外に退出する必要がある
}
func someFunction() {
let value = 9
guard case 1...10 = value else {
return
}
print("1以上10以下の値です")
}
someFunction()
//結果: 1以上10以下の値です
for case パターン in 値の連続 {
要素がマッチした場合の処理
}
let array = [1, 2, 3, 4]
for case 2...3 in array {
print("2以上3以下の値です")
}
//結果: 2以上3以下の値です 2以上3以下の値です
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