Haskell - KU Leuven
Download
Report
Transcript Haskell - KU Leuven
Haskell
Deel 2
list comprehensions, types en type klassen,
programma transformatie, I/O
(S)DT 2012-2013
1
LIST COMPREHENSIONS
(S)DT 2012-2013
2
List comprehension
Main> [ 2*a | a <- [2,3,8] ]
[4,6,16]
lezen we als de lijst met waarden 2*a met a
geselecteerd uit de lijst [2,3,8] -- <- generator
l = take 10 [ x*x | x <- (odds [1..])]
where odds [] = []
odds (x:xs) | x `mod` 2 == 1 = (x : odds xs)
| True
= (odds xs)
Main> l
[1,9,25,49,81,121,169,225,289,361]
(S)DT 2012-2013
3
Nog list comprehensions
Main> let l = [2,4,7] in [ a > 4 | a <-l]
[False,False,True]
Main> let l = [8,2,4,7] in [a | a <- l, a > 4]
[8,7]
-- generator en test
Main> take 10 [ x*x | x <- [1..], x `mod` 2 == 1]
[1,9,25,49,81,121,169,225,289,361]
Main> let l= [ [], [1], [4,5], []] in [ a | (a:x) <-l]
[1,4]
Main> let l = [ (1,2), (4,3)] in
[ a+b| (a,b) <- l, a > b]
[7]
(S)DT 2012-2013
4
Variabelen in list comprehensions
Main> let l = [(1,2), (4,3)] in [b | (1,b) <- l]
[2]
Main> let l = [(1,2), (3,3)] in [b | (b,b) <- l]
ERROR - Repeated variable "b" in pattern
-- scope van variabelen
f x l = [ a | (x,a) <- l]
g x l = [ a | (y,a) <- l, x == y]
Main> f 1 [(1,2), (4,3)]
[2,3]
Main> g 1 [(1,2), (4,3)]
[2]
(S)DT 2012-2013
-- nieuwe x
5
Verschillende generatoren
Main> [ (a,b) | a <- [1..2], b <- [1..3]]
[(1,1),(1,2),(1,3),(2,1),(2,2),(2,3)]
Main> [ (a,b) | b <- [1..3], a <- [1,2]]
[(1,1),(2,1),(1,2),(2,2),(1,3),(2,3)]
Main> let l = [(2,7), (4,3)]
-- b is afhankelijk
in [(a,b)| (a,_) <- l, b <- [1..a]]
[(2,1),(2,2),(4,1),(4,2),(4,3),(4,4)]
-- Een Pythagorees drietal (a, b, c) bestaat uit drie positieve gehele
getallen a, b, c met a < b < c waarvoor geldt a2 + b2 = c2
(S)DT 2012-2013
6
Algemene vorm
[ e | q1, q2, … , qn]
qi is (pattern <- listexpr)
of
(boolean expr)
vars in qi moeten links ervan voorkomen
elke var in een pattern is nieuw!
Schrijf map f (filter p xs) nu met list comprehension
En eventueel ook qsort
(S)DT 2012-2013
7
Opnieuw priem
priemgetallen n = [ x | x <- [1..n], priem x]
priem x = factors x == [1,x]
factors n = [ x | x <- [1..n], n `mod` x == 0]
(S)DT 2012-2013
8
Data declaraties; Polymorfe types; Type klassen
TYPES
(S)DT 2012-2013
9
Welke types kennen we?
Int, Integer
[Int]
(Bool, Int)
type variabele t,a,b
lengthlist :: [t] -> Int
map :: (a->b) -> [a] -> [b]
Char
‘a’
String
“hallo world”
in feite synoniem voor lijst van Char
type String = [Char]
type Positie = (Int,Int)
(S)DT 2012-2013
10
Data declaratie voor type IBoom
data
IBoom = Knoop IBoom IBoom | Blad Int
-- wat zijn de mogelijke waarden van het type IBoom?
-- Knoop (Blad 4) (Knoop (Blad 3) (Blad 100))
balanced :: IBoom -> Bool
balanced (Knoop left right) =
let l = diepte left
r = diepte right
in (ok l r ) && (balanced left) && (balanced right)
balanced (Blad _)
= True
ok :: Int -> Int -> Bool
ok l r = (l==r) || (l == (r+1)) || ( (l+1) == r)
(S)DT 2012-2013
11
diepte :: IBoom -> Int
diepte (Knoop l r) = 1 + maxi (diepte l) (diepte r)
diepte (Blad _)
= 1
maxi :: Int -> Int -> Int
maxi x y
| x > y
= x
| otherwise = y
(S)DT 2012-2013
12
Ingebouwde types
data Bool = True | False
-- enumeratie
-- Bool is een nieuw type met 2 mogelijke waarden
-- constructor-functies: True en False
data Int = 0
| 1 | -1 | 2 | -2 | 3 …
(S)DT 2012-2013
13
Meer types
Record type
data Punt = Coord Int Int -- Coord als data constructor
data Punt = Punt Int Int
-- Verschil??
data Punt = MkPunt Int Int
-- Verschil??
Hoe: Int of Bool
Constructors worden gebruikt:
Als een functie om waarden aan te maken (in rechter
deel van equations)
In patronen om waarden af te breken (in linker deel
van equations)
(S)DT 2012-2013
14
Polymorfe types
data Boom a =Knoop (Boom a) (Boom a)|Blad a
Voor elk type a is (Boom a) een data type
Boom is een typeconstructor
Knoop is een dataconstructor – of een
constructor functie
Main> :t Knoop
Knoop :: Boom a -> Boom a -> Boom a
Main> :t Blad
Blad :: a -> Boom a
(S)DT 2012-2013
15
Bladeren
bladeren :: Boom a -> [a]
bladeren (Knoop l r) = bladeren l ++ bladeren r
bladeren (Blad b) = [b]
-- Nog een ingebouwd (polymorf) datatype …
data [a] = [] |
a : [a]
(S)DT 2012-2013
16
Werkvoorbeeld polymorfisme
isort :: [Int] -> [Int]
isort []
= []
isort (x:xs) = insert x (isort xs)
insert :: Int -> [Int] -> [Int]
insert x [] = [x]
insert x (y:ys) | x<y
= x:y:ys
| otherwise = y : (insert x ys)
-- polymorfe versie?? door ???
-- Int vervangen door ???
(S)DT 2012-2013
17
Polymorfe isort
isort :: (t -> t -> Bool) -> [t] -> [t]
isort _
[]
= []
-- was isort (x:xs)
= insert x (isort xs)
isort orde (x:xs) = insert orde x (isort orde xs)
insert :: (t -> t -> Bool) -> t -> [t] -> [t]
insert _
x [] = [x]
insert orde x (y:ys)
| (orde x y) = x:y:ys
| otherwise
= y : (insert orde x ys)
Main> isort (<) [3,1,7]
[1,3,7]
(S)DT 2012-2013
18
Evaluatie: wat gedaan?
extra parameter (de functie) doorgeven – eventueel
diep
de type declaraties aanpassen
code aanpassen
de waarde van die parameter is gekend op moment
van de topoproep
de waarde (naam van de functie) kan afhankelijk zijn
van het type
Misschien is het handiger om uit te drukken:
isort mag enkel op een lijst met elementen waarop (<)
gedefineerd is
(S)DT 2012-2013
19
Type classes: uitbreidbare overloading
class HeeftOrde a where
(<<<)
:: a -> a -> Bool
-- klasse-declaratie
isort :: (HeeftOrde t) => [t] -> [t]
-- klasse-constraint
isort []
= []
isort (x:xs) = insert x (isort xs)
insert :: (HeeftOrde t) => t -> [t] -> [t]
insert x [] = [x]
insert x (y:ys) | x <<< y
= x:y:ys
| otherwise = y: (insert x ys)
(S)DT 2012-2013
21
Klasse-constraint
insert :: (HeeftOrde t) => t -> [t] -> [t]
insert x [] = [x]
insert x (y:ys) | x <<< y
= x:y:ys
| otherwise = y: (insert x ys)
zonder klasse-constraint zegt Haskell:
ERROR "cl.hs":12 - Cannot justify constraints in
explicitly typed binding
*** Expression
: insert
*** Type
: a -> [a] -> [a]
*** Constraints
: HeeftOrde a
Main> isort [2,1]
ERROR - Unresolved overloading
*** Type
: (Num a, HeeftOrde a) => [a]
*** Expression : isort [2,1]
(S)DT 2012-2013
22
Instance van een klasse
data Dagen = Maan | Dins | Woen | Dond |Vrij
instance HeeftOrde Dagen where
Maan <<< _
= True
_
<<< Maan = False
Dins <<< _
= True
_
<<< Dins = False
Woen <<< _
= True
_
<<< Woen = False
Dond <<< _
= True
_
<<< _
= False
Main> isort [Maan, Woen, Dins]
[Maan,Dins,Woen]
(S)DT 2012-2013
23
Dus nu met type classes
geen extra parameters
geen code aanpassen
type declaratie aanpassen – eventueel diep
het type van de polymorfe parameter is gekend op
moment van de topoproep
de compiler kan achter de schermen de
vergelijkingsfunctie meegeven (en doet dat)
de naam van de functie is voor alle instances van de
klasse dezelfde.
Gelijkenis met interface van Java?
(S)DT 2012-2013
24
Dus
Een type klasse beschrijft een “concept”
Een type kan een instance zijn van een type
klasse en is dan een implementatie van het
“concept”.
Generische programma’s mogelijk dankzij
Parametrisch polymorfisme
Type constraints (zie context)
In Java, concept als “interface”
En een Java class implements interface
(S)DT 2012-2013
26
Ingebouwde klassen uit Prelude
class Eq a where
(==), (/=) :: a -> a -> Bool
x /= y
= not (x==y)
-- default definitie
class (Eq a) => Ord a where
-- class extension
compare :: a -> a -> Ordering
(<), (<=), (>=), (>) :: a -> a -> Bool
compare x y | x == y
= EQ
| x <= y
= LT
| otherwise = GT
x <= y
x < y
= compare x y /= GT
= compare x y == LT
(S)DT 2012-2013
27
Ingebouwde instanties
data Bool = False | True
instance Eq Bool where
True == True
= True
False == False = True
_
== _
= False
instance Ord Bool where
False <= True = True
_
<= _
= False
(S)DT 2012-2013
28
Eenvoudig instanties maken
Er is dikwijls een natuurlijke implementatie
voor de functies in een klasse
data Bool = False | True
deriving (Eq, Ord, Show, … )
Is elk type een instance van Eq?
Door er deriving Eq bij te zetten?
Waarom mag dezelfde variabele niet
tweemaal in een pattern match?
(S)DT 2012-2013
29
De klasse Num
class (Eq a, Show a) => Num a where
(+), (-), (*) :: a -> a -> a
negate
:: a -> a
abs, signum
:: a -> a
fromInteger
:: Integer -> a
fromInt
:: Int -> a
x - y
fromInt
negate x
= x + negate y
= fromIntegral
= 0 - x
Main> :t square
square :: Num a => a -> a
(S)DT 2012-2013
30
Int als instance van Num
-- Int is builtin datatype of fixed size integers
primitive primEqInt
instance Eq Int
:: Int -> Int -> Bool
where (==)
= primEqInt
instance Num Int where
(+)
= primPlusInt
(-)
= primMinusInt
negate
= primNegInt
fromInteger n = n
(S)DT 2012-2013
-- ingebouwd prim…
31
Type klassen en type inferentie
De type inferentie leidt het
meest algemene type af .
Daarin komen dikwijls type constraints voor
(+) :: Num a => a -> a -> a
(/) :: Fractional a => a -> a -> a
(S)DT 2012-2013
32
Instances met voorwaarden
-- data [a] = [] | a : [a] deriving (Eq, Ord)
instance Eq a
[]
==
(x:xs) ==
_
==
=> Eq [a]
[]
=
(y:ys) =
_
=
where
-- context!!
True
x == y && xs == ys -- welke?
False
(S)DT 2012-2013
33
de klasse Text
ERROR - Cannot find "show" function for:
*** Expression : isort [Maan,Woen,Dins]
*** Of type
: [Dagen]
class Show a where
show
:: a -> String
data Dagen = Maan |
deriving Show
Dins | Woen | Dond |Vrij
Main> show Maan
“Maan”
(S)DT 2012-2013
34
Enkele vragen
Wat is het meest algemene type van
elem
elem
1
[ 2, 4, 9]
‘a’ “Harry”
False
True
En van
allevk a [] = []
allevk a ((x,y): rest)
= if a == x
then y : allvk a rest
else allevk a rest
En van qsort ??
(S)DT 2012-2013
35
Nog enkele vragen
Herinner je het type Boom a
data Boom a = Knoop (Boom a) (Boom a)| Blad a
Hoe een gelijkheidstest definieren?
Type declaratie van de functie element??
Eindige set (wat is een set??) voorgesteld
als een lijst
Type declaratie
Voegtoe, unie, element?
Gelijkheid van 2 sets?
(S)DT 2012-2013
36
Meer info over classes
In TasteofHaskell.pdf : slides 43-63
(S)DT 2012-2013
37
Equational reasoning
PROGRAMMA
TRANSFORMATIE
(S)DT 2012-2013
38
reverse en reverse
nrev []
= []
nrev (x:xs) = nrev xs ++ [x]
[]
++ l = l
(x:xs) ++ l = (x : (xs ++ l))
reverse l = rev2 l []
rev2 []
a = a
rev2 (x:xs) a = rev2 xs (x:a)
-- (a)
-- (b)
nrev is O( n**2) – reverse is O(n)
(S)DT 2012-2013
39
Automatisch van nrev naar reverse
vertrek van de invariant
voor alle l : rev2 l a = (nrev l) ++ a
gebaseerd op het type van l, herschrijf tot:
voor l== [] of l==(x:xs): rev2 l a = (nrev l) ++ a
of zonder quantoren
1. rev2 [] a = (nrev [] ) ++ a
2. rev2 (x:xs) a = (nrev (x:xs)) ++ a
we bewijzen achtereen volgens (a) en (b)
(S)DT 2012-2013
40
Te bewijzen: rev2 [] a = a
rev2 [] a = (nrev []) ++ a
= [] ++ a
= a
-- (1)
-- gebruik 1ste regel nrev
-- 1ste regel van ++
We hebben (a) bewezen
en gebruikten de invariant en partiële evaluatie
(S)DT 2012-2013
41
Te bewijzen rev2 (x:xs) a = rev2 xs (x:a)
rev2 (x:xs) a
= (nrev (x:xs)) ++ a -- (2)
= ( (nrev xs) ++ [x]) ++ a
= (nrev xs) ++ ([x] ++ a)
= (nrev xs) ++ (x : a)
= rev2 xs (x:a)
-----
gebruik 2e regel nrev
++ is associatief
regels van ++
invariant omgekeerd
We hebben (b) bewezen
en gebruikten de invariant en partiële evaluatie en
de associativiteit van ++ (nog te bewijzen)
(S)DT 2012-2013
42
Te bewijzen (a ++ b) ++ c = a ++ (b ++ c)
Inductie op lengte van a: a kan [] zijn of (x:xs)
Basis van de inductie: a = []
([] ++ b ) ++ c = b ++ c
-- regel 1 van ++
Inductiestap:
stel associativiteit is waar voor a == xs, bewijs
dat associatviteit is waar voor a = (x:xs)
(S)DT 2012-2013
43
Associativiteit: inductiestap x:xs
2e regel : (x:xs) ++ l = (x : (xs ++ l))
(a ++ b ) ++ c
= ( (x:xs) ++ b) ++ c
= ( x : (xs ++ b) ) ++ c
= x : ( (xs ++ b) ++ c)
= x : ( xs ++ (b ++ c))
= (x : xs) ++ (b ++ c)
= a ++ (b ++ c)
-------
(S)DT 2012-2013
a == (x:xs)
regel 2 van ++
regel 2 van ++
asso. Door inductie
omgekeerde regel 2 ++
a == (x:xs)
44
I/O HASKELL
(S)DT 2012-2013
45
I/O in pure Haskell
heeft geen zin want
referential transparency: 2x oproepen, 2x zelfde
resultaat
luiheid: op welk ogenblik wordt een functie
uitgevoerd?
Hoe zinvol aan I/O doen in Haskell?
de toestand van de wereld is een impliciet
argument van functies die I/O doen
sequentialisatie van I/O acties
(S)DT 2012-2013
46
I/O ondersteuning
door type systeem en
nieuwe syntactische constructie – do
getChar :: IO Char
putChar :: Char -> IO ()
Een waarde van type IO t is een “action” die,
als ze wordt uitgevoerd, mogelijk wat I/O doet
voor een resultaat van type t te geven.
Dus wat leiden we af uit het type van putChar ?
(S)DT 2012-2013
47
Achterliggend idee
Een waarde van type IO t is een “action” die,
als ze wordt uitgevoerd, mogelijk wat I/O doet
voor een resultaat van type t te geven.
type IO t = World -> (t,World)
-- een benadering!!!
result :: t
I/O t
World in
World out
(S)DT 2012-2013
48
Voorbeeld I/O en do-notatie
Een functie die een Int als argument heeft, IO doet en
een string teruggeeft: n chars worden gelezen en
omgekeerd als string teruggegeven
foo :: Int -> IO String
foo n = do a <- getnchars n []
return a
getnchars :: Int -> String -> IO String
getnchars 0
l = return l
getnchars n
l = do a <- getChar
s <- getnchars (n-1) (a:l)
return s
-- return :: a -> IO a
-- do maakt 1 actie van een sequentie van acties
(S)DT 2012-2013
49
Een functie f doet IO
f heeft als return type IO iets
als g f oproept, dan doet g IO en heeft type
IO iets’
f roept minstens 1 functie op die IO doet en
kan functies oproepen die dat niet doen
waarde van iets kan opgevangen worden
door de constructie <do dient om IO te sequentialiseren
(S)DT 2012-2013
50
Voorbeeld opnieuw
foo :: Int -> IO String
foo n = do a <- getnchars n
return (rev a)
getnchars :: Int -> IO String
getnchars 0
= return []
getnchars n
=
do a <- getChar
s <- getnchars (n-1)
return (a:s)
(S)DT 2012-2013
51
Waarden van het type (IO t) zijn first class
forever :: IO () -> IO ()
forever a = do { a; forever a }
repeatN :: Int -> IO () -> IO ()
repeatN 0 a = return ()
repeatN n a = do { a; repeatN (n-1) a }
Main > repeatN 10 (putChar ‘x’)
(S)DT 2012-2013
52
Lui en I/O … vergelijk
f = let x = (error)
in 3
g =
do x <- getnchars (error) []
return 3
Main> f
3
Main> g
Program error:
error
(S)DT 2012-2013
53
N-queens interactief
qs :: IO ()
qs = do i <- readi
schrijf (queens i)
schrijf :: Show a => [a] -> IO ()
schrijf []
= return ()
schrijf (x : xs) = do putStr “\n”
c <- putStr (show x)
schrijf xs
-----
Main> :t show
show :: Show a => a -> String
Main> :t ()
trivial type
() :: ()
(S)DT 2012-2013
54
readi :: IO Int
readi = riacc 0
riacc :: Int -> IO Int
riacc acc =
do
x <- getChar
if ( (tr x) < 0)
then return acc
else do z <- (riacc ((10* acc) + (tr x)))
return z
tr ‘0’ = 0
…
tr ‘9’ = 9 tr _ = -1
(S)DT 2012-2013
55
Main> qs
4
[3,1,4,2]
[2,4,1,3]
Main> qs
6
[5,3,1,6,4,2]
[4,1,5,2,6,3]
[3,6,2,5,1,4]
[2,4,6,1,3,5]
(S)DT 2012-2013
56
Doing “timings”
import CPUTime
main :: IO Int
main = do t1 <- getCPUTime
let f1 = fib(21)
print f1
-- forceert de oproep van fib
t2 <- getCPUTime
print "uitvoeringstijd : "
print (t2-t1)
-- in picoseconden (10 -12)
return f1
{- oproep
Main> do { a<- main; print a }
-}
(S)DT 2012-2013
17711
"uitvoeringstijd : "
140625000000
17711
57
De klasse Show
data Itree = Leeg | Node Itree Int Itree
instance Show Itree where
-- showsPrec :: Int -> a -> (String -> String)
showsPrec n Leeg = ([]++)
showsPrec n (Node t1 i t2) =
let n1 = n+1
ni = n+i
in (showsPrec n1 t1).((show ni)++).(showsPrec n1 t2)
-- show x = showsPrec 0 x “”
Main> show (Node (Node Leeg 23 Leeg) 5 Leeg)
“245”
(S)DT 2012-2013
58
In feite: I/O Monad
Haskell maakt gebruik van monads om
berekeningen met waarden te structureren:
bijvoorbeeld te sequentialiseren.
I/O is in feite ook zo’n monad en de dosyntax kan gebruikt worden voor monads.
Nog een monad: berekening die kan falen
data Maybe a = Nothing | Just a
In Prelude class Monad m where …
(S)DT 2012-2013
59