読者です 読者をやめる 読者になる 読者になる

【Swift】Appleの新言語「Swift」のリファレンスを読む(4) - Control Flow

注意

  • あくまでメモ書きなので細かい部分を端折りますし、色々間違ってるかもしれません。ちゃんとした内容は原文を読んでね。
  • コード例は基本的に原文からそのまま引用していますが、ちょっとした注釈をつけたり、統合したりしています。
  • SyntaxHighlighterが対応してないので微妙に読みにくいです。SyntaxHighlighterはこちらのものを使用させて頂いてます。
  • 他言語にそっくりな部分でも指摘しない。(自戒)

Control Flow(制御構文)

基本的にはC言語な制御構文です。ざっと列挙してみます。

  • for
    • for-in
    • for-condition-increment
  • if
  • switch
  • while
    • while
    • do-while
  • continue
  • break
  • fallthrough
  • return

…あれ?throwはないんだ。

全部紹介しても仕方ないので、ところどころ割愛しながら紹介していきます。

For Loops

コレクションに対するfor-in構文の説明は前回の記事を読めばそれで十分なので、Range Operatorを使用した場合のfor-in構文に関して説明します。

また、for-condition-increment構文はほんとにただのfor文なので割愛します。

for index in 1...5 {
    println("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25

説明すると言っても、別段説明することないですね…。ちなみにイテレートの一時的な結果を受け取る変数(例ではindex)を事前に宣言しておくと、参照できるスコープが広がります。

恐らくこれはRange Operator以外でも同じ挙動をする(最後の結果を保持する)と思います。活用するよりもむしろ意図せず変数が書き変わらないよう気をつけなきゃいけなさそうですね。

var index = 0

// これは変数名の重複としてコンパイルエラーにして欲しいナァ…
for index in 1...5 {
    println("\(index) times 5 is \(index * 5)")
}

println("total \(index) times.")

一時変数の結果を受け取る必要がなかったらアンダースコアにでもしとけば?とも書かれています。

let base = 3
let power = 10
var answer = 1

for _ in 1...power {
    answer *= base
}

println("\(base) to the power of \(power) is \(answer)")
// prints "3 to the power of 10 is 59049"

While Loops

普通のwhileです。

Conditional Statements

ifは特に面白いところがないんですが(optionalのアレは面白いと思うけど)、switchはC-likeと謳うにはあまりにも強化されすぎています。

まずはこれ。caseにOr条件を指定することが出来ます。

let someCharacter: Character = "e"

switch someCharacter {
case "a", "e", "i", "o", "u":
    println("\(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":
    println("\(someCharacter) is a consonant")
default:
    println("\(someCharacter) is not a vowel or a consonant")
}
// prints "e is a vowel"

本当にC-likeだったらcaseのOr条件はこう記述される(所謂フォールスルー)のですが、これはコンパイルエラーになるそうです。

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a":
case "A":
    println("The letter A")
default:
    println("Not the letter A")
}
// this will report a compile-time error

この構文が記述できない(する必要がない)以上、breakを書く必要性はほとんどありません。そんなわけでSwiftのswitch構文ではbreakが亡き者にされました。

が、「case文の中である条件を満たした場合はswitchから即座に抜けたい」と言う場合はやっぱりbreakを書く必要があります。

また、Range Operatorを使うとその範囲で判断してくれます。

let count = 3_000_000_000_000
let countedThings = "stars in the Milky Way"
var naturalCount: String

switch count {
case 0:
    naturalCount = "no"
case 1...3:
    naturalCount = "a few"
case 4...9:
    naturalCount = "several"
case 10...99:
    naturalCount = "tens of"
case 100...999:
    naturalCount = "hundreds of"
case 1000...999_999:
    naturalCount = "thousands of"
default:
    naturalCount = "millions and millions of"
}

println("There are \(naturalCount) \(countedThings).")
// prints "There are millions and millions of stars in the Milky Way."

タプルを用いたswitchもあります。「タプルのある項目のみが一致しているケース」も記述することが出来ますし、Range Operatorを併用することも出来ます。

let somePoint = (1, 1)

switch somePoint {
case (0, 0):
    println("(0, 0) is at the origin")
case (_, 0):
    println("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
    println("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
    println("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
    println("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}
// prints "(1, 1) is inside the box"

また、caseで変数宣言することで値をバインド出来ます。例ではタプルの一部分になってますが、switchに指定した変数がStringだろうとIntだろうとバインド出来ます。

let anotherPoint = (2, 0)

switch anotherPoint {
// letじゃなくてvarでもok。
case (let x, 0):
    println("on the x-axis with an x value of \(x)")
case (0, let y):
    println("on the y-axis with a y value of \(y)")
// defaultだとバインド出来ないのでこんな形になる
// このタプルは要素が2つだからいいけど、もっと多くなったら面倒そう
// default let xみたいにしてタプルごとバインド出来ればいいのに
case let (x, y):
    println("somewhere else at (\(x), \(y))")
}
// prints "on the x-axis with an x value of 2"

更にバインドした値で条件式を書くことが出来ます。

let yetAnotherPoint = (1, -1)

switch yetAnotherPoint {
case let (x, y) where x == y:
    println("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
    println("(\(x), \(y)) is on the line x == -y")
case let (x, y):
    println("(\(x), \(y)) is just some arbitrary point")
}
// prints "(1, -1) is on the line x == -y"

caseに書かれた値が一致し、なおかつwhereに指定した式がtrueを返せばそのcaseの中に書かれた処理を実行します。

Control Transfer Statements

continueは…まぁ…普通のcontinueです。breakも普通のbreakです。所謂Labeled Statementsがサポートされているので、入れ子になったfor / while / switchからも上手いこと抜け出せます。

fallthroughは聞きなれないステートメントですが、まぁ、呼んで字の如くです。switchの中でのみ呼び出せます。

let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"

switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
    description += " a prime number, and also"
    fallthrough
default:
    description += " an integer."
}
println(description)
// prints "The number 5 is a prime number, and also an integer."

returnに至っては記述すらありませんが、それこそ何も特筆すべきことがないんでしょう。