Funktionale Programmierung

Werbung
Funktionale Programmierung
14.6.2005
1
Das Funktionale Quiz
Warum haben wir den Typ () erst letzte Woche gebraucht?
Bis jetzt hatten wir keine Seiteneffekte, d.h. wir haben Ausdrücke
immer angegeben, um ihren Wert zu erfahren.
2-3
Das Funktionale Quiz
Warum ist es ein Unterschied, ob eine Liste ⊥ als Element enthält
oder aus ⊥ aufgebaut ist?
Funktion die Liste konsumiert betrachtet Elemente möglicherweise
gar nicht.
4-5
Das Funktionale Quiz
Haskell-Implementierungen bieten eine Möglichkeit, I/O auch
innerhalb von Ausdrücken auszuführen:
putStrLn (unsafePerfomIO getLine)
Welchen Typ hat unsafePerfomIO?
unsafePerformIO :: IO a -> a Führt also I/O-Aktion aus.
Gefährlich, nicht in den Übungen verwenden!
6-7
Das Funktionale Quiz
IO.isEOF verändert Datei/Filedeskriptor nicht, warum hat es trotzdem
den Typ IO Bool und nicht nur Bool?
Das Ergebnis von IO.isEOF hängt von anderen I/O-Aktionen ab,
daher muss feststehen, vor/nach welchen I/O-Aktionen IO.isEOF
ausgewertet wird.
8-9
Das Funktionale Quiz
Warum haben wir copyInputToOutput nicht so definiert:
copyInputToOutput =
while (not isEOF)
(putStrLn getLine)
isEOF liefert IO Bool, das kann not nicht negieren
getLine liefert IO String, das kann putStrLn nicht ausgeben
10-11
Das Funktionale Quiz
Worin besteht das Problem in folgendem Programm:
goUntilEmpty :: IO ()
goUntilEmpty
= do line <- getLine
while (return (line /= []))
(do putStrlen line
line <- getLine
return ()
<- sieht zwar aus wie die Zuweisung in C/C++/Java, ist aber ein
Bindungskonstrukt, das einen neuen Scope einführt!
Im Beispiel ist also die Variable line im Test und in putStrLn ist
also immer die erste eingelesene Zeile.
12-13
Themen heute
• Mehr zu I/O
• Monaden
• Roboter-Steuerung
14
Mehr I/O
I/O mit Dateien
• type FilePath = String
• readFile :: FilePath -> IO String liest Datei ein
• writeFile :: FilePath -> String -> IO () schreibt
Datei
• appendFile :: FilePath -> String -> IO () hängt an
Datei an
Fehler(-Behandlung)
• ioError :: IOError -> IO a Erzeugt I/O-Fehler
• catch :: IO a -> (IOError -> IO a) -> IO a Fängt
I/O-Fehler im ersten Argument mittels zweitem Argument ab
15
Erklärung des do-Konstrukts
Der Typ IO a besitzt mehere Operationen:
return, putStr, getLine
do kann Werte vom Typ IO a aneinanderreihen
Wie macht do das?
16
Der Operator >>=
do verwendet den Operator >>= ("then" oder "bind")
(>>=) :: IO a -> (a -> IO b) -> IO b
• >>= reiht 2 I/O-Aktionen aneinander
• >>= übergibt den Wert Ergebnis der ersten Aktion an Funktion, die
zweite Aktion berechnet
• >>= gibt zweite Aktion zurück
17
>>= statt do
addOneInt :: IO ()
addOneInt = do line <- getLine
putStrLn (show (1 + read line))
Mit<<=:
addOneInt =
getLine >>=
\line -> putStrLn (show (1 + read line))
18
übersetzung von do nach >>=
do ist nur syntaktischer Zucker
do {e}
do {e;stmts}
do {p <- e; stmts}
= e
= e >> do {stmts}
= let ok p = do {stmts}
ok _ = fail "No match"
in e >>= ok
do {let decls; stmts} = let decls in do {stmts}
wobei gilt m >> k = m >>= \_ -> k
19
Monaden
Datentyp der Berechnung kapselt
Berechung: Auswertung die Seiteneffekt beinhalten kann
Sequenzierung mit >>=
Ermöglicht es, Seiteneffekte in funktionale Programme einzubetten
20
Monad
Monaden sind in Haskell als Konstruktorklasse definiert:
class Monad m where
(>>=)
:: m a -> (a -> m b) -> m b
(>>)
:: m a -> m b -> m b
return :: a -> m a
fail
:: String -> m a
m >> k = m >>= \_ -> k
fail s = error s
• fail entspricht Berechnung, die fehlgeschlagen ist. Für IO:
*Main> fail "foo"
*** Exception: user error (foo)
*Main>
21
Geforderte Eigenschaften der Operationen
• return x sollte nur den Wert x liefern, ohne Seiteneffekte
• >>= ist assoziativ
• fail s sollte einer fehlgeschlagenen Berechnung entsprechen
22
Formale Definition: Monaden-Gesetze
Ersten beiden Punkte:
(return x) >>= f
m >>= return
(m >>= f) >>= g
== f x
== m
== m >>= (\x -> f x >>= g)
(M1)
(M2)
(M3)
Keine formale Möglichkeit, fail zu beschreiben.
23
Monadengesetze
(M1) und (M2) besagen, dass return die Identität für >>= ist.
(M3) besagt, dass >>= assoziativ ist.
Aber: Es gibt keine Möglichkeit, (M1) - (M3) automatisch zu
überprüfen.
24
Identitätsmonade
newtype Id a = Id a
instance Monad Id where
(Id x) >>= f = f x
return x = Id x
Repräsentiert triviale Berechung:
• Es werden keine Seiteneffekte ausgeführt
• Werte werden sofort zurückgegeben
25
Listenmonade
instance Monad [] where
xs >>= f = concat (map f xs)
return x = [x]
fail s
= []
Interpretation: nicht-deterministische Berechnung
• Liste repräsentiert alle möglichen Ergebnisse
• >>= wendet Funktion auf alle Ergebnisse an
• return erzeugt einzelnes Ergebnis - also kein
Nicht-Determinismus
• fail liefert gar kein Ergebnis - also einen Fehler
26
Beispiel Nicht-Determinismus
flipBool = [True,False]
res = do x1 <- flipBool
x2 <- flipBool
x3 <- flipBool
return ((x1 && x2) || (x1 && x3))
=> res = [True,True,True,False,
False,False,False,False]
flipBool liefert True und False
27
Maybe-Monade
instance Monad Maybe where
(Just x) >>= k
= k x
Nothing >>= k
= Nothing
return
= Just
fail s
= Nothing
Interpretation: Berechnung, die fehlschlagen kann
28
Beispiel Maybe-Monade
teile :: Int -> Int -> Maybe Int
teile x 0 = Nothing
teile x y = Just (div x y)
addiere :: Int -> Int -> Maybe Int
addiere x y = Just (x+y)
r = do a <- addiere 1 (-1)
b <- teile 10 a
return (addiere a b)
test = r == Nothing
29
Zustandsmonade
Brechnungen, die Zustand transformieren, d.h. verändern oder
auslesen
data StateT s a = StateT (s -> (a,s))
instance Monad (StateT s) where
return v = StateT (\s -> (v,s))
(StateT st) >>= f =
StateT (\store -> let (v,s1)
= st store
(StateT st') = f v in
st' s1)
fetchStateT :: StateT s s
fetchStateT = StateT (\s -> (s,s))
storeStateT :: s -> StateT s ()
storeStateT v = StateT (\_ -> ((), v))
30
Beispiel Zustandsmonade
runStateT :: StateT s a -> s -> (a,s)
runStateT (StateT st) init = st init
isTwFv :: StateT Integer Bool
isTwFv = do x <- fetchStateT
storeStateT (x * x)
y <- fetchStateT
return (y == 25)
test = runStateT isTwFv 5
== (True,25)
31
Standardfunktionen für Monaden
Im Modul Monad
• Liften
liftM :: Monad m => (a -> b) -> m a -> m b
liftM f a = do a' <- a
return (f a')
• Analog: liftM2... liftM5
• Plätten
join
join x
:: (Monad m) => m (m a) -> m a
= x >>= id
32
Imperative Robotersteuerung
• Domain-Specific-Language
• Imperativ: Kommandos steuern Roboter
Also Seiteneffekte => Roboter-Monade Robot a
Implementierung in Robot.hs, aber nicht zum Lesen gedacht
Roboter laufen lassen: runRobot :: Robot () -> IO ()
33
Roboter-Kommandos
Roboter hat eine momentane Richtung, einen Stift und kann Münzen
legen und sammeln
move :: Robot ()
1 Schritt vor
turnRight:: Robot () 90º rechts drehen
turnLeft:: Robot ()
90º links drehen
penDown :: Robot ()
Stift runter
penUp :: Robot ()
Stift hoch
34
Beispiel: Winkel zeichnen
do penDown
move
turnRight
move
35
Weitere Kommandos
In eine Richung drehen:
data Direction North | South | East | West
turnTo :: Direction -> Robot ()
Stiftfarbe wechseln
data Color = Black | Blue | Green | Cyan
| Red | Magenta | Yellow | White
deriving (Eq, Ord, Bounded, Enum, Ix, Show, Read)
setPenColor :: Color -> Robot ()
36
Reaktion auf Umwelt
Roboter lebt in Gitter-Welt, begrenzt durch Wände
• blocked :: Robot Bool blockiert?
• blockedRight,blockedLeft, blockedBack :: Robot Bool
rechts/links/hinten blockiert?
Kontrollkonstrukte:
• cond :: Robot Bool -> Robot a -> Robot a -> Robot a
Bedingte Anweisung (wie if)
• cond1 :: Robot Bool -> Robot () -> Robot () if ohne
Alternative
37
Beispiel
Bei Blockade nach rechts drehen
evade :: Robot ()
evade = cond blocked
(do turnRight
evade)
(do move
evade)
38
Geliftete Operatoren
isnt :: Robot Bool -> Robot Bool
isnt = liftM not
(>*),(<*) :: Robot Int -> Robot Int -> Robot Bool
(>*) = liftM2 (>)
(<*) = liftM2 (<)
39
Liften von && und ||
liftM2 macht Funktion strikt:
liftM2 :: (Monad m) =>
(a -> b -> c) -> (m a -> m b -> m c)
liftM2 f a
do a' <b' <return
b =
a
b
(f a' b')
Unerwünscht für binäre logische Operatoren
40
Liften von && und ||
Deswegen "zu Fuß" definieren:
(||*) :: Robot Bool -> Robot Bool -> Robot Bool
b1 ||* b2 = do p <- b1
if p then return True
else b2
(&&*) :: Robot Bool -> Robot Bool -> Robot Bool
b1 &&* b2 = do p <- b1
if p then b2
else return False
41
while für Roboter
while :: Robot Bool -> Robot () -> Robot ()
moveToWall :: Robot ()
moveToWall = while (isnt blocked)
move
42
Münzen und Position
onCoin :: Robot Bool
Münze unter Roboter?
pickCoin :: Robot ()
Münze aufnehmen
dropCoin :: Robot ()
Münze ablegen
getPosition :: Robot (Int,Int) liefert Position
43
Zugehörige Unterlagen
Herunterladen