Kotlin/制御構造
このページは、親コンテンツである「Kotlin」の中から {{:Kotlin/制御構造}} の形式で展開されることを意図して書かれています。
この手法は
- ページ分割すると、[[#inline|inline]] のようなページ内リンクが大量に切れる。
- ページ分割すると、<ref name=foobar /> のような名前のついた参照引用情報が大量に切れる。
- スマートフォンやタブレットではページ遷移は好まれない。
- MediaWikiは、圧縮転送に対応しているので1ページのサイズが大きくなるのはトラフィック的には問題が少なく、ページ分割によりセッションが多くなる弊害が大きい。
- 編集はより小さなサブパート(このページ)で行える。
という技術的背景があります。
- Kotlinのサブページ
制御構造
[編集]分岐
[編集]Kotlinは、if と when の2つの分岐構文を持ち、両方とも値を返す式です。
if
[編集]if式は、条件式に基づき分岐し、分岐先の式を評価します。 if式の値は、分岐先の式の値です(C言語系の三項演算子に相当する働きをします)。 if式の値を右辺値化した場合、else節は必須です。
- 構文
if-expr := if '(' 条件式 ')' 式1 [ else 式2 ]
- if式の例
fun main(args: Array<String>) { val i = 0 if (i == 0) println("zero") else println("non zero") println(if (i == 0) "零" else "非零" ) }
- 実行結果
zero 零
条件式の条件
[編集]- 論理型
- Nullableな型
でなければいけません。
- 条件式に整数を使うと
fun main(args: Array<String>) { val i = 0 if (i) println("non zero") }
- コンパイルエラー
Main.kt:4:9: error: type mismatch: inferred type is Int but Boolean was expected if (i) ^
- Kotlinでは、整数は自動的には論理型に変換されないので
if (i != 0) println("non zero")
- とします。
when
[編集]when式を使って条件分岐をすることができます。 when 式は、when に与えられた式(境界値)に最初にマッチするパターンに結びついた値を返すパターンマッチングです。 式が省略されると、パターンの条件が最初に真になるパターンに結びついた値を返します。
- whenの例
fun main() { val ary = arrayOf(1, 'X', 3.14, "abc", arrayOf(1,2,3), true) for (obj in ary) { when (obj) { is Number, is Boolean -> println(obj) is Char -> println("'$obj'") is String -> println("\"$obj\"") is Array<*> -> println(obj.joinToString(prefix="[", postfix="]")) else -> println(obj::class.simpleName) } } }
- 実行結果
1 'X' 3.14 "abc" [1, 2, 3] true
- when式は、いくつかの論理条件に応じて、複数の異なる制御構造体(ケース)のうちの1つを評価することができるという点で、条件式と類似しています[1]。
- 重要な違いは、when式は複数の異なる条件とそれに対応する制御構造体(control structure bodies; CSB)を含むことができることです。
- when式には、境界値付きと境界値なしの2種類の形式があります。
- 境界値(bound value;whenキーワードの後の括弧で囲まれた式)なしのwhen式は、whenエントリからの条件に基づいて異なるCSBのうちの1つを評価します。
- 各 when エントリは、boolean 条件(または特殊な else 条件)とそれに対応する CSB から構成されます。
- when項目は出現順にチェックされ評価され、条件が真と評価された場合、対応するCSBが評価され、when式の値はCSBの値と同じになり、残りのすべての条件と式は評価されません。
- whenパターン式のパターンの後ろに break は不要です(フォールスルーしませんし、することはできません)。
- もし break を書くと、when式の外のループ式からの脱出になります(フォールスルーしてしまう言語には、できなかったこと)。
whenは式なので以下のように書換えることができます。
- when式の例
fun main() { val ary = arrayOf(1, 'X', 3.14, "abc", arrayOf(1,2,3), true) for (obj in ary) { val s = when (obj) { is Number, is Boolean -> obj is Char -> "'$obj'" is String -> "\"$obj\"" is Array<*> -> obj.joinToString(prefix="[", postfix="]") else -> obj::class.simpleName } println(s) } }
区切子 ->
を使った構文の左辺の条件には、以下のようなバリエーションがあります。
- when式の条件の構文
値 値1, 値2... , 値n in 範囲式 !in 範囲式 is 型 !is 型 else
- 値は、定数である必要はなくメソッドでも構いません。
- 式は、単式だけでなくブロック式
{...}
でも構いません。
引数のないwhen
[編集]when
文は、多くの条件分岐をシンプルに表現するための強力な構造です。境界値が省略された場合、true
が境界値とみなされます。
- 引数のないwhenの例
fun main() { val a = 1 val b = 2 when { a == 0 -> println("a == 0") a == 1 && b == 1 -> println("a == 1 && b == 1") a == 1 && b == 2 -> println("a == 1 && b == 2") else -> println("else") } }
- 実行結果
a == 1 && b == 2
- 上記のコードでは、
when
文が引数を取らずに条件分岐を行っています。最初の条件がfalse
で、次の条件がtrue
となり、そのブロックが実行されます。 - ifを使った等価なコード
- 同じ条件分岐を
if
文を使って表現すると以下のようになります。 fun main() { val a = 1 val b = 2 if (a == 0) println("a == 0") else if (a == 1 && b == 1) println("a == 1 && b == 1") else if (a == 1 && b == 2) println("a == 1 && b == 2") else println("else") }
when
文を使うことで、このような条件分岐をより簡潔に表現できます。
境界値を省略した例
[編集]境界値を省略した例として、以下のコードを見てみましょう。
fun main() { val x = 5 val result = when { x > 0 -> "Positive" x < 0 -> "Negative" else -> "Zero" } println(result) }
このコードでは、when
文が x
の値に基づいて文字列を返します。境界値が省略されているため、条件が true
のブロックが実行されます。
when
文でループを脱出する例
[編集]when
文を使用してループから脱出する例もあります。以下は、when
文を使って特定の条件でループを脱出する例です。
fun main() { val numbers = listOf(1, 2, 3, 4, 5) for (number in numbers) { when (number) { 3 -> { println("Found 3, breaking the loop.") break } else -> println("Processing $number") } } }
このコードでは、when
文を使用して number
が3の場合にループから脱出します。
enum
な式が境界値に与えられた例
[編集]enum
クラスを使った when
文の例も挙げてみましょう。
enum class Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } fun main() { val today = Day.WEDNESDAY val activity = when (today) { Day.MONDAY, Day.TUESDAY -> "Working" Day.WEDNESDAY -> "Meeting" Day.THURSDAY, Day.FRIDAY -> "Coding" Day.SATURDAY, Day.SUNDAY -> "Relaxing" } println("Today's activity: $activity") }
このコードでは、enum
クラス Day
を使用して、曜日に応じた活動を選択しています。when
文が enum
の各ケースを処理し、対応する活動が選択されます。
繰返し処理
[編集]Kotlinには、while・do-while と for の3つの繰返し構文があります[2]。これらは文で、値を返すことはできません。
while
[編集]whileは、条件式が true の間、式を評価しつづけます[3]。
- 構文
while-stmt := while '(' 条件式 ')' 式
- 条件式は、Boolean 型でなければいけません。
- whileの例
fun main(args: Array<String>) { var i = 0 while (i < 5) { println(i) i += 1 } println("last = $i") }
- 実行結果
0 1 2 3 4 last = 5
do-while
[編集]do-whileは、条件式が true の間、式を評価しつづけるという意味では、whileと同じですが、条件式がループの最後にある、つまり条件が成立しなくても1周はループが回ることが異なります[4]。
- 構文
while-stmt := do 式 while '(' 条件式 ')'
- 条件式は、Boolean 型でなければいけません。
- do-whileの例
fun main() { var i = 0 do { i++ println("i = $i") } while (i < 0) }
- 実行結果
i = 1
for
[編集]Kotlinのforはw:foreach文タイプのループ構文で、C言語の for(;;) とは異なる構文です[5]。
- 構文
for (ループ変数 in コレクション) 式
- 範囲コレクションとforを組合せた例
fun main(args: Array<String>) { for (i in 1..5) println(i) println((1..5).javaClass.kotlin) }
- 実行結果
1 2 3 4 5 class kotlin.ranges.IntRange
- iの様なループ変数は、forがスコープになります。
- ここでは、型指定を省略しているので、型推論されコレクションの要素型になります。省略せず、
for (i : Int in 1..5) println(i)
- とすることもできます(ただし、異種コレクションだと要素型はUnion型になり宣言が複雑になるので、コードレビューの時に意図を明確にするなどの想起がない限り、型推論に任せるのが常です)。
- ループ変数は、varやvalを前置することができません。
- ループ変数には、代入できません。
for と等価な while
[編集]- for と等価な while
for (ループ変数 in コレクション) { println(ループ変数) } // は、以下と等価 val イテレーター = コレクション.iterator() while (イテレーター.hasNext()) { val ループ変数 = イテレーター.next() println(ループ変数) }