16.11.2010

Werbung
Was bisher geschah
I
Algebraische Datentypen
I
Pattern Matching
I
Rekursionsschemata
map, fold über Zahlen, Listen, Bäumen, ...
I
strukturelle Induktion
I
Polymorphie
61
Eingeschänkte Polymorphie
reverse [1,2,3,4] = [4,3,2,1]
reverse "foobar" = "raboof"
reverse :: [a] -> [a]
reverse ist polymorph
sort [5,1,4,3] = [1,3,4,5]
sort "foobar" = "abfoor"
sort :: [a] -> [a] -- ??
sort [sin,cos,log] = ??
sort ist eingeschränkt polymorph
62
Motivation
sort enthält:
let ( low, high ) = partition ( < ) xs in ...
Für alle Typen a, die für die es eine Vergleichs-Funktion
compare gibt, hat sort den Typ [a] -> [a].
sort :: Ord a => [a] -> [a]
Ord ist eine Typklasse, definiert durch
class Ord a where
compare :: a -> a -> Ordering
data Ordering = LT | EQ | GT
63
Instanzen
Typen können Instanzen von Typklassen sein.
(OO: Klassen implementieren Interfaces)
Für vordefinierte Typen sind auch die meisten sinnvollen
Instanzen vordefiniert
instance Ord Int ; instance Ord Char ; ...
weitere Instanzen kann man selbst deklarieren:
data Student = Student { vorname :: String
, nachname :: String
, matrikel :: Int
}
instance Ord Student where
compare s t =
compare (matrikel s) (matrikel t)
64
Typen und Typklassen
In Haskell sind diese drei Dinge unabhängig
1. Deklaration einer Typklasse (= Deklaration von abstrakten
Methoden) class C where { m :: ... }
2. Deklaration eines Typs (= Sammlung von Konstruktoren
und konkreten Methoden) data T = ...
3. Instanz-Deklaration (= Implementierung der abstrakten
Methoden) instance C T where { m = ... }
In Java sind 2 und 3 nur gemeinsam möglich
class T implements C { ... }
65
Wörterbücher
Haskell-Typklassen/Constraints. . .
class C a where
m :: a -> a -> Int
f :: C a => a -> Int
f x = m x x + 5
. . . sind Abkürzungen für Wörterbücher:
data C a = C { m :: a -> a -> Foo }
f :: C a -> a -> Int
f dict x = ( m dict ) x x + 5
Für jedes Constraint setzt der Compiler ein Wörterbuch ein.
66
instance C Bar where m x y = ...
dict_C_Bar :: C Bar
dict_C_Bar = C { m = \ x y -> ... }
An der aufrufenden Stelle ist das Wörterbuch statisch bekannt
(hängt nur vom Typ ab).
b :: Bar
... f b ...
wird übersetzt zu
... f dict_C_bar b ...
67
Vergleich Polymorphie
I
Haskell-Typklassen:
statische Polymorphie,
Wörterbuch ist zusätzliches Argument der Funktion
I
OO-Programmierung:
dynamische Polymorphie,
Wörterbuch ist im Argument-Objekt enthalten.
(OO-Wörterbuch = Methodentabelle der Klasse)
68
Typen mit Gleichheit
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
Beispiele:
I
(’a’ == ’b’) = False
I
(True /= False) = True
I
("ab" /= "ac") = True
I
([1,2] == [1,2,3]) = False
I
(\ x -> 2 * x) == (\ x -> x + x) = ?
69
Typen mit totaler Ordnung
Instanzen der Typklasse Eq mit
data Ordering = LT | EQ | GT
class Eq a => Ord a where
compare :: a -> a -> Ordering
(<)
:: a -> a -> Bool
(<=)
:: a -> a -> Bool
(>)
:: a -> a -> Bool
(>=)
:: a -> a -> Bool
min
:: a -> a -> a
max
:: a -> a -> a
Beispiele:
I
(’a’ < ’b’) = True
I
(False < True) = True
I
("ab" < "ac") = True (lexikographisch)
I
([1,2] > [1,2,3]) = False
70
Klassen-Hierarchien
Typklassen können in Beziehung stehen.
Ord ist „abgeleitet“ von Eq:
class Eq a where
(==) :: a -> a -> Bool
class Eq a => Ord a where
(<) :: a -> a -> Bool
Ord ist Typklasse mit Typconstraint (Eq)
also muß man erst die Eq-Instanz deklarieren, dann die
Ord-Instanz.
Jedes Ord-Wörterbuch hat ein Eq-Wörterbuch.
71
Instanzen
data Bool = False | True
instance Ord
False ==
True ==
_
==
Bool where
False = True
True
= True
_
= False
instance Eq Bool where
False < True = True
_
< _
= False
x
<= y
= ( x < y ) || ( x == y )
x
> y
= y < x
x
>= y
= y <= x
72
Numerische Typen
class (Eq a, Show a) => Num a where
(+)
:: a -> a -> a
(-)
:: a -> a -> a
(*)
:: a -> a -> a
negate :: a -> a
abs
:: a -> a
signum :: a -> a
Beispiele:
I
signum (-3) = -1
I
signum (-3.3) = -1.0
Instanzen Int, Integer, Float
73
Numerische Typen mit Division
class Num a => Integral a where
div
:: a -> a -> a
mod
:: a -> a -> a
Instanzen Int, Integer
class Num a => Fractional a where
(/)
:: a -> a -> a
recip
:: a -> a -> a
Instanz Float
Beispiele:
I
3 / 2 = 0.6
I
3 ‘div‘ 2 = 1
74
Typen mit Operation zum (zeilenweisen) Anzeigen
class Show a where
show :: a -> String
Beispiele:
I
show 123 = "123"
I
show True = "True"
I
show [1,2] = "[1,2]"
I
show (1,’a’,True) = "show (1,’a’,True)"
Instanzen Bool, Char, Int, Integer, Float,
Listen und Tupel von Instanzen
75
Typklasse Show
Die Interpreter Ghci/Hugs geben bei Eingabe exp
(normalerweise) show exp aus.
Man sollte (u. a. deswegen) für jeden selbst deklarierten
Datentyp eine Show-Instanz schreiben.
. . . oder schreiben lassen: deriving Show
76
Typen mit Operation zum Lesen
class Read a where
read :: String -> a
Beispiele:
I
( read "3" :: Int ) = 3
I
( read "3" :: Float ) = 3.0
I
( read "False" :: Bool ) = False
I
( read "’a’" :: Char ) = ’a’
I
( read "[1,2,3]" :: [Int] ) = [1,2,3]
Instanzen Bool, Char, Int, Integer, Float,
Listen und Tupel von Instanzen
77
Generische Instanzen
class Eq a where
(==) :: a -> a -> Bool
Vergleichen von Listen (elementweise)
wenn a in Eq, dann [a] in Eq:
instance Eq a => Eq [a] where
[]
== []
= True
(x : xs) == (y : ys)
= (x == y) && ( xs == ys )
_
== _
= False
78
Abgeleitete Instanzen
Deklaration eigener Typen als Instanzen von Standardklassen
durch automatische Erzeugung der benötigten Methoden:
Beispiele:
data Bool = False | True
deriving (Eq, Ord, Show, Read)
data Shape = Circle Float | Rect Float Float
deriving (Eq, Ord, Show, Read)
Beispiel: (Circle 3 < Rect 1 2) == True
data (Eq a) => Maybe a = Nothing | Just a
deriving (Eq, Ord, Show, Read)
Beispiel: (Just ’a’ == Just ’b’) == False
79
Interaktive Programme
I
Berechnungen in Haskell sind nebenwirkungsfrei.
(Diese Eigenschaft soweit wie möglich beibehalten)
I
Ein- und Ausgabe sind Nebenwirkungen.
(Nebenwirkungen zulassen, wo sie unvermeidbar sind)
I
Bei Ein- und Ausgabeaktionen ist die
Ausführungsreihenfolge wichtig.
Idee:
I
Isolation der Programmteile mit Nebenwirkungen
(Operation return )
I
sequentielle Verknüpfung von Ausdrücken mit
Nebenwirkungen
(Operation bind >>= )
80
Hakell-Compiler ghc
Programm hello.hs:
module Main where
main = putStrLn "Hello"
mit ghc kompilieren
ghc -o hello Main.hs
und ausführen:
./hello
81
Noch ein Beispiel
module Main where
sumList :: [Int] -> Int
sumList [] = 0
sumList (x:xs) = x + sumList xs
main = print ( sumList [1 .. 10] )
kompilieren mit ghc und aufrufen:
ghc -o sumlist Main.hs
./sumlist
oder interpretieren (wie bisher) mit ghci:
:l Main
main
82
Abstraktes Modell für IO
I
IO a = Aktion mit Resultat :: a und Nebenwirkung
I
ein ausführbares Haskell-Programm enthält
module Main where
main :: IO ()
main = ...
diese Aktion wird (berechnet und) ausgeführt.
oft zusätzlich notwendig:
import System.IO
main = do
hSetBuffering stdout NoBuffering
...
83
Konkretes Modell für IO: Zustand
Typ für Aktionen, Änderung des Weltzustandes
data World = ...
data IO = World -> World
data IO a = IO { World -> (a, World) }
das Welt-Objekt bezeichnet die Welt außerhalb des
Programmes
Problem:
f :: World -> ( World, World )
f w = ( deleteFile "foo" w, putStr "bar" w )
mögliche Lösungen:
I
Haskell: Typ World ist privat, öffentlich ist nur IO
I
Clean: Typ World ist öffentlich, aber unique
84
Ein- und Ausgabe
putStrLn "Hello"
hat den Typ IO ()
Zustandsänderung ohne Resultat
putStrLn
hat den Typ String -> IO ()
Funktion, Funktionswert ist eine Aktion
(die zu einer Zustandsänderung führen kann)
putStr, putStrLn nur für Strings
putStr :: String -> IO ()
print für beliebige Typen (mit show)
print x = putStrLn (show x)
getLine
hat den Typ IO String
Zustandsänderung mit Resultat :: String
85
Ein- und Ausgabe
Für IO-Aktion ist die Reihenfolge wichtig.
muss in Haskell angegeben werden
do-Notation
do
A
B
C
mit Anweisungen A,B,C
86
Ein- und Ausgabe-Anweisungen
Eingabe:
x <- A
mit Aktion A
(erzeugt Variablenbindung)
Beispiel (Kombination):
echoline :: IO ()
echoline = do
input <- getLine
putStr input
87
Häufige IO-Aktionen
abfrage :: String -> IO String
abfrage s = do
putStrLn s
getLine
mit
getLine :: IO String
getLine = do x <- getChar
if x == ’\n’ then return []
else do xs <- getLine
return xs
Verwendung:
main :: IO ()
main = do
name <- abfrage "Name:"
putStrLn ("Hello " ++ name)
88
Ein- und Ausgaben von Werten
getNumber :: IO Int
getNumber = do
putStr "Zahl eingeben: "
readLn
z.B. in
main :: IO ()
main = do
n1 <- getNumber
n2 <- getNumber
putStr "Summe: "
print (n1 + n2)
analog:
getNumber :: IO Int
89
Rückgabewerte
get2 :: IO (Int, Int)
get2 = do
n1 <- getNumber
n2 <- getNumber
return (n1, n2)
return :: a -> IO a
main = do
x <- get2
print x
90
Rekursion
getNums :: IO [Int]
getNums = do n <- readLn
if n == 0 then return []
else do
ns <- getNums
return ([n] -- ns)
main :: IO ()
main = do
putStrLn "Zahlen eingeben (0 für Ende)"
nums <- getNums
putStr "Summe = " ++ show (sum nums)
91
IO
data IO -- abstract
readFile :: FilePath -> IO String
putStrLn :: String -> IO ()
Alle „Funktionen“, deren Resultat von der Außenwelt
(Systemzustand) abhängt, haben Resultattyp IO ...
Am Typ einer Funktion erkennt man ihre möglichen
(schädlichen) Wirkungen bzw. deren garantierte Abwesenheit.
Wegen der Monad-Instanz: benutze do-Notation
do cs <- readFile "foo.bar"
putStrLn cs
92
Monaden
class Monad m where
return :: a -> m a
( >>= ) :: m a -> (a -> m b) -> m b
mit den Operationen:
return return :: a -> m a Eingang
bind
(>>=) :: m a -> (a -> m b) -> m b
sequentielle Verknüpfung
Monaden-Gesetze:
I
(return x) >>= f == f x
I
m >>= return == m
I
( m >>= f) >>= g == m >>= (\ x -> f x >>= g)
93
IO-Monade
class Monad m where
return :: a -> m a
( >>= ) :: m a -> (a -> m b) -> m b
für m = IO:
return :: a -> IO a
( >>= ) :: IO a -> (a -> IO b) -> IO b
instance Monad IO where
return v = \ w -> (v, w)
f >>= g = \ w -> case f w of
(v, w’) -> g v w’
94
Do-Notation für Monaden
Beispiel: original
evaluate e l >>= \ a ->
evaluate e r >>= \ b ->
return ( a + b )
do-Notation (implizit geklammert)
do a <- evaluate e l
b <- evaluate e r
return ( a + b )
Übersetzung:
I
do f übersetzt zu f
I
do f; ... übersetzt zu f >>= \ _ -> do
I
do x <- f; ... übersetzt zu f >>= \ x -> do
95
Beipiel Datei-Ein- und Ausgabe
main :: IO ()
main = do
putStr " Filename:"
fname <- getLine
cont <- readFile fname
putStr cont
96
Maybe-Monade
data Maybe a = Just a | Nothing
statt:
case ( evaluate e l ) of
Nothing -> Nothing
Just a -> case ( evaluate e r ) of
Nothing -> Nothing
Just b -> Just ( a + b )
kürzer und übersichtlicher:
evaluate e l >>= \ a ->
evaluate e r >>= \ b ->
return ( a + b )
mit Hilfe der Maybe-Monade
instance Monad Maybe where
return = \ x -> Just x
m >>= f = case m of
Nothing -> Nothing
Just x -> f x
97
Listen-Monade
instance Monad [] where
return = \ x -> [x]
m >>= f = concat ( map f m )
Beispiel: Kreuzprodukt von xs :: [a] mit ys :: [b]
cross xs ys =
concat ( map ( \ x ->
concat ( map ( \ y ->
[ (x,y) ] ) ys ) ) xs )
besser:
cross xs ys =
xs >>= \ x ->
ys >>= \ y ->
return (x,y)
noch einfacher lesbar:
cross xs ys = do
x <- xs
y <- ys
return (x,y)
98
Herunterladen