E
Appendix E: Chapter 5 Exercise & Challenge Solutions
Written by Massimo Carli
Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as
text.You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.
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
}
fun main() {
val input = arrayOf(1, 2, 3, 4, 5)
println(input.first {
it > 3 // 1
})
println(input.first {
it > 10 // 2
})
}
4
Exception in thread "main" java.util.NoSuchElementException: Array contains no element matching the predicate.
public inline fun <T> Array<T>.firstOrNull(
predicate: (T) -> Boolean
): T? { // 1
for (item in this) {
if (predicate(item)) {
return item
}
}
return null // 2
}
fun main() {
val input = arrayOf(1, 2, 3, 4, 5)
println(input.first {
it > 3
})
println(input.firstOrNull { // HERE
it > 10
})
}
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?
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
typealias CommandExecutor = (Command) -> Unit
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()
}
}
}
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()
}
}
class MyReader(val str: String) : Reader { // 1
var pos = 0 // 2
override fun readChar(): Char? =
if (pos < str.length) str[pos++] else null // 3
}
fun main() {
val input = MyReader("This is a String!")
println(input.readString())
}
This is a String!
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())
}
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())
}
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())
}
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
})
}
fun interface SinglePredicate<T> {
fun accept(value: T): Boolean
}
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 //
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 }
fun main() {
listOf(1, 2, 3, 4, 4, 5, 6, 7, 8, 8)
.filter(4.isEqualsPredicate())
.forEach(::println)
}
4
4
fun <T> T.isEqualsIPredicate(): SinglePredicate<T> =
SinglePredicate<T> { value -> this == value }
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:
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
}
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
.
fun <A, B> Array<A>.map(fn: (A) -> B): Array<B>
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)
}
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]) }
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>
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()
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
}
}
fun main() {
arrayOf(1, 45, 67, 78, 34, 56, 89, 121, 2, 11, 12, 13)
.all(isPrime)
.forEach(::println)
}
89
2
11
13