(x+a) xs

advertisement
CS5205: Foundation in
Programming Languages
Basics of
Functional Programming.
CS5205
Haskell
1
Topics
•
•
•
•
Higher-Order Functions
Formal Reasoning
Abstraction vs Efficiency
Bridging the Divide
CS5205
Haskell
2
Function Abstraction
• Function abstraction is the ability to convert any
expression into a function that is evaluated at a later
time.
<Expr>
p = \ () -> Expr
time
p ()
time
Normal Execution
CS5205
Delayed Execution
Haskell
3
Higher-Order Functions
• Higher-order programming treats functions as first-class,
allowing them to be passed as parameters, returned as
results or stored into data structures.
• This concept supports generic coding, and allows
programming to be carried out at a more abstract level.
• Genericity can be applied to a function by letting
specific operation/value in the function body to become
parameters.
CS5205
Haskell
4
Genericity
• Replace specific entities (0 and +) by parameters.
sumList ls =
case ls of
[]
-> 0
x:xs -> x+(sumList xs)
foldr f u ls =
case ls of
[]
-> u
x:xs -> f x (foldr f u xs)
CS5205
Haskell
5
Polymorphic, Higher-Order Types
sumList :: [Int] -> Int
sumList :: Num a => [a] -> a
foldr :: (a -> b -> b) -> b
-> [a] -> b
CS5205
Haskell
6
Instantiating Generic Functions
sumL2 :: Num a => [a] -> a
sumL2 ls = foldr (+) 0 ls
sumL2 [1, 2, 3]
)
sumL2 [1.1, 3, 2.3]
)
CS5205
Haskell
7
Instantiating Generic Functions
prodL :: Num a => [a] -> a
prodL ls = foldr (*) 1 ls
prodL [1, 2, 5]
)
prodL [1.1, 3, 2.3]
)
CS5205
Haskell
8
Instantiating Generic Functions
• Can you express map in terms of foldr?
map :: (a -> b) -> [a] -> [b]
map f []
= []
map f (x:xs) = (f x) : (map f xs)
map f xs
CS5205
= foldr … … … xs
Haskell
9
Instantiating Generic Functions
• Filtering a list of elements with a predicate.
filter :: (a ->
filter f []
=
filter f (x:xs)
if (f x) then
else filter f
Bool) -> [a] -> [a]
[]
=
x : (filter f xs)
xs
• Can we express filter in terms of foldr?
filter f xs
CS5205
= foldr … … … xs
Haskell
10
Pipe/Compose
compose :: (b -> c) -> (a -> b)
-> a -> c
compose f g
= \ x -> f (g x)
g | f
= compose f g
• Similar to Unix pipe command:
cmd1
CS5205
|
cmd2
Haskell
11
Iterator Construct
for :: Int -> Int -> (Int -> a -> a)
-> a -> a
for i j f a
=
if i>j then a
else for (i+1) j (f i a)
• In Haskell, type class help give a more generic type:
for :: Num b, Ord b => b -> b ->
(b -> a -> a) -> a -> a
CS5205
Haskell
12
Right Folding
foldr f u [x1,x2,..,xn]
 f x1 (foldr f u [x2 ..xn])
 f x1 (f x2 (fold f u [x3..xn]))
 f x1 (f x2 (… (fold f u [xn]) …))
 f x1 (f x2 (… (f xn u) …)))
associate to
right
CS5205
Haskell
13
Left Folding – Tail Recursion
• Accumulate result in a parameter:
foldl f u ls =
case ls of
[]
-> u
x:xs -> foldl f (f u x) xs
based on accumulation
• What is the type of foldl?
• Can we compute factorial using it?
CS5205
Haskell
14
Left Folding
foldl f u [x1,x2,..,xn]
 foldl f (f u x1) [x2 ..xn]
 foldl f (f (f u x1) x2) [x3..xn]))
 foldl f (f … (f (f u x1) x2)… xn) []
 f (… (f (f u x1) x2) …) xn
