E.
Appendix E: Chapter 5 Exercise & Challenge Solutions
Written by Massimo Carli
Exercise 5.1
Kotlin provides you with first, which returns the first element of Iterable<T> for which a predicate you provide as an input evaluates to true. Remember that Iterable<T> is the abstraction of all the collections providing an Iterator<T> implementation.
public interface Iterable<out T> {
public operator fun iterator(): Iterator<T>
}
Iterator<T> allows you to iterate over all the elements of a collection in a way that doesn’t depend on the collection implementation itself:
public interface Iterator<out T> {
public operator fun next(): T
public operator fun hasNext(): Boolean
}
The current first signature is:
public inline fun <T> Iterable<T>.first(predicate: (T) -> Boolean): T
Kotlin doesn’t allow you to override the current extension function on Iterable<T>. So, how would you implement first on Array<T>?
The current implementation of first throws an exception if the collection is empty, so there’s no first T. How would you implement the function firstOrNull on Array<T> returning null in such a case?
Exercise 5.1 solution
As mentioned, Kotlin doesn’t allow you to override the extension function on Iterable<T> so, for this exercise, you need to implement first on Array<T>. A possible solution is:
public inline fun <T> Array<T>.first(
predicate: (T) -> Boolean
): T { // 1
for (item in this) { // 2
if (predicate(item)) { // 3
return item // 4
}
}
throw NoSuchElementException("Array contains no element matching the predicate.") // 5
}
The code is quite simple. In it, you:
- Define
firstas an extension function forArray<T>, accepting a predicate as an input parameter and returning a value of typeT. - Iterate over the values in
Array<T>. - Evaluate the predicate on the current
item. - Return the value for which
predicateevaluates totrue. - Throw a
NoSuchElementExceptionifpredicatenever evaluates totrue.
Test first with the following code:
fun main() {
val input = arrayOf(1, 2, 3, 4, 5)
println(input.first {
it > 3 // 1
})
println(input.first {
it > 10 // 2
})
}
When you run the previous code, you get:
4
Exception in thread "main" java.util.NoSuchElementException: Array contains no element matching the predicate.
Note how:
- Passing the predicate
{ it > 3}, you get the value4. - Using
{ it > 30}, you getNoSuchElementException.
What if you don’t want to throw an exception but implement first in a more functional way without NoSuchElementException as a side effect?
A possible solution is the following firstOrNull implementation:
public inline fun <T> Array<T>.firstOrNull(
predicate: (T) -> Boolean
): T? { // 1
for (item in this) {
if (predicate(item)) {
return item
}
}
return null // 2
}
The code isn’t very different from the first one. Here, you:
- Have the optional
T?as a return type. - Return
nullif there’s novaluefor the given predicate.
Test firstOrNull by running the following code:
fun main() {
val input = arrayOf(1, 2, 3, 4, 5)
println(input.first {
it > 3
})
println(input.firstOrNull { // HERE
it > 10
})
}
In the previous code, you use firstOrNull instead of first. Run this code, and you get:
4
null
Exercise 5.2
The command pattern is another important design pattern that defines abstractions like Command and CommandExecutor. Command abstracts every possible operation that CommandExecutor can run. In other words, a Command represents a task and you can pass a Command to a CommandExecutor to run it. How would you represent them in a functional way?
Optionally, can you also provide a way to “redo” the most recent Command?
Exercise 5.2 solution
Command is basically a way to abstract the concept of a task. To represent it in a functional way, write:
typealias Command = () -> Unit
CommandExecutor is the component responsible for the execution of Command. A simple way to represent it is:
typealias CommandExecutor = (Command) -> Unit
You can create a possible implementation like the following:
class MyCommandExecutor : CommandExecutor { // 1
val commandHistory = mutableListOf<Command>() // 2
override fun invoke(command: Command) { // 3
command.run {
commandHistory.add(this)
this()
}
}
fun redo() { // 4
commandHistory.lastOrNull()?.let {
it()
}
}
}
In the previous code, you:
- Define
MyCommandExecutoras an implementation ofCommandExecutor. - Initialize
commandHistory, which will contain the history of all the commands you execute. This isn’t necessary, but usually, the command pattern allows you to runCommandmultiple times. - Override
invoke, passingCommandas a parameter. In the body, you simply invokeCommandafter saving its reference incommandHistory. - Provide
redo()as a chance to execute the last command again.
Usually, the command pattern also allows you to undo Commands, but that’s out of the scope of this book.
Exercise 5.3
Can you implement the following Reader interface as a functional interface? How would you test it?
interface Reader {
fun readChar(): Char?
fun readString(): String {
TODO("Call readChar() until it returns null")
}
}
Exercise 5.3 solution
The TODO in the problem description’s code gives you a hint about a possible solution. One option is the following:
fun interface Reader {
fun readChar(): Char?
fun readString(): String {
val result = StringBuilder()
do {
val nextChar = readChar()
if (nextChar != null) {
result.append(nextChar)
}
} while (nextChar != null)
return result.toString()
}
}
In this code, you’re basically implementing readString using StringBuilder appending Char, which you get from readChar until you get null. In that case, you return what’s in StringBuilder by invoking toString.
So far, so good! But how do you test it? Does making Reader a functional interface help? Actually, it doesn’t.
To test Reader, use the following implementation:
class MyReader(val str: String) : Reader { // 1
var pos = 0 // 2
override fun readChar(): Char? =
if (pos < str.length) str[pos++] else null // 3
}
Here, you:
- Create
MyReaderas a simple implementation ofReader, receivingStringas an input parameter. - Initialize
posto0, which is the position of the firstCharyou want to return fromreadChar. - Check if
posis in the boundary of theStringprovided as input. If so, you return theCharin it and incrementpos. Otherwise, you returnnull.
To test how this works, just run the following code:
fun main() {
val input = MyReader("This is a String!")
println(input.readString())
}
Getting:
This is a String!
The initial question remains: What advantage do you have by making Reader a functional interface? It gives you two advantages:
- You can use the handy
Type { /* lambda */}syntax. - You can pass a
Readerimplementation instance using a simple lambda expression as an input parameter of another function. In that case, Kotlin would infer the right type.
You could test the first case with the following code:
fun main() {
val inputString = "This is a String!"
var pos = 0
val input = Reader {
if (pos < inputString.length) inputString[pos++] else null
}
println(input.readString())
}
In this case, the Reader implementation captures the value of pos, which basically represents its own state. What if you had the following code instead?
fun main() {
val inputString = "This is a String!"
var pos = 0
val input = Reader {
if (pos < inputString.length) inputString[pos++] else null
}
val input2 = Reader {
if (pos < inputString.length) inputString[pos++] else null
}
println(input.readString())
println(input2.readString())
}
Only the first println would actually print something. To make things work, you’d need a second variable for the state of Reader for input2, like this:
fun main() {
val inputString = "This is a String!"
var pos = 0
var pos2 = 0
val input = Reader {
if (pos < inputString.length) inputString[pos++] else null
}
val input2 = Reader {
if (pos2 < inputString.length) inputString[pos2++] else null
}
println(input.readString())
println(input2.readString())
}
This method is confusing and error-prone.
You’d have the same problem, even if you pass Reader as an input parameter of another function, like this:
fun consumeReader(reader: Reader) {
println(reader.readString())
}
fun main() {
var pos = 0
val inputString = "This is a String!"
consumeReader({
if (pos < inputString.length) inputString[pos++] else null
})
}
The only advantage here is that you don’t need to specify the Reader type before the lambda because the Kotlin compiler infers it for you.
So, what’s the lesson here? As you learned in the chapter, typealias allows you to give a name to an existing type. In the case of function types, you can assume they already exist somewhere, and typealias is a tool to reduce the quantity of code you have to write, especially for generic types. Types like (T)-> Boolean, (T,T) -> T and so on already exist, even if you don’t declare them explicitly.
The type you define using a functional interface is a new type, and the implementations must specify the name explicitly. They don’t exist before that, even if they basically define an existing type. As proved in the chapter, consider the functional interface:
fun interface SinglePredicate<T> {
fun accept(value: T): Boolean
}
This is basically equivalent to (T)-> Boolean, but it’s a completely different type. The extension function you define for SinglePredicate<T> doesn’t work for (T)-> Boolean, and vice versa. If you have a parameter of type SinglePredicate<T>, you can’t pass a lambda expression of type (T)-> Boolean.
As a last note, remember that typealiases aren’t visible from Java, but functional interfaces are.
Exercise 5.4
Implement an extension function isEqualsPredicate on the generic type T that returns a predicate that tests if a given value is equal to the same T. The signature should be the following:
fun <T> T.isEqualsPredicate(): (T) -> Boolean //
How would the same function be different if you use the following functional interface?
fun interface SinglePredicate<T> {
fun accept(other: T): Boolean
}
Exercise 5.4 solution
A possible solution to the initial problem is the following:
fun <T> T.isEqualsPredicate(): (T) -> Boolean =
{ value -> this == value }
Test this with the following code:
fun main() {
listOf(1, 2, 3, 4, 4, 5, 6, 7, 8, 8)
.filter(4.isEqualsPredicate())
.forEach(::println)
}
Run it, and you get:
4
4
If you use the functional interface SinglePredicate<T>, you have:
fun <T> T.isEqualsIPredicate(): SinglePredicate<T> =
SinglePredicate<T> { value -> this == value }
Note how the name of the extension function isn’t isEqualsPredicate. This avoids conflicts with the previous one because they’re both acting on the same receiver, T, and not because of the different return type.
Exercise 5.5
Can you implement the same logic for implementing the example in the Imperative vs. declarative approach section using the definitions of Predicate1<T> and filterWithPredicate? Given a list of email addresses, you need to:
- Filter the valid email addresses.
- Filter the email addresses with the right length.
- Take the first five of them.
Exercise 5.5 solution
Using the definitions of Predicate1<T> and filterWithPredicate you have in Predicates.kt and what’s in Imperative.kt and Declarative.kt, you can write:
val isValidEmail: Predicate1<String> = // 1
Predicate1 { value -> EMAIL_REG_EX.matches(value) }
fun isLongerThan(length: Int): Predicate1<String> = // 2
Predicate1 { value -> value.length > length }
fun main() {
emails
.filterWithPredicate(isValidEmail and isLongerThan(10)) // 3
.take(5) // 4
.forEach(::println) // 5
}
In this code, you:
- Define
isValidEmailas aPredicate1<String>that checks the validity of email addresses. - Create
isLongerThanas a function returning aPredicate1<String>that checks the length ofString. - Use
filterWithPredicate, passing in as input the logicandbetweenisValidEmailandisLongerThan. - Take the first
5successful email addresses. - Print the result.
Running the previous code, you get:
email@emmmaail.com
mike@mcarli.it
first.second@ggg.com
test@test.co.uk
fp_is_great@funprog.com
Challenge 5.1: Mapping is important
In the chapter, you learned how to implement different types of higher-order functions, and in the next chapter, you’ll see many more. A very important one is called map. This is a function that applies a given function fn of type (A) -> B to all the elements of a collection of items of type A, getting a collection of items of type B.
Can you implement the function map for the Array<A> type with the following signature?
fun <A, B> Array<A>.map(fn: (A) -> B): Array<B>
When you run this code:
fun main() {
val square = { a: Int -> a * a }
val toString = { a: Int -> "This is $a" }
arrayOf(1, 2, 3)
.map(square)
.forEach(::println)
arrayOf(1, 2, 3)
.map(toString)
.forEach(::println)
}
You should get:
1
4
9
This is 1
This is 2
This is 3
Challenge 5.1 solution
A possible implementation of map for Array<A> is the following:
inline fun <A, reified B> Array<A>.map(fn: (A) -> B): Array<B> =
Array(this.size) { fn(this[it]) }
This simply uses the constructor for Array to initialize all the values after applying fn to all the elements in the original array.
Note the use of reified for the type B. This is because the Kotlin compiler needs to retain some information about the type B to properly initialize Array in output.
Running main in the challenge, you get the expected output:
1
4
9
This is 1
This is 2
This is 3
Challenge 5.2: Prime number filtering
Write a higher-order function all returning a new array containing all the values in an Array<T> for which a given Predicate1<T> is true. You can find the Predicate1<T> definition in Predicates.kt.
fun <T> Array<T>.all(predicate: Predicate1<T>) : Array<T>
Then, use it to return all the positive prime values in Array<Int>. A number is prime if it’s not evenly divisible with any number other than 1 and itself.
Challenge 5.2 solution
You can implement all in many different ways. One of them is:
inline fun <reified T> Array<T>.all(
predicate: Predicate1<T>
): Array<T> = filter { predicate.accept(it) }.toTypedArray()
You leverage the existing filter, adapting Predicate1<T> to the type (T) -> Boolean it requires. Then, you use toTypedArray() to make the List<T> an Array<T>. This requires more type information and is the reason for the reified keyword.
The following is a possible implementation of Predicate1<Int> testing if a given Int is a prime number:
val isPrime = Predicate1<Int> { value ->
if (value <= 3) value > 1
else if (value % 2 == 0 || value % 3 == 0) false
else {
var i = 5
while (i * i <= value) {
if (value % i == 0 || value % (i + 2) == 0)
return@Predicate1 false
i += 6
}
true
}
}
This predicate tests if the Int is a prime number or not.
Finally, you can test it with:
fun main() {
arrayOf(1, 45, 67, 78, 34, 56, 89, 121, 2, 11, 12, 13)
.all(isPrime)
.forEach(::println)
}
Running the previous code, you get:
89
2
11
13