-
Notifications
You must be signed in to change notification settings - Fork 330
/
Copy pathHigherOrderFunctions.scala
173 lines (139 loc) · 5.32 KB
/
HigherOrderFunctions.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/*
* scala-exercises - exercises-stdlib
* Copyright (C) 2015-2016 47 Degrees, LLC. <http://www.47deg.com>
*/
package stdlib
import org.scalatest._
/** @param name higher_order_functions
*
*/
object HigherOrderFunctions
extends FlatSpec
with Matchers
with org.scalaexercises.definitions.Section {
/** Meet lambda. Scala provides a relatively lightweight syntax for defining anonymous functions. Anonymous functions in source code are called function literals and at run time, function literals are instantiated into objects called function values.
*
* Scala supports first-class functions, which means you can express functions in function literal syntax, i.e. ` (x: Int) => x + 1`, and those functions can be represented by objects, which are called function values.
*/
def meetLambdaHigherOrderFunctions(
res0: Int,
res1: Int,
res2: Int,
res3: Int,
res4: Int,
res5: Int) {
def lambda = { x: Int ⇒
x + 1
}
def lambda2 = (x: Int) ⇒ x + 2
val lambda3 = (x: Int) ⇒ x + 3
val lambda4 = new Function1[Int, Int] {
def apply(v1: Int): Int = v1 - 1
}
def lambda5(x: Int) = x + 1
val result = lambda(3)
val `result1andhalf` = lambda.apply(3)
val result2 = lambda2(3)
val result3 = lambda3(3)
val result4 = lambda4(3)
val result5 = lambda5(3)
result should be(res0)
result1andhalf should be(res1)
result2 should be(res2)
result3 should be(res3)
result4 should be(res4)
result5 should be(res5)
}
/** An anonymous function can also take on a different look by taking out the brackets:
*/
def differentLookHigherOrderFunctions(res0: Int) {
def lambda = (x: Int) ⇒ x + 1
val result = lambda(5)
result should be(res0)
}
/** Here the only variable used in the function body, `i * 10`, is `i`, which is defined as a parameter to the function.
*
*{{{
* val multiplier = (i:Int) => i * 10
* }}}
*
* A closure is a function which maintains a reference to one or more variables outside of the function scope (it "closes over" the variables). Scala will detect that you are using variables outside of scope and create an object instance to hold the shared variables.
*/
def meetClosureHigherOrderFunctions(res0: Int, res1: Int) {
var incrementer = 1
def closure = { x: Int ⇒
x + incrementer
}
val result1 = closure(10)
result1 should be(res0)
incrementer = 2
val result2 = closure(10)
result2 should be(res1)
}
/** And then we get to Higher Order Functions:
* Higher Order Functions are functions that take functions as arguments and/or return functions.
*
* We can take that closure and throw it into a Higher Order Function and it will still hold the environment:
*/
def holdEnvironmentHigherOrderFunctions(res0: Int, res1: Int) {
def summation(x: Int, y: Int ⇒ Int) = y(x)
var incrementer = 3
def closure = (x: Int) ⇒ x + incrementer
val result = summation(10, closure)
result should be(res0)
incrementer = 4
val result2 = summation(10, closure)
result2 should be(res1)
}
/** Higher Order Function returning another function:
*/
def returningFunctionHigherOrderFunctions(res0: Boolean, res1: Int, res2: Int) {
def addWithoutSyntaxSugar(x: Int): Function1[Int, Int] = {
new Function1[Int, Int]() {
def apply(y: Int): Int = x + y
}
}
addWithoutSyntaxSugar(1).isInstanceOf[Function1[Int, Int]] should be(res0)
addWithoutSyntaxSugar(2)(3) should be(res1)
def fiveAdder: Function1[Int, Int] = addWithoutSyntaxSugar(5)
fiveAdder(5) should be(res2)
}
/** Function returning another function using an anonymous function:
*/
def returningAnonymousFunctionHigherOrderFunctions(res0: Boolean, res1: Int, res2: Int) {
def addWithSyntaxSugar(x: Int) = (y: Int) ⇒ x + y
addWithSyntaxSugar(1).isInstanceOf[Function1[Int, Int]] should be(res0)
addWithSyntaxSugar(2)(3) should be(res1)
def fiveAdder = addWithSyntaxSugar(5)
fiveAdder(5) should be(res2)
}
/** `isInstanceOf` is the same as `instanceof` in java, but in this case the parameter types can be *blanked out* using existential types with a single underline, since parameter types are unknown at runtime.
*/
def isInstanceOfMethodHigherOrderFunctions(res0: Boolean) {
def addWithSyntaxSugar(x: Int) = (y: Int) ⇒ x + y
addWithSyntaxSugar(1).isInstanceOf[Function1[_, _]] should be(res0)
}
/** Function taking another function as a parameter. Helps in composing functions.
*
* Hint: a map method applies the function to each element of a list.
*/
def functionAsParameterHigherOrderFunctions(
res0: List[String],
res1: List[String],
res2: List[String],
res3: List[Int]) {
def makeUpper(xs: List[String]) = xs map {
_.toUpperCase
}
def makeWhatEverYouLike(xs: List[String], sideEffect: String ⇒ String) =
xs map sideEffect
makeUpper(List("abc", "xyz", "123")) should be(res0)
makeWhatEverYouLike(List("ABC", "XYZ", "123"), { x ⇒
x.toLowerCase
}) should be(res1)
//using it inline
val myName = (name: String) => s"My name is $name"
makeWhatEverYouLike(List("John", "Mark"), myName) should be(res2)
List("Scala", "Erlang", "Clojure") map (_.length) should be(res3)
}
}