imperative programming: modifying mutable variables, using assignments, and control structures such as if-then-else, loops, continue, return, etc inspired by Von Neumann architecture of computers.
functional programming programming without mutable variables, assignments, loops, other imperative control structures; programming with functions; functions become values that are produced, consumed, and composed; functions can be passed as parameters and returned as values.
logic programming programming in logic; use logical deductions to run a program; programs are a set of logical rules and facts; solutions focus on “what” aspect of the problem and let the system figure out “how” to solve them.
Orthogonal to
87 + 145
val size = 2
5*size
var pi = 3.14
var radius = 21.5
(2 * pi) * radius
def square(x: Double) =
x*x
square(2)
square(5+4)
square(square(4))
def sumOfSquares(x: Double, y: Double) =
square(x) + square(y)
sumOfSquares(3,4)
def power(x: Double, y: Int): Double =
scala.math.pow(x,y)
power(2,3)
e.g.
sumOfSquares(3, 2+2)
sumOfSquares(3, 4)
square(3) + square(4)
3 * 3 + square(4)
9 + square(4)
9 + 4 * 4
9 + 16
25
def loop: Int =
loop
loop
loop
…
…
sumOfSquares(3, 2+2)
square(3) + square(2+2)
3*3 + square(2+2)
9 + square(2+2)
9 + (2+2) * (2+2)
9 + 4*(2+2)
9 + 4*4
9+16
25
call-by-name and call-by-value both result in the same value if
call-by-value evaluates arguments once
call-by-name delays evaluation and can get lucky in not evaluating if argument not needed!
// Consider the function test
def test(x: Int, y: Int) =
x * x
test(2,3)
CBV, CBN: test(2,3) = 2*2 = 4
test(3+4,8)
CBV: test(3+4,8) = test(7,8) = 7*7 = 49
CBN: test(3+4,8) = (3+4)*(3+4) = 7*(3+4) = 7*7 = 49
test(7,2*4)
CBV: test(7,2*4) = test(7,8) = 7*7 = 49
CBN: test(7,2*4) = 7*7 = 49
test(3+4,2*8)
Try this on your own…
Example:
def constOne(x: Int, y: =>Int) =
1
constOne(1+2, loop) = constOne(3,loop) = 1
constOne(loop, 1+2) = ...
def first(x: Int, y: Int) =
x
first(1,loop)
under CBN will terminate but will loop forever under CBV
val and var use CBV semantics
i.e. expression is evaluated on definition
val defines a fixed value
var defines a variable - which can be modified
val x = (1+2)
val y = 3
var z = 2*y
x = 9 // error
z = 12 // ok
def uses CBN semantics
i.e. expression is evaluated when needed
Example
def x = 1 + 2
at this point x
is not evaluated to 3
val y = 2 * x
at this point 2*(1+2)
is evaluated to make y = 6
Normally we use def for function definitions!
Scala provides an if-else
for expressions to be able to choose from two alternatives.
def abs(x: Int) =
if (x >= 0) x else -x
Here (x >= 0)
is a predicate with type Boolean
Boolean expressions can be composed of
true
false
!b
b && b
b || b
and of the usual comparison operations
<=, >=, <, >, ==, !=
!true
—> false
!false
—> true
true && e
—> e
false && e
—> false
true || e
—> true
false || e
—> e
&&
, ||
use short-circuit evaluation (second operand need not be evaluated)
Rewrite rule for if (b) e1 else e2
if (true) then e1 else e2
—> e1
if (false) then e1 else e2
—> e2
def and(x: Boolean, y: Boolean): Boolean =
if (x) y else false
def or(x: Boolean, y: Boolean): Boolean =
if (x) true else y
and(true,false)
or(false,false)
To compute sqrt(x):
Start with initial estimate y
(let y = 1
)
Repeatedly improve the estimate by taking the mean of y
and x/y
Let x = 2
Estimate (y) | Quotient (x/y) | Mean (y+x/y)/2 |
---|---|---|
1 | 2/1 = 2 | 1.5 |
1.5 | 2/1.5= 1.333 | 1.4167 |
1.4167 | 2/1.4167=1.4118 | 1.4142 |
1.4142 |
// Take 1
def sqrt(x: Double) =
sqrtIter(1.0, x)
def sqrtIter(guess: Double, x: Double): Double =
if (isGoodEnough(guess, x)) guess else sqrtIter(improve(guess,x), x)
def improve(guess: Double, x: Double) =
(guess + x/guess)/2
def isGoodEnough(guess: Double, x: Double) =
scala.math.abs(guess*guess-x) < 0.001
sqrt(2)
Note: Recursive functions in Scala need an explicit return type.
Remark: This version is not very accurate for very very small numbers such 0.001, 0.1e-20 and for very large numbers it may not terminate! e.g. for 1.0e20
// Take 2
def sqrt(x: Double) = {
def sqrtIter(guess: Double, x: Double): Double =
if (isGoodEnough(guess, x)) guess else sqrtIter(improve(guess,x), x)
def improve(guess: Double, x: Double) =
(guess + x/guess)/2
def isGoodEnough(guess: Double, x: Double) =
scala.math.abs(guess*guess-x) < 0.001
sqrtIter(1.0, x)
}
sqrt(2)
A block is delimited by braces {…}
It contains a sequence of definitions or expressions
The last element of a block is an expression that defines the block
A block is itself an expression (whose value is the value of the last expression)
val x = 2
def f(y: Int) = y + 1
val result = {
val x = f(3)
x * x
} + x
// Take 3; x visible inside block
def sqrt(x: Double) = {
def sqrtIter(guess: Double): Double =
if (isGoodEnough(guess)) guess else sqrtIter(improve(guess))
def improve(guess: Double) =
(guess + x/guess)/2
def isGoodEnough(guess: Double) =
scala.math.abs(guess*guess-x) < 0.001
sqrtIter(1.0)
}
sqrt(2)
def f(x1,...,xn) =
B
f(e1,...,en)
e1,…,en
resulting in values v1,…,vn
B
in which actual arguments replace formal parameters of f; i.e.f(e1,...,en)
is replaced by
B[x1:=v1, ...xn:=vn]
GCD
// famous Euclid Algorithm
def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a%b)
gcd(14,21)
gcd(14,21)
if (21 == 0) 14 else gcd(21, 14%21)
if (false)14 else gcd(21, 14%21)
gcd(21, 14%21)
gcd(21, 14)
if (14 == 0) 21 else gcd(14, 21%14)
if (false) 21 else gcd(14, 21%14)
gcd(14, 21%14)
gcd(14, 7)
if (7 == 0) 14 else gcd(7, 14%7)
if (false)14 else gcd(7, 14%7)
gcd(7, 14%7)
gcd(7, 0)
if (0 == 0) 7 else gcd(0, 7%0)
7
In Scala, tail-recursive functions can be annotated
@tailrec
def gcd(a: Int, b: Int): Int = …
Factorial
def factorial(n: Int): Int =
if n <= 0 1 else n* factorial(n-1)
def factorial(n: Int): Int =
fact(1,n)
def fact(answer: Int, n: Int): Int =
if (n == 0) answer else fact(n*answer, n - 1)