Contents
While Loops
whileループは条件式がfalseになるまでstatementsを実行する制御です。イテレーションが始まる前にイテレーションの回数がわからないような場合にwhileループを使用するのが適しています。Swiftは二つの種類のwhileループを提供します。
- while文はループの最初に条件式を評価します。
- repeat-while文はループの終わりに条件式を評価します。
while文
whileループは一つの条件式をループの最初に評価します。条件式がtrueと評価された場合、条件式がfalseと評価されるまで実行文が繰り返し実行されます。
while condition { statements }
下のサンプルはシンプルなゲーム「Snakes and Ladders(へびとはしご)」です。
ルールは以下の通りです。
ボードには25のマスがあります。ゲームの目的(ゴール)は25マス目に到達、あるいは25マス目を超えることです。
- スタート地点は「0マス目」であり、ボードの外にあります。
- それぞれのターンで六面のサイコロを振り、図で示す道順に従いサイコロの結果の数だけ進みます。
- 進んだ先にはしごの下端があれば、はしごを登ってはしごの上端まで進めます。
- 進んだ先にへびの頭があれば、へびに食われてへびの尻尾まで戻されます。
ゲームのボードはInt型の配列で表現されます。ゲームボードのサイズは定数finalSquareの値に基づきます。ボードを表現する配列boardのイニシャライズにfinalSquareが使用されます。またこの後登場する勝利条件の判定にもfinalSquareが使用されます。配列boardは、プレーヤーのスタート地点も含めて全部で26(25ではなくて)のInt型の値0でイニシャライズされます。
let finalSquare = 25 var board = [Int](repeating: 0, count: finalSquare + 1)
いくつかのマス目は、へびとはしごを表現するために、特別に値をセットします。はしごの入り口のマス目には正の数値をセットしてボードを進めることを表現します。へびの頭のマス目には負の数値をセットしてボードを戻らなければならないことを表現します。
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
board[03]ははしごの入り口なので、「+08」をセットしてboard[11]に進めることを表します。値とステートメントを揃えるために、単項プラス演算子(+ i)が単項マイナス演算子(-i)とともに明示的に使用され、10未満の数値にはゼロが埋め込まれます。 (どちらのスタイルの手法も厳密には必要ありませんが、よりきれいなコードになります。)
var square = 0 var diceRoll = 0 while square < finalSquare { // roll the dice diceRoll += 1 if diceRoll == 7 { diceRoll = 1 } // move by the rolled amount square += diceRoll if square < board.count { // if we're still on the board, move up or down for a snake or a ladder square += board[square] } } print("Game over!")
工事中🏗
No Implicit Fallthrough
CやObjective-Cのswitch文と違い、Swiftのswitch文はデフォルトで、マッチしたケースのブロックの実行が終わるとswitch文自体から抜けます。ですから各ケースのブロックの最後にbreakを明示的に記述する必要がありません。
各ケースのボディは少なくとも一つの文を含んでいる必要があります。次のサンプルは一番目のケースが空ですから許されません。
let anotherCharacter: Character = "a" switch anotherCharacter { case "a": // ←文が一つも無いのでエラー case "A": print("The letter A") default: print("Not the letter A") } //コンパイルエラー発生
Cのswitch文とは違い、上記のサンプルのswitch文は”a”と”A”の両方にマッチすることはありません。上記のサンプルは
ケース”a”
に一つも実行すべき文が無いのでコンパイルエラーが発生します。この挙動によりミスによるフォールスルーを回避でき、意図がわかりやすい安全なコードが書けます。
一つのケースで”a”と”A”の両方にマッチさせたい場合、以下のようにカンマで区切って記述します。
let anotherCharacter: Character = "a" switch anotherCharacter { case "a", "A": print("The letter A") default: print("Not the letter A") } // Prints "The letter A"
範囲によるマッチング(Interval Matching)
工事中🏗
Tuples
工事中🏗
バリューバインディング(Value Bindings)
switch文のケースは値に対して、一時的な定数名、変数名により名前をつけることができます(定数・変数にその値がセットされる)。そしてその定数・変数ケースのボディ内で使用できます。ケースのボディ内で一時的な定数・変数と値が結び付けられますので、この挙動はバリューバインディングとして知られています。
下のサンプルは 座標(x,y)を(Int,Int)型のタプルで表現しています。そして下のグラフにカテゴライズします。
let anotherPoint = (2, 0) switch anotherPoint { case (let x, 0): print("on the x-axis with an x value of \(x)") case (0, let y): print("on the y-axis with a y value of \(y)") case let (x, y): print("somewhere else at (\(x), \(y))") } // Prints "on the x-axis with an x value of 2"
このサンプルでswitch文は、座標が
- x軸上にある
- y軸上にある
- それ以外(どちらの軸の上にも無い)
のように場合分けします。
switch文の三つのケースはプレースホルダーとして定数xと定数yを宣言し、anotherPointからタプルの片方、あるいは両方の値を受け取ります。一番目のケース
case (let x,0)
は、y座標が0のあらゆる座標とマッチし、一時的な定数xに「anotherPointのx座標の値」をセットします。同様に二番目のケース
case (0,let y)
は、x座標が0のあらゆる座標とマッチし、一時的な定数yに「anotherPointのy座標の値」をセットします。
一時的な定数が宣言されると、その定数をそのケースのコードブロック内で使用できます。サンプルでは定数の値を表示しています。
このサンプルのswitch文はdefaultケースがありません。最後のケースである
case let (x,y)
は、二つのプレースホルダーx,yを持つタプルを宣言しており、これはあらゆる座標とマッチします。anotherPointは常に二つの値を持つタプルですので、このケースは、それよりも上のケースでマッチしなかった全ての値とマッチします。ですので、defaultケースを用意しなくてもswitch文の網羅性が確保されます。
Where
switch文のケースではwhere節を使用して追加的な条件についてチェックすることができます。
下のサンプルは(x,y)座標を以下のように分類します。
工事中🏗
ケースの合成(Compound Cases)
複数のケースが同じボディを有する場合
let someCharacter: Character = "e" switch someCharacter { case "a", "e", "i", "o", "u": print("\(someCharacter) is a vowel") case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": print("\(someCharacter) is a consonant") default: print("\(someCharacter) is not a vowel or a consonant") } // Prints "e is a vowel"
参考
https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html