Skip to content

Commit 554f908

Browse files
committed
Add FunctionalLoops
1 parent 81b6abe commit 554f908

File tree

4 files changed

+286
-100
lines changed

4 files changed

+286
-100
lines changed

src/main/scala/scalatutorial/sections/DefinitionsAndEvaluation.scala

Lines changed: 143 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ object DefinitionsAndEvaluation extends ScalaTutorialSection {
4646
* = Methods =
4747
*
4848
* Definitions can have parameters. For instance:
49-
*/
50-
def square(x: Double) = x * x
51-
52-
/**
49+
*
50+
* {{{
51+
* def square(x: Double) = x * x
52+
* }}}
53+
*
5354
* And then you can ''call'' a method as follows:
5455
*/
5556
def usingSquare(res0: Double): Unit = {
@@ -69,103 +70,145 @@ object DefinitionsAndEvaluation extends ScalaTutorialSection {
6970
* = Multiple Parameters =
7071
*
7172
* Separate several parameters with commas:
73+
*
74+
* {{{
75+
* def sumOfSquares(x: Double, y: Double) = square(x) + square(y)
76+
* }}}
77+
*
78+
* = Parameters and Return Types =
79+
*
80+
* Function parameters come with their type, which is given after a colon
81+
*
82+
* {{{
83+
* def power(x: Double, y: Int): Double = ...
84+
* }}}
85+
*
86+
* If a return type is given, it follows the parameter list.
87+
*
88+
* = Val vs Def =
89+
*
90+
* The right hand side of a `def` definition is evaluated on each use.
91+
*
92+
* The right hand side of a `val` definition is evaluated at the point of the definition
93+
* itself. Afterwards, the name refers to the value.
94+
*
95+
* {{{
96+
* val x = 2
97+
* val y = square(x)
98+
* }}}
99+
*
100+
* For instance, `y` above refers to `4`, not `square(2)`.
101+
*
102+
* = Evaluation of Function Applications =
103+
*
104+
* Applications of parametrized functions are evaluated in a similar way as
105+
* operators:
106+
*
107+
* 1. Evaluate all function arguments, from left to right
108+
* 1. Replace the function application by the function's right-hand side, and, at the same time
109+
* 1. Replace the formal parameters of the function by the actual arguments.
110+
*
111+
* == Example ==
112+
*
113+
* {{{
114+
* sumOfSquares(3, 2+2)
115+
* sumOfSquares(3, 4)
116+
* square(3) + square(4)
117+
* 3 * 3 + square(4)
118+
* 9 + square(4)
119+
* 9 + 4 * 4
120+
* 9 + 16
121+
* 25
122+
* }}}
123+
*
124+
* = The substitution model =
125+
*
126+
* This scheme of expression evaluation is called the ''substitution model''.
127+
*
128+
* The idea underlying this model is that all evaluation does is ''reduce
129+
* an expression to a value''.
130+
*
131+
* It can be applied to all expressions, as long as they have no side effects.
132+
*
133+
* The substitution model is formalized in the λ-calculus, which gives
134+
* a foundation for functional programming.
135+
*
136+
* = Termination =
137+
*
138+
* Does every expression reduce to a value (in a finite number of steps)?
139+
*
140+
* No. Here is a counter-example:
141+
*
142+
* {{{
143+
* def loop: Int = loop
144+
*
145+
* loop
146+
* }}}
147+
*
148+
* = Value Definitions and Termination =
149+
*
150+
* The difference between `val` and `def` becomes apparent when the right
151+
* hand side does not terminate. Given
152+
*
153+
* {{{
154+
* def loop: Int = loop
155+
* }}}
156+
*
157+
* A definition
158+
*
159+
* {{{
160+
* def x = loop
161+
* }}}
162+
*
163+
* is OK, but a definition
164+
*
165+
* {{{
166+
* val x = loop
167+
* }}}
168+
*
169+
* will lead to an infinite loop.
170+
*
171+
* = Changing the evaluation strategy =
172+
*
173+
* The interpreter reduces function arguments to values before rewriting the
174+
* function application.
175+
*
176+
* One could alternatively apply the function to unreduced arguments.
177+
*
178+
* For instance:
179+
*
180+
* {{{
181+
* sumOfSquares(3, 2+2)
182+
* square(3) + square(2+2)
183+
* 3 * 3 + square(2+2)
184+
* 9 + square(2+2)
185+
* 9 + (2+2) * (2+2)
186+
* 9 + 4 * (2+2)
187+
* 9 + 4 * 4
188+
* 25
189+
* }}}
190+
*
191+
* = Call-by-name and call-by-value =
192+
*
193+
* The first evaluation strategy is known as ''call-by-value'',
194+
* the second is is known as ''call-by-name''.
195+
*
196+
* Both strategies reduce to the same final values
197+
* as long as
198+
*
199+
* - the reduced expression consists of pure functions, and
200+
* - both evaluations terminate.
201+
*
202+
* Call-by-value has the advantage that it evaluates every function argument
203+
* only once.
204+
*
205+
* Call-by-name has the advantage that a function argument is not evaluated if the
206+
* corresponding parameter is unused in the evaluation of the function body.
207+
*
208+
* Scala normally uses call-by-value.
72209
*/
73-
def sumOfSquares(x: Double, y: Double) = square(x) + square(y)
74-
75-
/**
76-
* = Parameters and Return Types =
77-
*
78-
* Function parameters come with their type, which is given after a colon
79-
*
80-
* {{{
81-
* def power(x: Double, y: Int): Double = ...
82-
* }}}
83-
*
84-
* If a return type is given, it follows the parameter list.
85-
*
86-
* = Evaluation of Function Applications =
87-
*
88-
* Applications of parametrized functions are evaluated in a similar way as
89-
* operators:
90-
*
91-
* 1. Evaluate all function arguments, from left to right
92-
* 1. Replace the function application by the function's right-hand side, and, at the same time
93-
* 1. Replace the formal parameters of the function by the actual arguments.
94-
*
95-
* == Example ==
96-
*
97-
* {{{
98-
* sumOfSquares(3, 2+2)
99-
* sumOfSquares(3, 4)
100-
* square(3) + square(4)
101-
* 3 * 3 + square(4)
102-
* 9 + square(4)
103-
* 9 + 4 * 4
104-
* 9 + 16
105-
* 25
106-
* }}}
107-
*
108-
* = The substitution model =
109-
*
110-
* This scheme of expression evaluation is called the ''substitution model''.
111-
*
112-
* The idea underlying this model is that all evaluation does is ''reduce
113-
* an expression to a value''.
114-
*
115-
* It can be applied to all expressions, as long as they have no side effects.
116-
*
117-
* The substitution model is formalized in the λ-calculus, which gives
118-
* a foundation for functional programming.
119-
*
120-
* = Termination =
121-
*
122-
* Does every expression reduce to a value (in a finite number of steps)?
123-
*
124-
* No. Here is a counter-example:
125-
*
126-
* {{{
127-
* def loop: Int = loop
128-
*
129-
* loop
130-
* }}}
131-
*
132-
* = Changing the evaluation strategy =
133-
*
134-
* The interpreter reduces function arguments to values before rewriting the
135-
* function application.
136-
*
137-
* One could alternatively apply the function to unreduced arguments.
138-
*
139-
* For instance:
140-
*
141-
* {{{
142-
* sumOfSquares(3, 2+2)
143-
* square(3) + square(2+2)
144-
* 3 * 3 + square(2+2)
145-
* 9 + square(2+2)
146-
* 9 + (2+2) * (2+2)
147-
* 9 + 4 * (2+2)
148-
* 9 + 4 * 4
149-
* 25
150-
* }}}
151-
*
152-
* = Call-by-name and call-by-value =
153-
*
154-
* The first evaluation strategy is known as ''call-by-value'',
155-
* the second is is known as ''call-by-name''.
156-
*
157-
* Both strategies reduce to the same final values
158-
* as long as
159-
*
160-
* - the reduced expression consists of pure functions, and
161-
* - both evaluations terminate.
162-
*
163-
* Call-by-value has the advantage that it evaluates every function argument
164-
* only once.
165-
*
166-
* Call-by-name has the advantage that a function argument is not evaluated if the
167-
* corresponding parameter is unused in the evaluation of the function body.
168-
*/
169210
def nothing(): Unit = ()
170211

212+
def square(x: Double) = x * x
213+
171214
}

src/main/scala/scalatutorial/sections/FunctionalLoops.scala

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

6+
/**
7+
* = Conditional Expressions =
8+
*
9+
* To express choosing between two alternatives, Scala
10+
* has a conditional expression `if-else`.
11+
*
12+
* It looks like a `if-else` in Java, but is used for expressions, not statements.
13+
*
14+
* Example:
15+
*
16+
* {{{
17+
* def abs(x: Int) = if (x >= 0) x else -x
18+
* }}}
19+
*
20+
* `x >= 0` is a ''predicate'', of type `Boolean`.
21+
*
22+
* = Boolean Expressions =
23+
*
24+
* Boolean expressions `b` can be composed of
25+
*
26+
* {{{
27+
* true false // Constants
28+
* !b // Negation
29+
* b && b // Conjunction
30+
* b || b // Disjunction
31+
* }}}
32+
*
33+
* and of the usual comparison operations:
34+
* {{{
35+
* e <= e, e >= e, e < e, e > e, e == e, e != e
36+
* }}}
37+
*
38+
* = Rewrite rules for Booleans =
39+
*
40+
* Here are reduction rules for Boolean expressions (`e` is an arbitrary expression):
41+
*
42+
* {{{
43+
* !true --> false
44+
* !false --> true
45+
* true && e --> e
46+
* false && e --> false
47+
* true || e --> true
48+
* false || e --> e
49+
* }}}
50+
*
51+
* Note that `&&` and `||` do not always need their right operand to be evaluated.
52+
*
53+
* We say, these expressions use “short-circuit evaluation”.
54+
*
55+
* = Computing the Square Root of a Value =
56+
*
57+
* We will define in this section a method
58+
*
59+
* {{{
60+
* /** Calculates the square root of parameter x */
61+
* def sqrt(x: Double): Double = ...
62+
* }}}
63+
*
64+
* The classical way to achieve this is by successive approximations using
65+
* Newton's method.
66+
*
67+
* = Method =
68+
*
69+
* To compute `sqrt(x)`:
70+
*
71+
* - Start with an initial _estimate_ `y` (let's pick `y = 1`).
72+
* - Repeatedly improve the estimate by taking the mean of `y` and `x/y`.
73+
*
74+
* Example:
75+
*
76+
* {{{
77+
* Estimation Quotient Mean
78+
* 1 2 / 1 = 2 1.5
79+
* 1.5 2 / 1.5 = 1.333 1.4167
80+
* 1.4167 2 / 1.4167 = 1.4118 1.4142
81+
* 1.4142 ... ...
82+
* }}}
83+
*
84+
* = Implementation in Scala =
85+
*
86+
* First, we define a method which computes one iteration step
87+
*
88+
* {{{
89+
* def sqrtIter(guess: Double, x: Double): Double =
90+
* if (isGoodEnough(guess, x)) guess
91+
* else sqrtIter(improve(guess, x), x)
92+
* }}}
93+
*
94+
* Note that `sqrtIter` is ''recursive'', its right-hand side calls itself.
95+
*
96+
* Recursive methods need an explicit return type in Scala.
97+
*
98+
* For non-recursive methods, the return type is optional.
99+
*
100+
* Second, we define a method `improve` to improve an estimate and a test to check for termination:
101+
*
102+
* {{{
103+
* def improve(guess: Double, x: Double) =
104+
* (guess + x / guess) / 2
105+
*
106+
* def isGoodEnough(guess: Double, x: Double) =
107+
* abs(guess * guess - x) < 0.001
108+
* }}}
109+
*
110+
* Third, we define the `sqrt` function:
111+
*
112+
* {{{
113+
* def sqrt(x: Double) = sqrtIter(1.0, x)
114+
* }}}
115+
*
116+
* = Exercise =
117+
*
118+
* Complete the following method definition that computes the factorial of a number:
119+
*/
120+
def factorialExercise(res0: Int, res1: Int, res2: Int): Unit = {
121+
def factorial(n: Int): Int =
122+
if (n == res0) res1
123+
else factorial(n - res2) * n
124+
125+
factorial(3) shouldBe 6
126+
factorial(4) shouldBe 24
127+
}
128+
6129
}

src/main/scala/scalatutorial/sections/LexicalScopes.scala

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

6+
/**
7+
*
8+
*/
9+
def nothing(): Unit = ()
610
}

0 commit comments

Comments
 (0)