I
Appendix I: Chapter 9 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 9.1
In this chapter, you learned what the Optional<T>
data type is, and you implemented some important functions for it like lift
, empty
, map
and flatMap
. Kotlin defines its own optional type represented by ?
. How would you implement the lift
, empty
and getOrDefault
functions for it?
Exercise 9.1 solution
You can implement the previous functions like this:
fun <T : Any> T.lift(): T? = this // 1
fun <T : Any> T.empty(): T? = null // 2
fun <T : Any> T?.getOrDefault(defaultValue: T): T =
this ?: defaultValue // 3
fun main() {
val optStr = "10".lift() // 1
optStr pipe ::println
val empty = String.empty() // 2
empty pipe ::println
optStr // 3
.getOrDefault("Default")
.pipe(::println)
empty // 4
.getOrDefault("Default")
.pipe(::println)
}
10 // 1
null // 2
10 // 3
Default // 4
Exercise 9.2
In this chapter, you learned what the Optional<T>
data type is, and you implemented some important functions for it like lift
, empty
, map
and flatMap
. Kotlin defines its own optional type represented by ?
. How would you implement the map
and flatMap
functions?
Exercise 9.2 solution
A possible implementation of map
for the Kotlin optional type is:
fun <A : Any, B : Any> A?.map(fn: Fun<A, B>): B? =
if (this != null) fn(this).lift() else null
fun <A : Any, B : Any> A?.flatMap(fn: Fun<A, B?>): B? =
if (this != null) fn(this)?.lift() else null
Exercise 9.3
How would you replicate the example you implemented in OptionalTest.kt using T?
instead of Optional<T>
? Use the solutions of Exercise 9.1 and Exercise 9.2 to implement this example.
Exercise 9.3 solution
You can test the code you created in Exercise 9.1 and Exercise 9.2 by running the following code:
fun strToInt(value: String): Int? = // 1
try {
value.toInt().lift()
} catch (nfe: NumberFormatException) {
null
}
fun <T : Any> T?.getOrDefault(defaultValue: T): T = // 2
if (this == null) defaultValue else this
fun main() {
"10" // 3
.lift()
.flatMap(::strToInt)
.map(::double)
.getOrDefault(-1)
.pipe(::println)
"10sa" // 4
.lift()
.flatMap(::strToInt)
.map(::double)
.getOrDefault(-1)
.pipe(::println)
}
20
-1
Exercise 9.4
Implement a function that reverses a String
using one of the folding functions you’ve implemented in this chapter.
Exercise 9.4 solution
A String
is just an array of Char
s. This means that a possible implementation for the reverse
function is:
fun reverse(str: String) =
str.toCharArray().toList() // 1
.declarativeFoldRight(StringBuilder()) { c, acc -> // 2
acc.append(c) // 3
acc
}.toString() // 4
fun main() {
reverse("supercalifragilisticexpialidocious") pipe ::println
}
suoicodilaipxecitsiligarfilacrepus
Exercise 9.5
In this chapter, you implemented declarativeFold
and declarativeFoldRight
as extension functions for List<T>
. How would you implement them for Iterable<T>
?
Exercise 9.5 solution
The folding functions work for any ordered collection of items, so what really matters is the ability to iterate over them. A possible implementation for declarativeFold
on Iterable
is:
fun <T, S> Iterable<T>.iterableFold(
start: S,
combineFunc: (S, T) -> S
): S { // 1
tailrec fun helper(iterator: Iterator<T>, acc: S): S { // 2
if (!iterator.hasNext()) { // 3
return acc
}
return helper(iterator, combineFunc(acc, iterator.next())) // 4
}
return helper(iterator(), start) // 5
}
fun <T, S> Iterable<T>.iterableFoldRight(
start: S,
combineFunc: (T, S) -> S
): S {
fun helper(iterator: Iterator<T>): S {
if (!iterator.hasNext()) {
return start
}
return combineFunc(iterator.next(), helper(iterator))
}
return helper(iterator())
}
fun main() {
"supercalifragilisticexpialidocious".asIterable()
.iterableFoldRight(StringBuilder()) { item, acc ->
acc.append(item)
acc
} pipe ::println
"supercalifragilisticexpialidocious".asIterable()
.iterableFold(StringBuilder()) { acc, item ->
acc.append(item)
acc
} pipe ::println
}
suoicodilaipxecitsiligarfilacrepus
supercalifragilisticexpialidocious
Challenge 9.1: Filtering
How would you implement a filter
function on a List<T>
using fold
or foldRight
? You can name it filterFold
. Remember that given:
typealias Predicate<T> = (T) -> Boolean
fun <T> List<T>.filterFold(predicate: Predicate<T>): List<T> {
// Implementation
}
Challenge 9.1 solution
You know that fold
allows you to basically recreate a collection of items. If you add an item after the evaluation of a predicate, you basically implement the filter
function. One possible solution is:
fun <T> List<T>.filterFold(predicate: Predicate<T>): List<T> =
fold(mutableListOf()) { acc, item -> // 1
if (predicate(item)) { // 2
acc.add(item) // 3
}
acc
}
fun main() {
listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.filterFold { it % 2 == 0 }
.forEach(::println)
}
2
4
6
8
10
Challenge 9.2
How would you implement the length
function for a List<T>
that returns its size using fold
or foldRight
?
Challenge 9.2 solution
A possible implementation is:
fun <T> List<T>.length(): Int =
fold(0) { acc, _ ->
acc + 1
}
fun main() {
val list = List<Int>(37) { it }
list.length() pipe ::println
}
37
Challenge 9.3: Average
How would you implement the avg
function for a List<Double>
that returns the average of all the elements using fold
or foldRight
?
Challenge 9.3 solution
The solution here is simple, and is basically the implementation of the definition of average: the sum of all the elements divided by the number of elements:
fun List<Double>.average(): Double =
fold(0.0) { acc, item -> acc + item } /
fold(0.0) { acc, _ -> acc + 1 }
fun main() {
val list = List<Int>(37) { it }
list.average() pipe ::println
}
18.0
Challenge 9.4: Last
How would you implement the lastFold
function for a List<T>
that returns the last element using fold
or foldRight
? What about firstFold
?
Challenge 9.4 solution
One possible implementation is:
fun <T> List<T>.lastFold(): T? =
fold(null as T?) { _, item -> item }
fun main() {
val list = List<Int>(37) { it }
list.lastFold() pipe ::println
val empty = emptyList<Int>()
empty.lastFold() pipe ::println
}
36
null
fun <T> List<T>.firstFold(): T? =
foldRight(null as T?) { item, acc -> item }
val list = List<Int>(37) { it }
list.firstFold() pipe ::println
0