Skip to content

Commit 385b945

Browse files
author
Javier de Silóniz Sandino
committed
Added exercise for polymorphic functions / HOF
1 parent a1af10a commit 385b945

File tree

4 files changed

+81
-10
lines changed

4 files changed

+81
-10
lines changed

src/main/scala/fpinscalalib/FPinScalaLibrary.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import org.scalaexercises.definitions._
44

55
/** Set of exercises based on Manning's "Functional Programming in Scala" (aka "The Red Book")
66
*
7-
* @param name fpinscala
7+
* @param name fp_in_scala
88
*/
99
object FPinScalaLibrary extends Library {
1010
override def owner = "scala-exercises"

src/main/scala/fpinscalalib/GettingStartedWithFPSection.scala

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@ package fpinscalalib
22

33
import org.scalatest.{FlatSpec, Matchers}
44

5-
/** @param name gettingstartedwithfp
5+
/** @param name getting_started_with_functional_programming
66
*/
77
object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scalaexercises.definitions.Section {
88

99
/**
1010
* = Tail-recursive functions =
1111
*
1212
* We're going to introduce some of the basic techniques for how to write functional programs. Let's start by writing
13-
* loops using `tail-recursive functions`. For instance, let's take a look on how to functionally write a function that
13+
* loops using tail-recursive functions. For instance, let's take a look on how to functionally write a function that
1414
* calculates the factorial of a given number.
1515
*
1616
* {{{
1717
* def factorial(n: Int): Int = {
1818
* @annotation.tailrec
1919
* def go(n: Int, acc: Int): Int =
2020
* if (n <= 0) acc
21-
* else go(n-1, n*acc)
21+
* else go(n - 1, n * acc)
2222
* go(n, 1)
2323
* }
2424
* }}}
@@ -33,19 +33,19 @@ object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scala
3333
* return the value of `acc` if `n <= 0`).
3434
*
3535
* Scala is able to detect this sort of self-recursion and compiles it to the same sort of bytecode as would be emitted
36-
* by a `while` loop, as long as the recursive call is in `tail position`. The basic idea is that this optimization
36+
* by a `while` loop, as long as the recursive call is in tail position. The basic idea is that this optimization
3737
* (tail call elimination) is applied when there's no additional work left to do after the recursive call returns.
3838
*
3939
* Let's do the same with a function to call the nth number from the Fibonacci sequence. The first two numbers
40-
* 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`
40+
* 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`
4141
* function starts by calling its `loop` helper function with the initial values of `n` (the position in the sequence
4242
* we need to calculate), and the previous and current values in the sequence.
4343
*
4444
* {{{
4545
* def fib(n: Int): Int = {
4646
* @annotation.tailrec
4747
* def loop(n: Int, prev: Int, cur: Int): Int =
48-
* if (n == ???) prev
48+
* if (n <= ???) prev
4949
* else loop(n - ???, cur, prev + cur)
5050
* loop(n, 0, 1)
5151
* }
@@ -55,12 +55,11 @@ object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scala
5555
* way. What should the missing expressions for the trivial case and the recursive call be?
5656
*/
5757

58-
5958
def fibAssert(res0: Int, res1: Int) {
6059
def fib(n: Int): Int = {
6160
@annotation.tailrec
6261
def loop(n: Int, prev: Int, cur: Int): Int =
63-
if (n == res0) prev
62+
if (n <= res0) prev
6463
else loop(n - res1, cur, prev + cur)
6564
loop(n, 0, 1)
6665
}
@@ -71,7 +70,7 @@ object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scala
7170
/**
7271
* = Polymorphic and higher-order functions =
7372
*
74-
* Polymorphic functions allow us to write code that works for **any** type it's given. For instance, take a look at
73+
* Polymorphic functions allow us to write code that works for any type it's given. For instance, take a look at
7574
* `findFirst`, a function that finds the first index in an array where the key occurs (or `-1` if it doesn't exist),
7675
* implemented more generally by accepting a function to use for testing a particular `A` value.
7776
*
@@ -91,7 +90,63 @@ object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scala
9190
*
9291
* To write a polymorphic function as a method, we introduce a comma-separated list of type parameters, surrounded by
9392
* square brackets (here, just a single `[A]`), following the name of the function, in this case `findFirst`.
93+
*
94+
* = Higher-order functions =
95+
*
96+
* Let's see an example of a higher-order function (HOF):
97+
* {{{
98+
* def formatResult(name: String, n: Int, f: Int => Int) = {
99+
* val msg = "The %s of %d is %d."
100+
* msg.format(name, n, f(n))
101+
* }
102+
* }}}
103+
*
104+
* Our `formatResult` HOF takes another function called `f`. We give a type to `f`, as we would for any other parameter.
105+
* Its type is `Int => Int`, which indicates that `f` expects an integer argument and will also return an integer.
106+
*
107+
* Let's create a polymorphic, tail-recursive higher-order function that checks if an array is sorted, according to
108+
* a given comparison function that will be passed as a parameter:
109+
*
110+
* {{{
111+
* def isSorted[A](as: Array[A], ordering: (A, A) => Boolean): Boolean = {
112+
* @annotation.tailrec
113+
* def go(n: Int): Boolean =
114+
* if (n >= as.length - 1) true
115+
* else if (ordering(as(n), as(n + 1))) false
116+
* else go(n + 1)
117+
*
118+
* go(0)
119+
* }
120+
* }}}
121+
*
122+
* When using HOFs, it's often convenient to be able to call these functions with anonymous functions, rather than
123+
* having to supply some existing named function. For instance, using the previously implemented `findFirst`:
124+
*
125+
* {{{
126+
* findFirst(Array(7, 9, 13), (x: Int) => x == 9)
127+
* }}}
128+
*
129+
* The syntax `(x: Int) => x == 9` is a `function literal` or `anonymous function`, defining a function that takes one
130+
* argument `x` of type `Int` and returns a `Boolean` indicating whether `x` is equal to 9.
131+
*
132+
* Let's do the same with `isSorted`. After taking a detailed look at its implementation, what would be the results of
133+
* applying the following anonymous functions to it?
94134
*/
95135

136+
def isSortedAssert(res0: Boolean, res1: Boolean, res2: Boolean): Unit = {
137+
def isSorted[A](as: Array[A], ordering: (A, A) => Boolean): Boolean = {
138+
@annotation.tailrec
139+
def go(n: Int): Boolean =
140+
if (n >= as.length - 1) true
141+
else if (ordering(as(n), as(n + 1))) false
142+
else go(n + 1)
143+
144+
go(0)
145+
}
146+
147+
isSorted(Array(1, 3, 5, 7), (x: Int, y: Int) => x > y) shouldBe res0
148+
isSorted(Array(7, 5, 3, 1), (x: Int, y: Int) => x < y) shouldBe res1
149+
isSorted(Array("Scala", "Exercises"), (x: String, y: String) => x.length > y.length) shouldBe res2
150+
}
96151
}
97152

src/test/scala/exercises/Test.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package exercises
2+
3+
/**
4+
* Created by Javi on 11/7/16.
5+
*/
6+
class Test {
7+
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package exercises.fpinscala
2+
3+
/**
4+
* Created by Javi on 11/7/16.
5+
*/
6+
class GettingStartedWithFPSpec {
7+
8+
}

0 commit comments

Comments
 (0)