Kotlin/制御構造

出典: フリー教科書『ウィキブックス(Wikibooks)』

このページは、親コンテンツである「Kotlin」の中から {{:Kotlin/制御構造}} の形式で展開されることを意図して書かれています。

この手法は

  1. ページ分割すると、[[#inline|inline]] のようなページ内リンクが大量に切れる。
  2. ページ分割すると、<ref name=foobar /> のような名前のついた参照引用情報が大量に切れる。
  3. スマートフォンやタブレットではページ遷移は好まれない。
  4. MediaWikiは、圧縮転送に対応しているので1ページのサイズが大きくなるのはトラフィック的には問題が少なく、ページ分割によりセッションが多くなる弊害が大きい。
  5. 編集はより小さなサブパート(このページ)で行える。

という技術的背景があります。

Kotlinのサブページ

制御構造[編集]

分岐[編集]

Kotlinは、ifwhen の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 
零
条件式の条件[編集]

ifwhile の条件式は

  • 論理型
  • 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には、whiledo-whilefor の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(プ変数)
}
  1. ^ Kotlin language specification Chapter 8 Expressions 8.6 When expressions
  2. ^ loopStatement
  3. ^ whileStatement
  4. ^ doWhileStatement
  5. ^ forStatement