Utilize Control Flow in Kotlin

May 22 2024 · Kotlin 1.9.23, Android 14, Kotlin Playground

Lesson 03: Loop Code

Demo

Episode complete

Play next episode

Next
Transcript

Open the starter project in the playground and look at the very top. For the readability of this screencast, it shows only the code needed for the given task. Showing the entire sample project code at once would be overwhelming.

The first task is to create a loop that prints the message Cleaning classroom i… for the three classrooms. You can use the range to achieve that. The range should start from 1 and end at 3, with both the bounds inclusive. The loop should iterate over the range and print the message.

To denote a range in the code you can use two dots between the bounds. For example, 1..3 represents the numbers matching the condition 1 <= i <= 3.

Apart from the range you need the loop itself. The simplest loop is the for loop. It begins with the keyword for followed by the loop index name, then the keyword in and the range.

    println("For loop with range 1..3")
    for (i in 1..3) {
        println("Cleaning classroom $i...")
    }

As you can see the loop iterates over the range and prints the message for each classroom. Both the bounds of the range are inclusive, so the loop iterates over the numbers 1, 2, and 3. That is a close-ended range.

You can also exclude the upper bound of the range from the sequence. It will be an open-ended or half-open range. Why there is a need to distinguish between these two types of ranges? The simple lowering of the upper bound by one doesn’t seem to be a big deal.

The real-life example are time ranges. Imagine that the lesson starts at 8:00 AM and ends at 9:00 AM. Then, there is a break for ten minutes, from 9:00 AM to 9:10 AM. Take a look at the following diagram:

The 9:00 AM value belongs to both ranges. However, that exact moment is not a part of the lesson. You don’t normally use something like 08:59 AM or 08:59:59 AM to denote the end of the lesson. You just use 09:00 AM. With open-ended ranges, even the accuracy of the time calculation does not matter. Whatever you use minutes, seconds, or even milliseconds, the end of the lesson will always consist of any value up until 09:00 AM. If you use the open-ended ranges, the are no gaps between the them and no need to add or subtract numbers by one. The algorithm requires less code, it is simpler, more readable and less error-prone.

Consider the following example related to the school. There are two janitors in the school. The second one is responsible for cleaning the classrooms from 4 to 6. So the first one can clean the room from 1 to 4 exclusive.

To denote an open-ended range in the code, you can precede the upper bound with the < character.

    println("For loop with range 1..<3")
    for (i in 1..<3) {
        println("Cleaning classroom $i...")
    }

As you can see, the loop iterates over the range and prints the message for each classroom. But, the classroom number 3 is not included.

Now, imagine that you have to start from the classroom number 3 and go down to the first one. The straightforward way to achieve that is to swap the bounds of the range. Try to use 3..1 range.

    println("For loop with range 3..1")
    for (i in 3..1) {
        println("Cleaning classroom $i...")
    }

Hmm, nothing happened. The loop didn’t iterate at all. The reason is that the range is empty. To iterate over the range in the opposite direction you have to use downTo operator instead of ...

    println("For loop with range 3 downTo 1")
    for (i in 3 downTo 1) {
        println("Cleaning classroom $i...")
    }

Now the loop iterates over the range backwards and prints the message for each classroom.

The for loop is the most common loop in Kotlin. It is used when you want to iterate over an already known number of items/elements, for instance as you already saw using a range - where the bounds are known. There are other iterable types in Kotlin which can be used with for loop, like collections or sequences. But, they are not the subject of this lesson. You will learn about them in the next modules. So, to sum up, the for loop is used when you want to iterate over a defined series of elements.

Kotlin has also two other loops: while and do-while. They are used when you want to loop until a certain condition is met. For example, you want to keep cleaning rooms until the entire school is clean. Or, you want to keep cleaning rooms until you run of the washing fluid. The number of rooms is not known in advance.

The while loop is used when you want to execute the loop body as long as the given condition is true. The condition is checked before the loop body is executed. If the condition is false, the loop body is no longer executed. It may be executed zero or more times. The syntax of the while loop is similar to the if statement. It begins with the keyword while followed by the condition in the parentheses and the loop body in the optional curly braces.

