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