Lambdas Workbook (Part 4): References, Returns, and Anonymous Functions
Twelve short exercises on the four kinds of function reference, non-local returns and labels, and anonymous functions.
Practice problems for Lambdas, Sharpened: References, Returns, and Anonymous Functions. Each takes a minute or two. Every exercise has a runnable editor — fill in the TODO, press Run (the code compiles on JetBrains’ Kotlin server), then click Show answer to check yourself. Nothing here is a trick question, just direct practice of the syntax from the lesson.
the four kinds of reference
1. A member reference
Uppercase each of words using a member reference instead of the lambda { it.uppercase() }.
fun main() {
val words = listOf("ada", "linus")
val shouted = words.map { it } // TODO: use a member reference String::uppercase
println(shouted)
} Show answer Hide answer
words.map(String::uppercase) 2. A top-level function reference
Given fun isEven(n: Int) = n % 2 == 0, filter nums to its even elements using a reference.
fun isEven(n: Int) = n % 2 == 0
fun main() {
val nums = listOf(1, 2, 3, 4, 5, 6)
val evens = nums.filter { true } // TODO: filter with a reference ::isEven
println(evens)
} Show answer Hide answer
nums.filter(::isEven) 3. A bound reference
Given a logger with fun log(m: String), print each message in messages using a bound reference.
class Logger {
fun log(m: String) = println(m)
}
fun main() {
val logger = Logger()
val messages = listOf("boot", "run", "stop")
messages.forEach { } // TODO: use a bound reference logger::log
} Show answer Hide answer
messages.forEach(logger::log) 4. A constructor reference
Given class User(val id: Int) and ids: List<Int>, build a User for each id with a constructor reference.
class User(val id: Int)
fun main() {
val ids = listOf(1, 2, 3)
val users = ids.map { User(it) } // TODO: use a constructor reference ::User
println(users.map { it.id })
} Show answer Hide answer
ids.map(::User) 5. Replace a forwarding lambda
Replace the lambda in words.map { it.trim() } with a reference.
fun main() {
val words = listOf(" a ", " b ")
val trimmed = words.map { it.trim() } // TODO: replace the lambda with String::trim
println(trimmed)
} Show answer Hide answer
words.map(String::trim) returns and labels
6. A non-local return
Write firstEven(numbers: List<Int>): Int? that uses forEach and a plain return to return the first even number, or null.
fun firstEven(numbers: List<Int>): Int? {
// TODO: use forEach and a plain return to return the first even, or null
return null
}
fun main() {
println(firstEven(listOf(1, 3, 6, 8)))
} Show answer Hide answer
fun firstEven(numbers: List<Int>): Int? {
numbers.forEach { if (it % 2 == 0) return it }
return null
}A plain return exits firstEven, not just the lambda.
7. Skip with a labelled return
Using forEach, print every element of nums except negatives — skip a negative with a labelled return.
fun main() {
val nums = listOf(1, -2, 3, -4, 5)
nums.forEach {
// TODO: skip negatives with a labelled return, otherwise print it
println(it)
}
} Show answer Hide answer
nums.forEach {
if (it < 0) return@forEach
println(it)
} 8. The two returns, side by side
Write two forEach lambdas over nums: one that exits the enclosing function on the first zero, and one that merely skips zeros.
fun main() {
val nums = listOf(1, 0, 2)
// TODO: a forEach that returns from main on the first zero
nums.forEach { }
// TODO: a forEach that only skips zeros and prints the rest
nums.forEach { }
} Show answer Hide answer
nums.forEach { if (it == 0) return } // exits the whole function
nums.forEach { if (it == 0) return@forEach; println(it) } // skips this element anonymous functions
9. Rewrite a lambda as an anonymous function
Rewrite { n: Int -> n * 2 } as an anonymous function.
fun main() {
val doubler = { n: Int -> n * 2 } // TODO: rewrite as an anonymous function fun(...)...
println(doubler(21))
} Show answer Hide answer
fun(n: Int): Int { return n * 2 }Inside an anonymous function, a plain return returns from it — the normal rules.
10. Pass an anonymous function
Pass an anonymous function to filter that keeps positive numbers.
fun main() {
val nums = listOf(-1, 2, -3, 4)
val positives = nums.filter { true } // TODO: pass an anonymous function fun(n: Int): Boolean
println(positives)
} Show answer Hide answer
nums.filter(fun(n: Int): Boolean { return n > 0 }) 11. A bound reference to a method with a result
Given val parser = Parser() with fun parse(s: String): Int, map lines through parser.parse using a bound reference.
class Parser {
fun parse(s: String): Int = s.length
}
fun main() {
val parser = Parser()
val lines = listOf("a", "bb", "ccc")
val sizes = lines.map { parser.parse(it) } // TODO: use a bound reference parser::parse
println(sizes)
} Show answer Hide answer
lines.map(parser::parse) 12. A constructor reference for a data class
Given data class Point(val x: Int) and xs: List<Int>, build points using ::Point.
data class Point(val x: Int)
fun main() {
val xs = listOf(1, 2, 3)
val points = xs.map { Point(it) } // TODO: use a constructor reference ::Point
println(points)
} Show answer Hide answer
xs.map(::Point) Going deeper: references and non-local return
12. Drop the wrapper lambda
Replace { it.uppercase() } with a function reference so shout(listOf("ada", "linus")) returns [ADA, LINUS].
import org.junit.Test
import org.junit.Assert
class Test {
@Test fun shout() {
Assert.assertEquals(listOf("ADA", "LINUS"), shout(listOf("ada", "linus")))
}
}
//sampleStart
fun shout(words: List<String>): List<String> =
words.map { it } // TODO: use a function reference instead of a lambda
//sampleEnd Show answer Hide answer
fun shout(words: List<String>) = words.map(String::uppercase)String::uppercase is an unbound member reference of type (String) -> String, so it fits map directly. If the lambda did anything more than forward, you’d keep the lambda.
13. Return from where?
Implement firstEven so a plain return inside forEach jumps out of the whole function with the first even number (a non-local return), or null if there is none.
import org.junit.Test
import org.junit.Assert
class Test {
@Test fun firstEven() {
Assert.assertEquals(4, firstEven(listOf(1, 3, 4, 5)))
Assert.assertEquals(null, firstEven(listOf(1, 3, 5)))
}
}
//sampleStart
fun firstEven(xs: List<Int>): Int? {
// TODO: return the first even from inside forEach, else null
return null
}
//sampleEnd Show answer Hide answer
fun firstEven(xs: List<Int>): Int? {
xs.forEach { if (it % 2 == 0) return it }
return null
}A bare return in a lambda leaves the enclosing function — a non-local return, possible because forEach is inline. To leave only the lambda (like continue), you’d write return@forEach.
Back to the lesson, References, Returns, and Anonymous Functions, or on to part five: why lambdas are free.
Comments