Thursday, October 22, 2015

Functional programming in Scala, chapter 2

object Example {
  // recursion instead of loop
  def factorial(n: Int): Int = {
    // this recursive function is compiled into loops by scalac
    @annotation.tailrec
    def go(n: Int, acc: Int): Int = n match {
      case 0 => acc
      case _ => go(n - 1, n * acc)
    }
    go(n, 1)
  }

  // Exercise 2.1, write a recursive function to get the nth Fibonacci number
  def fibonacci(k: Int): Int = {
    @annotation.tailrec
    def helper(i: Int, j: Int, counter: Int): Int = {
      if (counter == k) i
      else helper(j, i + j, counter + 1)
    }
    helper(0, 1, 0)
  }

  // parametric polymorphism
  // generic programming, abstracting over the type, type parameters
  def formatResult[A, B](name: String, in: A, f: A => B): Unit = {
    println("%s %s is %s".format(name, in, f(in)))
  }

  // type variables
  def findFirst[A](as: Array[A], p: A => Boolean): Int = {
    @annotation.tailrec
    def helper(idx: Int): Int = {
      if (idx >= as.length) -1
      else if (p(as(idx))) idx
      else helper(idx + 1)
    }
    helper(0)
  }

  // Exercise 2.2, check if an array is sorted
  def isSorted[A](as: Array[A], order: (A, A) => Boolean): Boolean = {
    as.sliding(2).forall(xx => order(xx(0), xx(1)))
  }

  // partial application
  def partial1[A, B, C](a: A, f: (A, B) => C): B => C = {
    (b: B) => f(a, b)
  }

  // Exercise 2.3 curry
  def curry[A, B, C](f: (A, B) => C): A => B => C = {
    (a: A) => (b: B) => f(a, b)
  }

  // Exercise 2.4 uncurry
  def uncurry[A, B, C](f: A => B => C): (A, B) => C = {
    (a, b) => f(a)(b)
  }

  // Exercise 2.5 function composition
  def compose[A, B, C](f: A => B, g: B => C): A => C = {
    a => g(f(a))
  }
}

object Test {

  import Example._

  formatResult("Factorial of", 5, factorial) // 120
  formatResult("Fibonacci number at", 5, fibonacci) // 5
  findFirst(Array(1, 2, 3, 4), (x: Int) => {
    x % 2 == 0
  })
  isSorted(Array(1, 2, 3, 4), (a: Int, b: Int) => a < b) // true
  isSorted(Array(1, 2, 3, 7, 4), (a: Int, b: Int) => a < b)

  // false

  def sum(a: Int, b: Int) = a + b

  curry(sum)(1)(2)

  // 3
  def sum1(a: Int) = (b: Int) => a + b

  uncurry(sum1)(1, 2)

  // 3

  def add1(a: Int) = a + 1

  def add2(a: Int) = a + 2

  compose(add1, add2)(3) // 6
}

0 comments: