Skip to content

Commit c26221e

Browse files
committed
Add HigherOrderFunctions section
1 parent 6182139 commit c26221e

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

src/main/scala/scalatutorial/sections/HigherOrderFunctions.scala

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,156 @@ package scalatutorial.sections
33
/** @param name higher_order_functions */
44
object HigherOrderFunctions extends ScalaTutorialSection {
55

6+
/**
7+
* = Higher-Order Functions =
8+
*
9+
* Functional languages treat functions as ''first-class values''.
10+
*
11+
* This means that, like any other value, a function
12+
* can be passed as a parameter and returned as a result.
13+
*
14+
* This provides a flexible way to compose programs.
15+
*
16+
* Functions that take other functions as parameters or that return functions
17+
* as results are called ''higher order functions''.
18+
*
19+
* = Motivation =
20+
*
21+
* Consider the following programs.
22+
*
23+
* Take the sum of the integers between `a` and `b`:
24+
*
25+
* {{{
26+
* def sumInts(a: Int, b: Int): Int =
27+
* if (a > b) 0 else a + sumInts(a + 1, b)
28+
* }}}
29+
*
30+
* Take the sum of the cubes of all the integers between `a`
31+
* and `b`:
32+
*
33+
* {{{
34+
* def cube(x: Int): Int = x * x * x
35+
*
36+
* def sumCubes(a: Int, b: Int): Int =
37+
* if (a > b) 0 else cube(a) + sumCubes(a + 1, b)
38+
* }}}
39+
*
40+
* Take the sum of the factorials of all the integers between `a`
41+
* and `b`:
42+
*
43+
* {{{
44+
* def sumFactorials(a: Int, b: Int): Int =
45+
* if (a > b) 0 else factorial(a) + sumFactorials(a + 1, b)
46+
* }}}
47+
*
48+
* Note how similar these methods are.
49+
* Can we factor out the common pattern?
50+
*
51+
* = Summing with Higher-Order Functions =
52+
*
53+
* Let's define:
54+
*
55+
* {{{
56+
* def sum(f: Int => Int, a: Int, b: Int): Int =
57+
* if (a > b) 0
58+
* else f(a) + sum(f, a + 1, b)
59+
* }}}
60+
*
61+
* We can then write:
62+
*
63+
* {{{
64+
* def id(x: Int): Int = x
65+
* def sumInts(a: Int, b: Int) = sum(id, a, b)
66+
* def sumCubes(a: Int, b: Int) = sum(cube, a, b)
67+
* def sumFactorials(a: Int, b: Int) = sum(factorial, a, b)
68+
* }}}
69+
*
70+
* = Function Types =
71+
*
72+
* The type `A => B` is the type of a ''function'' that
73+
* takes an argument of type `A` and returns a result of
74+
* type `B`.
75+
*
76+
* So, `Int => Int` is the type of functions that map integers to integers.
77+
*
78+
* = Anonymous Functions =
79+
*
80+
* Passing functions as parameters leads to the creation of many small functions.
81+
*
82+
* Sometimes it is tedious to have to define (and name) these functions using `def`.
83+
*
84+
* Compare to strings: We do not need to define a string using `val`. Instead of:
85+
*
86+
* {{{
87+
* val str = "abc"; println(str)
88+
* }}}
89+
*
90+
* We can directly write:
91+
*
92+
* {{{
93+
* println("abc")
94+
* }}}
95+
*
96+
* because strings exist as ''literals''. Analogously we would like function
97+
* literals, which let us write a function without giving it a name.
98+
*
99+
* These are called ''anonymous functions''.
100+
*
101+
* == Anonymous Function Syntax ==
102+
*
103+
* Example of a function that raises its argument to a cube:
104+
*
105+
* {{{
106+
* (x: Int) => x * x * x
107+
* }}}
108+
*
109+
* Here, `(x: Int)` is the ''parameter'' of the function, and
110+
* `x * x * x` is it's ''body''.
111+
*
112+
* The type of the parameter can be omitted if it can be inferred by the
113+
* compiler from the context.
114+
*
115+
* If there are several parameters, they are separated by commas:
116+
*
117+
* {{{
118+
* (x: Int, y: Int) => x + y
119+
* }}}
120+
*
121+
* == Anonymous Functions are Syntactic Sugar ==
122+
*
123+
* An anonymous function `(x1: T1, …, xn: Tn) => e`
124+
* can always be expressed using `def` as follows:
125+
*
126+
* {{{
127+
* { def f(x1: T1, …, xn: Tn) = e ; f }
128+
* }}}
129+
*
130+
* where `f` is an arbitrary, fresh name (that's not yet used in the program).
131+
*
132+
* One can therefore say that anonymous functions are ''syntactic sugar''.
133+
*
134+
* == Summation with Anonymous Functions ==
135+
*
136+
* Using anonymous functions, we can write sums in a shorter way:
137+
*
138+
* {{{
139+
* def sumInts(a: Int, b: Int) = sum(x => x, a, b)
140+
* def sumCubes(a: Int, b: Int) = sum(x => x * x * x, a, b)
141+
* }}}
142+
*
143+
* = Exercise =
144+
*
145+
* The `sum` function uses linear recursion. Complete the following tail-recursive
146+
* version:
147+
*/
148+
def tailRecSum(res0: Int, res1: Int): Unit = {
149+
def sum(f: Int => Int, a: Int, b: Int): Int = {
150+
def loop(x: Int, acc: Int): Int = {
151+
if (x > b) acc
152+
else loop(x + res0, acc + f(x))
153+
}
154+
loop(a, res1)
155+
}
156+
sum(x => x, 1, 10) shouldBe 55
157+
}
6158
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package scalatutorial.sections
2+
3+
import org.scalacheck.Shapeless._
4+
import org.scalaexercises.Test
5+
import org.scalatest.Spec
6+
import org.scalatest.prop.Checkers
7+
import shapeless.HNil
8+
9+
class HigherOrderFunctionsSpec extends Spec with Checkers {
10+
11+
def `check tail rec sum`: Unit = {
12+
check(Test.testSuccess(HigherOrderFunctions.tailRecSum _, 1 :: 0 :: HNil))
13+
}
14+
15+
16+
17+
}

0 commit comments

Comments
 (0)