Deklarative Programmierung

Werbung
Was bisher geschah
I
Algebraische Datentypen
I
Pattern Matching
I
Funktionen höherer Ordnung
I
Rekursionsschemata map, fold
über Zahlen, Listen, Bäumen, ...
I
strukturelle Induktion
I
Polymorphie
I
Typklassen
I
Lazy evaluation
99
Funktionen mit Zusatzinformation
Funktion
f :: Int -> Int
erweitern um zusätzliche Information (String)
f’ :: Int -> (Int, String)
Verkettung zweier Funktionen
f,g :: Int -> Int
(f . g) x = f (g x)
beide mit zusätzlicher Information
f’, g’ :: Int -> (Int, String)
Nacheinanderausführung
let ( y, s ) = g’ x
( z, s’) = f’ y
in (z, s ++ s’)
umständlich, besser wäre
Kombination (Nacheinanderausführung) mit Verknüpfung der
Informationen
100
Transformation: Wert → Wert mit Zusatzinformation
infos x = (x, "")
Transformation: Funktion → Funktion mit Zusatzinformation
lift f x = (f x, "")
alternative Defintion für lift
lift f = infos . f
Es gilt also
f’ = lift f , g’ = lift g
lift lässt sich also durch infos ausdrücken
101
Nacheinanderausführung
Definition:
nach :: (a -> (b, String) ) -> (a, String)
-> (b, String)
nach f (y,s) = let (z,s’) = f y
in (z, s ++ "." ++ s’)
Verwendung:
g’ ‘nach‘ ( f’ ‘nach‘ (infos 4)))
oder
(lift g) ‘nach‘ ( ( lift f) ‘nach‘ (infos 4))
mon-info.hs
102
Datentyp für Wert mit Zusatzinformation
f, g :: a -> b
data MZ a = MZ (a, String)
f’, g’ :: a -> MZ b
Nacheinanderausführung
let MZ ( y, s ) = g’ x
MZ ( z, s’) = f’ y
in MZ (z, s ++ "." ++ s’)
Kombination (Nacheinanderausführung) mit Verknüpfung der
Informationen Definition:
nach :: (a -> MZ b) -> MZ a -> MZ b
nach f (MZ (y,s)) = let (MZ (z,s’)) = f y
in MZ (z, s ++ "." ++ s’)
mon-mz.hs
103
Maybe
data Maybe a = Just a | Nothing
f’ : a -> Maybe b
g’ : b -> Maybe c
Nacheinanderausführung
case ( g’ x ) of
Nothing -> Nothing
Just y -> f’ y
umständlich, besser wäre
Kombination (Berechnung) mit automatischer Übergabe von
Nothing
104
Fehlermitteilung
data MayFail a = Error EC | Result a
data EC = Fgross | Gklein
f’ : a -> MayFail b
g’ : b -> MayFail c
Nacheinanderausführung
case ( g’ x ) of
Error c -> Error c
Result y -> f’ y
umständlich, besser wäre
Kombination (Berechnung) mit automatischer Übergabe der
Fehlermitteilung
105
Gemeinsames Muster
f :: a -> b
g :: b -> c
Übergang zu erweitertem Ergebnistyp
f’ :: a -> .. b
g’ :: b -> .. c
Nacheinanderausführung solcher Funktionen mit
automatischer Übergabe
nach :: (a -> .. b) -> .. a -> .. b
106
in den Beispielen
I
Zusatzinformation
data MZ a = MZ ( a, String
nach :: (Int -> MZ Int) ->
nach f (MZ (y,s)) = let MZ
in MZ (z, s
I
)
MZ Int -> MZ Int
(z,s’) = f y
++ "." ++ s’)
Maybe
nach :: (Int -> Maybe Int) -> Maybe Int
-> Maybe Int
nach f y = case y of
Nothing -> Nothing
Just y -> f y
I
Fehlermitteilung
nach :: (Int -> MayFail Int) -> MayFail Int
-> MayFail Int
nach f y = case y of
Error c -> Error c
Result y -> f y
107
Monaden
Ziel: Verknüpfung von Berechnungen zu komplexeren Berechnungen
class Monad m where
return :: a -> m a
( >>= ) :: m a -> (a -> m b) -> m b
mit den Operationen:
return (Eingang)
return :: a -> m a
bind (sequentielle Verknüpfung)
(>>=) :: m a -> (a -> m b) -> m b
Monaden-Gesetze:
I
(return x) >>= f == f x
I
m >>= return == m
I
( m >>= f) >>= g == m >>= (\ x -> f x >>= g)
108
Beispiel Zusatzinformation
data MZ a = MZ (a, String)
instance Monad MZ where
-- return :: Int -> MZ Int
return x = (x, "")
-- (>>=) :: MZ Int -> (Int -> MZ Int) -> MZ Int
(y,s) >>= f = let (z,s’) = f y
in (z, s ++ "." ++ s’)
109
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 )
mit Hilfe der Maybe-Monade
instance Monad Maybe where
return = \ x -> Just x
m >>= f = case m of
Nothing -> Nothing
Just x -> f x
kürzer und übersichtlicher:
evaluate e l >>= \ a ->
evaluate e r >>= \ b ->
return ( a + b )
110
MayFail-Monade
data MayFail a = Error EC | Result a
data EC = Fgross | Gklein
statt:
case ( evaluate e l ) of
Error c -> Error c
Result a -> case ( evaluate e r ) of
Error c -> Error c
Result b -> Result ( a + b )
mit Hilfe der MayFail-Monade
instance Monad MayFail where
return = \ x -> Just x
m >>= f = case m of
Error c -> Error c
Result b -> f b
kürzer und übersichtlicher:
evaluate e l >>= \ a ->
evaluate e r >>= \ b ->
return ( a + b )
111
Listen-Monade
instance Monad [a] 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)
112
Do-Notation für Monaden
Beispiel: original
eval e l >>= \ a ->
eval e r >>= \ b ->
return ( a + b )
do-Notation (implizit geklammert)
do a <- eval e l
b <- eval 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
113
Berechnungen mit Nebenwirkungen
Nebenwirkungen sind mitunter erwünscht, z.B.
I
von-Neumann-Maschine: Änderungen im Hauptspeicher
I
Modellierung von Prozessen mit Zustandsübergängen
Zustände haben Typ State
Übergang von Val zu
State -> (State, Val)
114
Zustands-Monade
data State s a = ST ( s -> (a,s) )
instance Monad State where
return :: a -> State s a
return x = ST (\x -> (x,s))
(>>=) :: State s a -> (a -> State s b) -> State s b
(ST m) >>= f = ST (\s -> let (x,s1) = m s
ST f’ = f x
in f’ s1)
Verwendung:
import Control.Monad.State
tick :: State Integer ()
tick = do c <- get ; put $ c + 1
evalState ( do tick ; tick ; get ) 0
115
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 >>= )
116
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’
117
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
...
118
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
119
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
120
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
121
Ein- und Ausgabe-Anweisungen
Eingabe:
x <- A
mit Aktion A
(erzeugt Variablenbindung)
Beispiel (Kombination):
echoline :: IO ()
echoline = do
input <- getLine
putStr input
122
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)
123
Hakell-Compiler ghc
Programm hello.hs:
module Main where
main = putStrLn "Hello"
mit ghc kompilieren
ghc -o hello hello.hs
und ausführen:
./hello
124
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 sumlist.hs
./sumlist
oder interpretieren (wie bisher) mit ghci:
:l Main
main
125
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
126
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
127
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)
128
Beipiel Datei-Ein- und Ausgabe
readFile :: FilePath -> IO String
putStrLn :: String -> IO ()
Wegen der Monad-Instanz: do-Notation
do cs <- readFile "foo.bar"
putStrLn cs
main :: IO ()
main = do
putStr " Filename:"
fname <- getLine
cont <- readFile fname
putStr cont
129
Herunterladen