Control Flow Workbook

Eleven short exercises on Kotlin control flow — if as an expression, ranges, for loops, membership with in, indices, and labelled break.

Practice problems for Kotlin’s if Returns a Value, So There’s No Ternary. Each takes a minute or two. Some exercises auto-check: implement the function in the editor and press Run — hidden tests go green when you’re right and red (with a hint) when you’re not, all on JetBrains’ Kotlin compiler server. The printing and range exercises stay attempt-then-reveal: press Run to try them, then click Show answer. Nothing here is a trick question, just direct practice of the syntax from the lesson.

if as an expression

1. Assign from an if

Implement grade so it returns "pass" when score > 50, otherwise "fail" — using if as an expression.

Implement it, then press Run to check import org.junit.Test import org.junit.Assert class Test { @Test fun grades() { Assert.assertEquals("pass when score is above 50", "pass", grade(60)) Assert.assertEquals("fail at exactly 50", "fail", grade(50)) } } //sampleStart fun grade(score: Int): String = "fail" // TODO: "pass" when score > 50, else "fail" //sampleEnd
Show answer Hide answer
fun grade(score: Int) = if (score > 50) "pass" else "fail"

2. No ternary needed

Implement sign — the Kotlin form of the Java expression x > 0 ? "+" : "-".

Implement it, then press Run to check import org.junit.Test import org.junit.Assert class Test { @Test fun signs() { Assert.assertEquals("plus when x is positive", "+", sign(3)) Assert.assertEquals("minus when x is negative", "-", sign(-3)) Assert.assertEquals("minus at zero", "-", sign(0)) } } //sampleStart fun sign(x: Int): String = "-" // TODO: "+" when x > 0, else "-" //sampleEnd
Show answer Hide answer
fun sign(x: Int) = if (x > 0) "+" else "-"

Ranges

3. Two ranges

Write a range covering 1 through 10 inclusive, and one covering 1 through 9 (excluding the upper end).

Try it — edit, then press Run fun main() { val inclusive = 1..10 // 1 through 10 inclusive val exclusive = 1..10 // TODO: 1 through 9 — use 'until' println(inclusive.toList()) println(exclusive.toList()) }
Show answer Hide answer
1..10
1 until 10

4. Count down

Print 5, 4, 3, 2, 1 with a single for loop.

Try it — edit, then press Run fun main() { // TODO: print 5, 4, 3, 2, 1 — one loop for (i in 1..5) println(i) }
Show answer Hide answer
for (i in 5 downTo 1) println(i)

5. Every other number

Print 0, 2, 4, 6, 8, 10 with a single for loop.

Try it — edit, then press Run fun main() { // TODO: print 0, 2, 4, 6, 8, 10 — one loop for (i in 0..10) println(i) }
Show answer Hide answer
for (i in 0..10 step 2) println(i)

6. A range membership check

Implement working — true when age is between 18 and 65, both inclusive.

Implement it, then press Run to check import org.junit.Test import org.junit.Assert class Test { @Test fun inRange() { Assert.assertTrue("true inside the range", working(30)) Assert.assertTrue("true at the inclusive lower bound", working(18)) Assert.assertFalse("false above the range", working(70)) } } //sampleStart fun working(age: Int): Boolean = false // TODO: true when age is 18..65 inclusive //sampleEnd
Show answer Hide answer
fun working(age: Int) = age in 18..65

for, indices, and in

7. Walk the elements

Given a list names, print each name. No index.

Try it — edit, then press Run fun main() { val names = listOf("Ada", "Grace", "Alan") // TODO: print each name, no index }
Show answer Hide answer
for (name in names) println(name)

8. Index and value together

Print each name in names prefixed by its index, e.g. 0: Ada.

Try it — edit, then press Run fun main() { val names = listOf("Ada", "Grace", "Alan") // TODO: print "0: Ada", "1: Grace", ... with withIndex() }
Show answer Hide answer
for ((i, name) in names.withIndex()) {
    println("$i: $name")
}

9. Loop over positions

Print the indices of names (0, 1, 2, …) using the list’s own index range.

Try it — edit, then press Run fun main() { val names = listOf("Ada", "Grace", "Alan") // TODO: print 0, 1, 2 using names.indices }
Show answer Hide answer
for (i in names.indices) println(i)

10. Not a member

Implement unknown — true when command is not in the valid list.

Implement it, then press Run to check import org.junit.Test import org.junit.Assert class Test { @Test fun notMember() { val valid = listOf("go", "stop", "wait") Assert.assertTrue("true when the command is absent", unknown("jump", valid)) Assert.assertFalse("false when the command is present", unknown("go", valid)) } } //sampleStart fun unknown(command: String, valid: List<String>): Boolean = true // TODO: true when command is NOT in valid //sampleEnd
Show answer Hide answer
fun unknown(command: String, valid: List<String>) = command !in valid

11. Break out of both loops

Given a grid of rows of cells, break out of both loops the moment you hit a cell where isMine is true.

Try it — edit, then press Run fun main() { data class Cell(val isMine: Boolean) val grid = listOf( listOf(Cell(false), Cell(false)), listOf(Cell(false), Cell(true)), ) // TODO: stop at the first mine, breaking out of BOTH loops for (row in grid) { for (cell in row) { if (cell.isMine) println("found a mine") } } }
Show answer Hide answer
outer@ for (row in grid) {
    for (cell in row) {
        if (cell.isMine) break@outer
    }
}

Going deeper: the ..< operator and ranges

12. Half-open the modern way

Build the list [0, 1, 2, 3] for n = 4 using the modern half-open range operator, not until.

Implement it, then press Run to check import org.junit.Test import org.junit.Assert class Test { @Test fun upTo() { Assert.assertEquals(listOf(0, 1, 2, 3), upTo(4)) Assert.assertEquals(emptyList<Int>(), upTo(0)) } } //sampleStart fun upTo(n: Int): List<Int> = emptyList() // TODO: 0 up to but not including n, using ..< //sampleEnd
Show answer Hide answer
fun upTo(n: Int): List<Int> = (0..<n).toList()

0..<n is the preferred spelling of the old 0 until n — both are an IntRange, and the for/range form compiles to a plain counting loop.

13. Membership beats comparisons

Rewrite a “between” check as a single range in test: inWorkingAge(age) is true for 18 through 65 inclusive.

Implement it, then press Run to check import org.junit.Test import org.junit.Assert class Test { @Test fun working() { Assert.assertTrue(inWorkingAge(18)) Assert.assertTrue(inWorkingAge(65)) Assert.assertFalse(inWorkingAge(17)) Assert.assertFalse(inWorkingAge(66)) } } //sampleStart fun inWorkingAge(age: Int): Boolean = false // TODO: 18..65 inclusive, using the in operator //sampleEnd
Show answer Hide answer
fun inWorkingAge(age: Int) = age in 18..65

age in 18..65 compiles to the two comparisons you’d write by hand, so it’s free — and reads far better.

14. Loops aren’t expressions

Why doesn’t val x = for (i in 1..3) i compile? Reveal to check.

Show answer Hide answer

for and while are statements, not expressions — they do work but don’t produce a value, so there’s nothing to assign. To turn iteration into a result, use a collection function (map, sumOf, first), which are expressions. Only if and when return values among the control-flow constructs.


Back to the lesson, Kotlin’s if Returns a Value, or on to the next one: when.

Comments