はじめに
コーディングの基礎体力づくりとして取り組んでいる、AOJ ITP1 の学習記録です。
今回は、"Branch on Condition" を題材に Kotlin の言語機能そのものにフォーカスしました。
基本的な if 文から、Kotlinの強みである when 式やメソッドチェーンを活用した、より簡潔で表現力豊かなコードの書き方までを記録しています。
Branch on Condition
2_A: Small, Large, or Equal
2 つの整数 $a$ と $b$ の大小関係を判定し、a < b、a > b、a == b のいずれかを出力する問題です。
提出したコード
fun main() {
val (a, b) = readLine()!!.split(" ").map { it.toInt() }
if(a < b){
println("a < b")
} else if(a > b){
println("a > b")
} else
println("a == b")
}
2_B: Range
3 つの整数 $a, b, c$ が $a < b < c$ の条件を満たしているか判定し、Yes または No を出力する問題です。
提出したコード
fun main() {
val (a, b, c) = readLine()!!.split(" ").map(String::toInt)
println(if(a < b && b < c) "Yes" else "No")
}
2_C: Sorting Three Numbers
与えられた 3 つの整数を昇順(小さい順)に並べ替えて出力する問題です。
提出したコード
fun main() {
val items = readLine()!!.split(" ").map (String::toInt)
println(items.sorted().joinToString(" "))
}
2_D: Circle in a Rectangle
長方形の中に円が完全に含まれているかを判定する問題です。
提出したコード
fun main() {
val (w, h, x, y, r) = readLine()!!.split(" ").map(String::toInt)
println(if(x - r >= 0 && x + r <= w && y - r >= 0 && y + r <= h) "Yes" else "No")
}
番外編
今回は Kotlin の言語仕様を最大限に活かした書き方にチャレンジしてみました。
より Kotlin らしい書き方へ
2_A のリファクタリング
fun main() {
val (a, b) = readLine()!!.split(" ").map(String::toInt)
// if式と同様にwhenも値を返す「式」として使える
println(when {
a < b -> "a < b"
a > b -> "a > b"
else -> "a == b"
})
}
map { it.toInt() } → map(String::toInt) への変更
- ラムダ式を メソッド参照 に置き換えることによって「各要素に
toInt()を適用する」意図をより直接的に表せる - 型推論が効いた状態で「
StringのtoIntを呼ぶ」ことが明示され、何を変換しているかがコード上で明確になる
if-else if-else → when 式 への変更
- Kotlin では
whenやifは 式(Expression) として値を返せる - 各分岐で値を返すため、
println()の呼び出しを1箇所にまとめられる
2_C のリファクタリング
fun main() {
// メソッドチェーンで、データが変換されていく流れを表現
readLine()!!
.split(" ")
.map(String::toInt)
.sorted()
.joinToString(" ")
.let(::println) // 最終結果をprintlnで出力
}
中間変数 val items の削除
- 一度しか使わない変数への代入を省略することで、コードの行数を減らし、読み手の認知負荷を低減できる
メソッドチェーンの改行
-
.split、.map、.sortedなどのメソッド呼び出しを改行で区切ることで、データがどのように変換されていくのかが一目でわかる - デバッグ時のコメントアウトや処理の入れ替えが容易になる
スコープ関数 let と関数参照 ::println の活用
- スコープ関数
letを使うと、直前の処理結果をそのまま引数として渡すことができる -
joinToString(" ")の結果をletに渡し、さらにそれを 関数参照::printlnを使ってprintln関数の引数として直接渡すことで、流れるようなデータ処理を表現できる
おわりに
今回、Kotlin の if が文ではなく 式 として扱えることを学びました。
TypeScript では if は 文 であるため、この違いが Kotlin に三項演算子が存在しない理由なのだと理解できました。
また、スコープ関数 や 関数参照 といった機能を初めて使ってみましたが、これらを活用することで、より Kotlin らしい簡潔なコードが書けることを実感しました。