left is here!
CS5205
Haskell
15
Instance of Left Folding
• Summing a list by accumulation.
sumT acc ls =
case ls of
[]
-> 0
x:xs -> sumT (x+acc) xs
sumList ls = sumT 0 ls
sumT acc ls = foldl (+) acc ls
CS5205
Haskell
16
Referential Transparency
• An expression is referentially transparent if it can
always be replaced by an equivalent expression with the
same value and effect. Allows reasoning based on
components.
• Useful for:
• simplifying algorithm
• proving correctness
• optimization + parallelization
• Pure functions are referentially transparent, as relied on
in mathematical reasoning.
CS5205
Haskell
17
Equivalence Proof
• Can we Prove : sumList xs = sumT 0 xs.
• Generalise : (sumList xs)+a = sumT a xs.
• By Induction
Case : x=[]
(sumList [])+a = sumT a []
0+a = a
Case : x=x:xs
(sumList x:xs)+a = sumT a (x:xs)
x+(sumList xs)+a = sumT (x+a) xs
(sumList xs)+(x+a) = sumT (x+a) xs
// apply induction hypothesis
CS5205
Haskell
18
List Reversal
• Concatenate first element to last position.
rev []
rev (x:xs)
= []
= rev xs ++ [x]
rev xs = foldr … …
…
What is the time complexity?
CS5205
Haskell
19
Time Complexity
• Assume : C(xs++ys) = length xs
Derive :Steps(rev(xs))
• Case [] :
Steps(rev([])) = 1+Steps([])
= 1+0
• Case x:xs :
Steps(rev(x:xs))
= 1+Steps(rev(xs)++[a])
= 1+C(rev(xs)++_)+Steps(rev(xs))
= 1+length(rev(xs)+Steps(rev(xs))
= 1+length(xs)+Steps(rev(xs))
• Thus : C(rev(xs))
CS5205
= (length xs)^2
Haskell
20
Iterative List Reversal
• Concatenate first element to last position.
revT w []
revT w (x:xs)
= w
= revT (x:w) xs
Same as:
revT w xs = foldl (\ w x -> x:w) w xs
What is the time complexity?
CS5205
Haskell
21
Time Complexity
• Derive Steps(revT w xs)
• Case [] :
Steps(revT w [])
= 1+Steps(w)
= 1+0
• Case x:xs :
Steps(revT w (x:xs))
= 1+Steps(revT (x:w) xs)
= 1+Steps(revT _ xs)
• Thus : C(revT w xs)
CS5205
= (length xs)
Haskell
22
Abstraction vs Efficiency
• Abstraction  helps with programmers’ productivity
• Efficiency  helps machine execution.
• Tension between abstraction and efficiency
• Abstract program
• stress on ‘what’ rather than ‘how’
• typically uses simpler (maybe naïve) algorithm
• Efficient program
• optimised implementation
• use of clever programming techniques
CS5205
Haskell
23
Bridging the Divide
Abstract Code/Specs
transform or
synthesize
verify
Efficient Code
or Implementation
CS5205
Haskell
24
Unfold/Fold Transformation
• DEFINE - new function definition
• UNFOLD – replace a call by its body
• FOLD – replace an expression matching the RHS of a
definition by its corresponding call
• INSTANTIATE – provide special cases of a given
equation.
• ABSTRACT – introduce a tuple of expressions
• LAW – application of valid lemma, e.g. associativity
CS5205
Haskell
25
Fusion Transformation
• Consider:
…sum (double xs)…
sum []
= 0
sum x:xs
= x+(sum xs)
double []
= []
double x:xs = 2*x : (double xs)
• Computation reuses smaller functions to build larger
ones but may result in unnecessary intermediate
structures. They can cause space overheads.
• Solution : Fuse the code together!
CS5205
Haskell
26
Fusion Transformation
• Define:
sumdb xs = sum (double xs)
• Instantiate: xs=[]
sumdb []
= sum (double [])
= sum []
= 0
• Instantiate: xs=x:xs
sumdb x:xs
=
=
=
=
CS5205
sum
sum
2*x
2*x
(double x:xs)
(2*x : double xs)
+ sum(double xs)
+ (sumdb xs)
Haskell
27
Iteration Transformation
• Define:
sumdbT a xs = a+(sumdb xs)
• Instantiate: xs=[]
sumdbT a [] = a+(sumdb [])
= a
• Instantiate: xs=x:xs
sumdbT a (x:xs)
CS5205
=
=
=
=
a+(sumdb x:xs)
a+(2*x + sumdb xs)
(a+2*x) + (sumdb xs)
sumdbT (a+2*x) xs
Haskell
28
Laziness vs Strictness
• Laziness increase expressiveness, allowing infinite data
structures, by not evaluating each subexpression until it
is really needed.
• However, there is a performance penalty (both space and
time), as the suspended computation has to be stored as a
closure and then invoked subsequently.
• If you always need some of the parameters, we might as
well evaluate their corresponding arguments first.
• Question : When is an argument to a function needed
(strict)? Using ? to denote non-termination.
f…?… = ?
CS5205
Haskell
29
Strictness Transformation
• Can analyse that accumulating argument of sumdb is
strict. We can force strictness using the `seq` operator.
sumdbT a []
sumdbT a (x:xs)
= a
= sumdbT (a+2*x) xs
sumdbT a []
sumdbT a (x:xs)
= a
= let p = a+2*x in
p `seq` sumdbT p xs
CS5205
Haskell
30
Tupling Transformation
• Average of a List :
average xs = sum xs / length xs
• Define :
both xs = (sum xs, length xs)
• Instantiate :
both []
= (sum [], length [])
= (0,0)
• Instantiate :
both x:xs
= (sum x:xs, length x:xs)
= (x+sum xs, 1+length xs)
= let (u,v)= (sum xs,length xs) in
(x+u, 1+v)
= let (u,v)= both xs in u/v
CS5205
Haskell
31
Naive Fibonacci
• Natural but inefficient version of fibonacci :
fib 0 = 1
fib 1 = 1
fib n = (fib n-1)+(fib n-2)
• Time complexity is exponential time due to presence of
redundant calls.
CS5205
Haskell
32
A Call Tree of fib
fib 6
fib 4
fib 5
fib 4
fib 3
fib 2
fib 3
fib 2
fib 3
fib 1
fib 2
fib 2
fib 1
Many repeated calls!
CS5205
Haskell
33
A Call Graph of fib
fib 6
fib 5
fib 4
fib 3
fib 2
fib 1
No repeated call through reuse of identical calls
CS5205
Haskell
34
Tupling - Computing Two
Results
Compute two calls from next two calls in hierarchy:
((fib n)
, (fib n-1))
((fib n-1) , (fib n-2))
CS5205
Haskell
35
Tupling Fibonacci
• Define :
fibtup n = (fib n+1, fib n)
• Instantiate :
fibtup 0
= (fib 1, fib 0)
= (1,1)
• Instantiate :
fibtup n+1
= (fib n+2, fib n+1)
= ((fib n+1)+(fib n), fib n+1)
= let (u,v)= (fib n+1, fib n)
in (u+v, u)
= let (u,v)= fibtup n in (u+v, u)
CS5205
Haskell
36
Using the Tupled Function
fib 0 = 1
fib 1 = 1
fib n = fib n-1 + fib n-2
= let
in
= let
in
CS5205
(u,v) = (fib n-1,fib n-2)
u+v
(u,v) = fibtup (n-2)
u+v
Haskell
37
Linear Recursion
fib 6
fib 5
fib 4
fib 3
fibtup 0
= (1,1)
fibtup (n+1) =
let (u,v)= fibtup n
in (u+v,u)
fib 2
fib 1
CS5205
Haskell
38
To Iteration
fib 6
fib 5
fib 4
fib 3
fibT 0 u v
= (u,v)
fibT (n+1) u v
= fibT n (u+v,u)
fib 2
fib 1
CS5205
Haskell
39
Optimization
• Should be done only if critical.
• Should be automated where possible, for e,g. as part of
compilation.
• Manual optimization by human should preferably be
carefully checked or verified
CS5205
Haskell
40
Download