Functional Programming in Haskell

Download Report

Transcript Functional Programming in Haskell

Functional Programming
in Haskell
Motivation through Concrete Examples
Adapted from Lectures by
Simon Thompson
Functional Programming
Given the functions
above
invertColour
flipH
sideBySide
superimpose
flipV
and the horse picture,
how do you get …
(expression and evaluation)
CS7120 (Prasad)
L5-Haskell
2
Definitions in Haskell
name :: Type
name = expression
blackHorse :: Picture
blackHorse = invertColour horse
rotate :: Picture -> Picture
rotate pic = flipH (flipV pic)
CS7120 (Prasad)
L5-Haskell
3
Higher-level
Evaluation is about expressions and values, not
storage locations.
• No need to allocate/deallocate storage: garbage collection.
• Values don't change over program execution: contrast
x=x+1 etc. of Java, C, …
•… instead we describe relations between values by means of
(fixed) functions.
CS7120 (Prasad)
L5-Haskell
4
Declarative … proofs possible
Programs describe themselves:
square n = n*n
double n = 2*n
'The square of n is n*n, for every integer n.'
Programs are equations.
So we can write proofs using the definitions.
square (double n)
= square (2*n)
= (2*n)*(2*n) = 2*2*n*n
= double (double (square n))
CS7120 (Prasad)
L5-Haskell
5
Evaluation freedom
Evaluation can occur in any order ...
(4-3)+(2-1)
(4-3)+1
(4-3)+(2-1)
1+(2-1)
1+1
1+1
2
2
(4-3)+(2-1)
1+1
2
… and can choose to evaluate only what is needed,
when it is needed: lazy evaluation (more later).
Can also evaluate in parallel … efficiently?
CS7120 (Prasad)
L5-Haskell
6
History
First 'functional' language, LISP, defined c. 1960 …
popular in AI in 70s/80s.
• Now represented best by Scheme.
• Weakly typed; allows side-effects and eval.
Next generation: ML (1980…), Miranda (1985…) and
Haskell (1990…).
• Strongly-typed; ML allows references and thus side-effects.
• Miranda and Haskell: pure and lazy.
• FP (1982): heroic experiment by Backus (FORTRAN, ALGOL).
CS7120 (Prasad)
L5-Haskell
7
Haskell and Hugs
Named after Haskell Brooks Curry: mathematician and
logician; inventor of the -calculus.
Haskell 98 is the recent 'standard' version of Haskell.
Various implementations: Hugs (interpreter for
Windows, Mac, Unix) and GHC, NHC, HBC (compilers).
http://www.haskell.org/
CS7120 (Prasad)
L5-Haskell
8
Basics: guards and base types
How many of three integers are equal … ?
howManyEqual :: Int -> Int -> Int -> Int
howManyEqual n m k
| n==m && m==k
= 3
| n==m || m==k || k==n
= 2
| otherwise
= 1
… and if we reach here
they're all different.
CS7120 (Prasad)
L5-Haskell
If we reach here
they're not all equal …
9
How many pieces with n cuts?
CS7120 (Prasad)
L5-Haskell
11
How many pieces with n cuts?
No cuts: 1 piece.
With the nth cut, you get n more pieces:
cuts :: Int -> Int
cuts n
| n==0
= 1
| n>0
= cuts (n-1) + n
| otherwise
= 0
CS7120 (Prasad)
L5-Haskell
12
The Pictures case study.
Using a powerful library of functions over lists.
• Pattern matching
• Recursion
• Generic functions
• Higher-order functions
•…
CS7120 (Prasad)
L5-Haskell
13
Using Hugs
expr
Evaluate expr
:type expr
Give the type of expr
:l Blah
Load the file Blah.hs
:r
Reload the last file
:?
Help: list commands
:e
Edit the current file
:q
Quit
CS7120 (Prasad)
L5-Haskell
14
Functions over pictures
A function to flip a picture in a vertical mirror:
input
output
flipV
CS7120 (Prasad)
L5-Haskell
15
Functions over pictures
A function to invert the colours in a picture:
invertColour
CS7120 (Prasad)
L5-Haskell
16
Functions over pictures
A function to superimpose two pictures:
superimpose
CS7120 (Prasad)
L5-Haskell
17
Functions over pictures
A function to put one picture above another:
above
CS7120 (Prasad)
L5-Haskell
18
Functions over pictures
A function to put two pictures side by side:
sideBySide
CS7120 (Prasad)
L5-Haskell
19
A naïve implementation
type Picture = [String]
type String = [Char]
A Picture is a list of Strings.
A String is a list of Char (acters).
.......##...
.....##..#..
...##.....#.
..#.......#.
..#...#...#.
..#...###.#.
.#....#..##.
..#...#.....
...#...#....
....#..#....
.....#.#....
......##....
CS7120 (Prasad)
L5-Haskell
20
How are they implemented?
flipH
Reverse the list of strings.
flipV
Reverse each string.
rotate
flipH then flipV (or v.versa).
above
Join the two lists of strings.
sideBySide
Join corresponding lines.
invertColour
Change each Char … and each line.
superimpose
Join each Char … join each line.
CS7120 (Prasad)
L5-Haskell
21
How are they implemented?
flipH
reverse
flipV
map reverse
rotate
flipV . flipH
above
++
sideBySide
zipWith (++)
invertColour
map (map invertChar)
superimpose
zipWith (zipWith combine)
CS7120 (Prasad)
L5-Haskell
22
Lists and types
Haskell is strongly typed: detect all type errors
before evaluation.
For each type t there is a type [t], 'list of t'.
reverse []
= []
reverse (x:xs) = reverse xs ++ [x]
reverse :: [a] -> [a]
a is a type variable: reverse works over any list
type, returning a list of the same type.
CS7120 (Prasad)
L5-Haskell
23
Flipping in a vertical mirror
flipV :: Picture -> Picture
flipV [] = []
flipV (x:xs) = reverse x : flipV xs
Run along the list, applying reverse to each element
Run along the list, applying … to every element.
General pattern of computation.
CS7120 (Prasad)
L5-Haskell
24
Implementing the mapping pattern
map f []
= []
map f (x:xs) = f x : map f xs
map :: (a -> b) -> [a] -> [b]
Examples over pictures:
flipV pic
= map reverse pic
invertColour pic = map invertLine pic
invertLine line = map invertChar line
CS7120 (Prasad)
L5-Haskell
25
Functions as data
Haskell allows you to pass functions as arguments
and return functions as results, put them into lists,
etc. In contrast, in Pascal and C, you can only pass
named functions, not functions you build dynamically.
map isEven = ??
map isEven :: [Int] -> [Bool]
It is a partial application, which gives a function:
give it a [Int] and it will give you back a [Bool]
CS7120 (Prasad)
L5-Haskell
26
Partial application in Pictures
flipV
= map reverse
invertColour = map (map invertChar)
A function
[[Char]]->[[Char]]
CS7120 (Prasad)
A function
[Char]->[Char]
L5-Haskell
27
Another pattern: zipping together
sideBySide [l1,l2,l3] [r1,r2,r3]
= [ l1++r1, l2++r2, l3++r3 ]
zipWith f (x:xs) (y:ys)
= f x y : zipWith f xs ys
zipWith f xs ys = []
zipWith :: (a->b->c) -> [a] -> [b] -> [c]
CS7120 (Prasad)
L5-Haskell
28
In the case study …
sideBySide = zipWith (++)
Superimposing two pictures: need to combine
individual elements:
combine :: Char -> Char -> Char
combine top btm
= if (top=='.' && btm=='.') then '.' else '#'
superimpose = zipWith (zipWith combine)
CS7120 (Prasad)
L5-Haskell
29
Parsing
"((2+3)-4)"
is a sequence of symbols, but underlying it is a
structure ...
-
+
2
CS7120 (Prasad)
4
3
L5-Haskell
30
Arithmetical expressions
An expression is either
• a literal, such as 234 or a composite expression:
• the sum of two expressions (e1+e2)
• the difference of two expressions (e1-e2)
• the product of two expressions (e1*e2)
CS7120 (Prasad)
L5-Haskell
31
How to represent these structures?
data Expr = Lit Int |
Sum Expr Expr |
Minus Expr Expr |
Times Expr Expr
Elements of this algebraic data type include
Lit 34
34
Sum (Lit 45) (Lit 3)
(45+3)
Minus (Sum (Lit 2) (Lit 3)) (Lit 4)
((2+3)-4)
CS7120 (Prasad)
L5-Haskell
32
Counting operators
data Expr = Lit Int | Sum Expr Expr | Minus ...
How many operators in an expression?
Definition using pattern matching
cOps (Lit n)
= 0
cOps (Sum e1 e2)
= cOps e1 + cOps e2 + 1
cOps (Minus e1 e2) = cOps e1 + cOps e2 + 1
cOps (Times e1 e2) = cOps e1 + cOps e2 + 1
CS7120 (Prasad)
L5-Haskell
33
Evaluating expressions
data Expr = Lit Int | Sum Expr Expr | Minus ...
Literals are themselves …
eval (Lit n)
= n
… in other cases, evaluate the two arguments and
then combine the results …
eval (Sum e1 e2)
= eval e1 + eval e2
eval (Minus e1 e2) = eval e1 - eval e2
eval (Times e1 e2) = eval e1 * eval e2
CS7120 (Prasad)
L5-Haskell
34
List comprehensions
Example list x = [4,3,2,5]
[ n+2 | n<-x, isEven n]
run through the n in x …
4
3
2
5
select those which are even …
4
2
and add 2 to each of them
6
4
giving the result
[6,4]
CS7120 (Prasad)
L5-Haskell
35
List comprehensions
Example lists x = [4,3,2]
y = [12,17]
[ n+m | n<-x, m<-y]
run through the n in x …
4
3
2
and for each, run through the m in y …
12
17
12
17
12
17
20
14
19
add corresponding pairs
16
21
15
giving the result
[16,21,15,20,14,19]
CS7120 (Prasad)
L5-Haskell
36
Quicksort
qsort []
= []
qsort (x:xs) =
qsort elts_lt_x
++
[x]
++ qsort elts_greq_x
where
elts_lt_x
= [y | y <- xs, y < x]
elts_greq_x = [y | y <- xs, y >= x]
CS7120 (Prasad)
L5-Haskell
37
MergeSort
mergeSort [] = []
mergeSort [x] = [x]
mergeSort xs | size > 1 =
merge (mergeSort front) (mergeSort back)
where
size = length xs `div` 2
front = take size xs
back = drop size xs
CS7120 (Prasad)
L5-Haskell
38
Merging
x
x <= y?
y
merge [1, 3] [2, 4]
1 : merge [3] [2, 4]
1 : 2 : merge [3] [4]
1 : 2 : 3 : merge [] [4]
CS7120 (Prasad)
1 : 2 L5-Haskell
: 3 : [4]
[1,2,3,4]
39
Defining Merge
One list gets
smaller.
merge (x : xs) (y : ys)
| x <= y
= x : merge xs (y : ys)
| x > y
= y : merge (x : xs) ys
merge [] ys
= ys
merge xs []
= xs
CS7120 (Prasad)
L5-Haskell
Two possible
base cases.
40
Lazy evaluation
Only evaluate what is needed … infinite lists
nums :: Int -> [Int]
nums n = n : nums (n+1)
sft (x:y:zs) = x+y
sft (nums 3)
= sft (3: nums 4)
= sft (3: 4: nums 5)
= 7
CS7120 (Prasad)
L5-Haskell
41
The list of prime numbers
primes = sieve (nums 2)
sieve (x:xs)
= x : sieve [ z | z<-xs, z `mod` x /= 0]
To sieve (x:xs) return x, together with the result
of sieveing xs with all multiples of x removed.
take 100 primes
CS7120 (Prasad)
L5-Haskell
42