Imagine you’re writing universal domain logic for your program. All I/O is provided via an interface (like UI, persistent storage, networking, etc.), you don’t have to think about it, and you almost don’t care about the platform, on which you run. Your program is fairly simple, only a few straightforward tasks. This chapter describes all essentials you need to write it. The foundational basics that you can build upon: data, processing primitives, and functions to reuse code.
Flow control
Let’s talk logic. The most basic thing you can do to process data or program a behavior is to execute different commands based on a decision. Then you may want to execute the same commands repeatedly over varying inputs – loop – or stop processing early – jump. Branching, loops, and jumps form the foundation of logic programming.
Note: Essentially, all logic can be boiled down to two primitives – branching and jumping, bundled together in different ways. However, when you think about designing an algorithm rather than taking the machine code level perspective, loops emerge as a distinct, fundamental concept. So they have a dedicated topic of their own.
Jumps
Break
Exit a loop early:
for (i in 1..5) {
if (i == 3) break
}
Continue
Skip an iteration of the loop:
for (i in 1..5) {
if (i == 3) continue
}
Labels
Use labels to exit several nested loops early or skip outer iterations from a nested loop:
outerLoop@ for (i in 1..10) {
for (j in 1..10) {
if (i > j) break@outerLoop
}
}
outerLoop@ for (i in 1..10) {
for (j in 1..10) {
if (i == 5 && j == 5) continue@outerLoop
}
}
Labels also work for lambdas. They are the only way to exit early from lambdas, via the so called “qualified returns”. Depending on use, they can behave as break or continue.
var positiveSum = listOf(-1, -2, 2, 1).reduce lambda@{ acc, it ->
// Continue with explicit label
if (acc >= 50) return@lambda acc
if (it > 0) acc + it else acc
}
positiveSum = listOf(-1, -2, 2, 1).reduce { acc, it ->
// Continue with implicit label
if (acc >= 50) return@reduce acc
if (it > 0) acc + it else acc
}
val initialNumbers = mutableListOf<Int>()
run abort@{
listOf(1, 2, "three", null, true).forEach {
// Break
if (it !is Int) return@abort
initialNumbers.add(it)
}
}