Chapters

Hide chapters

Functional Programming in Kotlin by Tutorials

First Edition · Android 12 · Kotlin 1.6 · IntelliJ IDEA 2022

Section I: Functional Programming Fundamentals

Section 1: 8 chapters
Show chapters Hide chapters

Appendix

Section 4: 13 chapters
Show chapters Hide chapters

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 scrambled text.

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
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now