Look how to write the while loop that prints the message for the three classrooms.

    println("Count of classrooms to clean:")
    val classroomsToClean = args.firstOrNull()?.toIntOrNull()
        ?: throw IllegalArgumentException("Invalid input, please enter a number")

    val cleanedClassrooms = mutableListOf<Int>()
    println()
    println("While loop")
    while (cleanedClassrooms.size < classroomsToClean) {
        val currentClassroom = cleanedClassrooms.size + 1
        println("Cleaning classroom $currentClassroom...")
        cleanedClassrooms.add(currentClassroom)
    }

A program takes the number of classrooms to clean as an input. If the input is not a number, it throws an exception so no more code is executed.

Then, the loop goes over classrooms and prints the message for each one.

The loop body is executed as long as the list of cleaned classrooms contains less elements than the desired number of classrooms to clean.

Note the condition. There is a less than operator <,

not less than or equal to <=. If you use the latter, the loop will iterate one more time than needed.

Note: This is important!

Use the correct comparison operator in the loop conditions. If you use the wrong one, the loop may execute one more or one less time than needed. It is called off-by-one error and is a common source of bugs in software.

If the loop body consists of only one statement, you can omit the curly braces. But, it is a good practice to always use them. It makes the code more readable and less error-prone.

The next kind of loop is the do-while loop. It differs from the while loop only in terms of when the condition is checked. The do-while loop checks the condition after the loop body is executed. It implies that the loop body is executed at least once. It cannot execute zero times like the while loop. Otherwise, the syntax is the same as the while loop.

Look how to write the do-while loop that prints the message about cleaning the classrooms.

    println("Do-while loop")
    cleanedClassrooms.clear()
    do {
        val currentClassroom = cleanedClassrooms.size + 1
        println("Cleaning classroom $currentClassroom...")
        cleanedClassrooms.add(currentClassroom)
    } while (cleanedClassrooms.size < classroomsToClean)

It seems that there is no difference between the while and do-while loops in this case. The loop body is executed the same number of times. So, change the input to zero and run the snippet again.

The while loop didn’t execute at all. The condition was false at the beginning. The do-while loop on the other hand executed the body once. It printed the message for the first classroom. That’s because it checks the condition after the loop body execution. So, the do-while loop is not suitable for the cases when there is a chance there will be no iterations at all.

Which loop should you use? Neither while nor do-while is better or worse than the other. The main difference is when the condition is checked. If there is a chance that no iteration will be needed then the only choice is the while loop. do-while loop may be more convenient in cases at least one iteration is required and condition may have to be evaluated after the loop body has been executed.

For example, the supervisors of the janitors check if the school is clean. They do that room by room. If some room is not clean enough, they tell the janitors to clean it again. So they keep doing checks while the school is not clean. It is not possible to tell if the school is clean before the first check. So, the do-while loop may a better choice to model that real-life situation.

What if you want to stop the loop before the condition is met? For example, the given classroom may be closed due to the renovation so it makes no sense to clean it right now. A janitor should skip that one room and continue to the next one.

To achieve that, you can use the continue statement.

    println("Continue statement")
    for (currentClassroom in 1..3) {
        if (currentClassroom == 2) {
            println("Skipping classroom $currentClassroom...")
            continue
        }
        println("Cleaning classroom $currentClassroom...")
    }

Look at the place where the continue statement is used. It is before the main action, cleaning in this case. If you put it after the cleaning, at the end of the loop body, it will have no effect.

In theory the continue worked. But, since there is no action after that statement it makes no sense to use it. The loop will continue to the next iteration anyway.

Now, consider the situation when there is an emergency in the school. The janitors should leave the school immediately. They should not continue their work.

To achieve that in Kotlin, you can use the break statement.

    println("Break statement")
    for (currentClassroom in 1..3) {
        if (currentClassroom == 2) {
            println("Breaking on classroom $currentClassroom...")
            break
        }
        println("Cleaning classroom $currentClassroom...")
    }

The break statement exits the loop immediately. No other iterations are executed. Note the break and continue statements may reduce the readability of the code especially if they are located in the middle of the loop bodies.

See forum comments
Cinema mode Download course materials from Github
Previous: Instruction Next: Conclusion