## Functional programming in Scala, chapter 2

object Example {
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

}