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() }.

Try it — edit, then press Run 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.

Try it — edit, then press Run 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.

Try it — edit, then press Run 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.

Try it — edit, then press Run 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.

Try it — edit, then press Run 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.

Try it — edit, then press Run 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.

Try it — edit, then press Run 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.

Try it — edit, then press Run 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.

Try it — edit, then press Run 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.

Try it — edit, then press Run 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.

Try it — edit, then press Run 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.

Try it — edit, then press Run 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].

Implement it, then press Run to check 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.

Implement it, then press Run to check 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