@@ -2,23 +2,23 @@ package fpinscalalib
2
2
3
3
import org .scalatest .{FlatSpec , Matchers }
4
4
5
- /** @param name gettingstartedwithfp
5
+ /** @param name getting_started_with_functional_programming
6
6
*/
7
7
object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scalaexercises.definitions.Section {
8
8
9
9
/**
10
10
* = Tail-recursive functions =
11
11
*
12
12
* 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
14
14
* calculates the factorial of a given number.
15
15
*
16
16
* {{{
17
17
* def factorial(n: Int): Int = {
18
18
* @annotation.tailrec
19
19
* def go(n: Int, acc: Int): Int =
20
20
* if (n <= 0) acc
21
- * else go(n- 1, n* acc)
21
+ * else go(n - 1, n * acc)
22
22
* go(n, 1)
23
23
* }
24
24
* }}}
@@ -33,19 +33,19 @@ object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scala
33
33
* return the value of `acc` if `n <= 0`).
34
34
*
35
35
* 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
37
37
* (tail call elimination) is applied when there's no additional work left to do after the recursive call returns.
38
38
*
39
39
* 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`
41
41
* function starts by calling its `loop` helper function with the initial values of `n` (the position in the sequence
42
42
* we need to calculate), and the previous and current values in the sequence.
43
43
*
44
44
* {{{
45
45
* def fib(n: Int): Int = {
46
46
* @annotation.tailrec
47
47
* def loop(n: Int, prev: Int, cur: Int): Int =
48
- * if (n = = ???) prev
48
+ * if (n < = ???) prev
49
49
* else loop(n - ???, cur, prev + cur)
50
50
* loop(n, 0, 1)
51
51
* }
@@ -55,12 +55,11 @@ object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scala
55
55
* way. What should the missing expressions for the trivial case and the recursive call be?
56
56
*/
57
57
58
-
59
58
def fibAssert (res0 : Int , res1 : Int ) {
60
59
def fib (n : Int ): Int = {
61
60
@ annotation.tailrec
62
61
def loop (n : Int , prev : Int , cur : Int ): Int =
63
- if (n = = res0) prev
62
+ if (n < = res0) prev
64
63
else loop(n - res1, cur, prev + cur)
65
64
loop(n, 0 , 1 )
66
65
}
@@ -71,7 +70,7 @@ object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scala
71
70
/**
72
71
* = Polymorphic and higher-order functions =
73
72
*
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
75
74
* `findFirst`, a function that finds the first index in an array where the key occurs (or `-1` if it doesn't exist),
76
75
* implemented more generally by accepting a function to use for testing a particular `A` value.
77
76
*
@@ -91,7 +90,63 @@ object GettingStartedWithFPSection extends FlatSpec with Matchers with org.scala
91
90
*
92
91
* To write a polymorphic function as a method, we introduce a comma-separated list of type parameters, surrounded by
93
92
* 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?
94
134
*/
95
135
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
+ }
96
151
}
97
152
0 commit comments