Permalink
Browse files

Added exercise for polymorphic functions / HOF

  • Loading branch information...
1 parent a1af10a commit 385b94563f87beca05dbb16534404ebef4138feb @jdesiloniz jdesiloniz committed Jul 11, 2016
@@ -4,7 +4,7 @@ import org.scalaexercises.definitions._
/** Set of exercises based on Manning's "Functional Programming in Scala" (aka "The Red Book")
*
- * @param name fpinscala
+ * @param name fp_in_scala
*/
object FPinScalaLibrary extends Library {
override def owner = "scala-exercises"
@@ -2,23 +2,23 @@ package fpinscalalib
import org.scalatest.{FlatSpec, Matchers}
-/** @param name gettingstartedwithfp
+/** @param name getting_started_with_functional_programming
*/
object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scalaexercises.definitions.Section {
/**
* = Tail-recursive functions =
*
* We're going to introduce some of the basic techniques for how to write functional programs. Let's start by writing
- * loops using `tail-recursive functions`. For instance, let's take a look on how to functionally write a function that
+ * loops using tail-recursive functions. For instance, let's take a look on how to functionally write a function that
* calculates the factorial of a given number.
*
* {{{
* def factorial(n: Int): Int = {
* @annotation.tailrec
* def go(n: Int, acc: Int): Int =
* if (n <= 0) acc
- * else go(n-1, n*acc)
+ * else go(n - 1, n * acc)
* go(n, 1)
* }
* }}}
@@ -33,19 +33,19 @@ object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scala
* return the value of `acc` if `n <= 0`).
*
* Scala is able to detect this sort of self-recursion and compiles it to the same sort of bytecode as would be emitted
- * by a `while` loop, as long as the recursive call is in `tail position`. The basic idea is that this optimization
+ * by a `while` loop, as long as the recursive call is in tail position. The basic idea is that this optimization
* (tail call elimination) is applied when there's no additional work left to do after the recursive call returns.
*
* Let's do the same with a function to call the nth number from the Fibonacci sequence. The first two numbers
- * are 0 and 1. Then, the nth number is always the sum of the previous two, i.e.: 0, 1, 1, 2, 3, 5... The `fib`
+ * are 0 and 1. Then, the nth number is always the sum of the previous two, i.e.: 0, 1, 1, 2, 3, 5... The fib`
* function starts by calling its `loop` helper function with the initial values of `n` (the position in the sequence
* we need to calculate), and the previous and current values in the sequence.
*
* {{{
* def fib(n: Int): Int = {
* @annotation.tailrec
* def loop(n: Int, prev: Int, cur: Int): Int =
- * if (n == ???) prev
+ * if (n <= ???) prev
* else loop(n - ???, cur, prev + cur)
* loop(n, 0, 1)
* }
@@ -55,12 +55,11 @@ object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scala
* way. What should the missing expressions for the trivial case and the recursive call be?
*/
-
def fibAssert(res0: Int, res1: Int) {
def fib(n: Int): Int = {
@annotation.tailrec
def loop(n: Int, prev: Int, cur: Int): Int =
- if (n == res0) prev
+ if (n <= res0) prev
else loop(n - res1, cur, prev + cur)
loop(n, 0, 1)
}
@@ -71,7 +70,7 @@ object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scala
/**
* = Polymorphic and higher-order functions =
*
- * Polymorphic functions allow us to write code that works for **any** type it's given. For instance, take a look at
+ * Polymorphic functions allow us to write code that works for any type it's given. For instance, take a look at
* `findFirst`, a function that finds the first index in an array where the key occurs (or `-1` if it doesn't exist),
* implemented more generally by accepting a function to use for testing a particular `A` value.
*
@@ -91,7 +90,63 @@ object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scala
*
* To write a polymorphic function as a method, we introduce a comma-separated list of type parameters, surrounded by
* square brackets (here, just a single `[A]`), following the name of the function, in this case `findFirst`.
+ *
+ * = Higher-order functions =
+ *
+ * Let's see an example of a higher-order function (HOF):
+ * {{{
+ * def formatResult(name: String, n: Int, f: Int => Int) = {
+ * val msg = "The %s of %d is %d."
+ * msg.format(name, n, f(n))
+ * }
+ * }}}
+ *
+ * Our `formatResult` HOF takes another function called `f`. We give a type to `f`, as we would for any other parameter.
+ * Its type is `Int => Int`, which indicates that `f` expects an integer argument and will also return an integer.
+ *
+ * Let's create a polymorphic, tail-recursive higher-order function that checks if an array is sorted, according to
+ * a given comparison function that will be passed as a parameter:
+ *
+ * {{{
+ * def isSorted[A](as: Array[A], ordering: (A, A) => Boolean): Boolean = {
+ * @annotation.tailrec
+ * def go(n: Int): Boolean =
+ * if (n >= as.length - 1) true
+ * else if (ordering(as(n), as(n + 1))) false
+ * else go(n + 1)
+ *
+ * go(0)
+ * }
+ * }}}
+ *
+ * When using HOFs, it's often convenient to be able to call these functions with anonymous functions, rather than
+ * having to supply some existing named function. For instance, using the previously implemented `findFirst`:
+ *
+ * {{{
+ * findFirst(Array(7, 9, 13), (x: Int) => x == 9)
+ * }}}
+ *
+ * The syntax `(x: Int) => x == 9` is a `function literal` or `anonymous function`, defining a function that takes one
+ * argument `x` of type `Int` and returns a `Boolean` indicating whether `x` is equal to 9.
+ *
+ * Let's do the same with `isSorted`. After taking a detailed look at its implementation, what would be the results of
+ * applying the following anonymous functions to it?
*/
+ def isSortedAssert(res0: Boolean, res1: Boolean, res2: Boolean): Unit = {
+ def isSorted[A](as: Array[A], ordering: (A, A) => Boolean): Boolean = {
+ @annotation.tailrec
+ def go(n: Int): Boolean =
+ if (n >= as.length - 1) true
+ else if (ordering(as(n), as(n + 1))) false
+ else go(n + 1)
+
+ go(0)
+ }
+
+ isSorted(Array(1, 3, 5, 7), (x: Int, y: Int) => x > y) shouldBe res0
+ isSorted(Array(7, 5, 3, 1), (x: Int, y: Int) => x < y) shouldBe res1
+ isSorted(Array("Scala", "Exercises"), (x: String, y: String) => x.length > y.length) shouldBe res2
+ }
}
@@ -0,0 +1,8 @@
+package exercises
+
+/**
+ * Created by Javi on 11/7/16.
+ */
+class Test {
+
+}
@@ -0,0 +1,8 @@
+package exercises.fpinscala
+
+/**
+ * Created by Javi on 11/7/16.
+ */
+class GettingStartedWithFPSpec {
+
+}

0 comments on commit 385b945

Please sign in to comment.