Funktionale Programmierung Das Funktionale Quiz Das Funktionale

Werbung
Funktionale Programmierung
Das Funktionale Quiz
Warum haben wir den Typ () erst letzte Woche gebraucht?
14.6.2005
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.
Bis jetzt hatten wir keine Seiteneffekte, d.h. wir haben Ausdrücke
immer angegeben, um ihren Wert zu erfahren.
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!
1-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.
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
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 ()
Themen heute
• Mehr zu I/O
• Monaden
• Roboter-Steuerung
<- 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.
8-14
Mehr I/O
Erklärung des do-Konstrukts
I/O mit Dateien
• type FilePath = String
• readFile :: FilePath -> IO String liest Datei ein
• writeFile :: FilePath -> String -> IO () schreibt
Datei
Der Typ IO a besitzt mehere Operationen:
return, putStr, getLine
• appendFile :: FilePath -> String -> IO () hängt an
Datei an
do kann Werte vom Typ IO a aneinanderreihen
Wie macht do das?
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
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
>>= statt do
addOneInt :: IO ()
addOneInt = do line <- getLine
putStrLn (show (1 + read line))
Mit<<=:
addOneInt =
getLine >>=
\line -> putStrLn (show (1 + read line))
15-18
übersetzung von do nach >>=
Monaden
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}
Datentyp der Berechnung kapselt
Berechung: Auswertung die Seiteneffekt beinhalten kann
Sequenzierung mit >>=
Ermöglicht es, Seiteneffekte in funktionale Programme einzubetten
wobei gilt m >> k = m >>= \_ -> k
Monad
Geforderte Eigenschaften der Operationen
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
• return x sollte nur den Wert x liefern, ohne Seiteneffekte
• >>= ist assoziativ
• fail s sollte einer fehlgeschlagenen Berechnung entsprechen
• fail entspricht Berechnung, die fehlgeschlagen ist. Für IO:
*Main> fail "foo"
*** Exception: user error (foo)
*Main>
19-22
Formale Definition: Monaden-Gesetze
Monadengesetze
Ersten beiden Punkte:
(return x) >>= f
m >>= return
(m >>= f) >>= g
(M1) und (M2) besagen, dass return die Identität für >>= ist.
== f x
== m
== m >>= (\x -> f x >>= g)
Keine formale Möglichkeit, fail zu beschreiben.
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
(M1)
(M2)
(M3)
(M3) besagt, dass >>= assoziativ ist.
Aber: Es gibt keine Möglichkeit, (M1) - (M3) automatisch zu
überprüfen.
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
23-26
Beispiel Nicht-Determinismus
Maybe-Monade
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]
instance Monad Maybe where
(Just x) >>= k
= k x
Nothing >>= k
= Nothing
return
= Just
fail s
= Nothing
Interpretation: Berechnung, die fehlschlagen kann
flipBool liefert True und False
Beispiel Maybe-Monade
Zustandsmonade
Brechnungen, die Zustand transformieren, d.h. verändern oder
auslesen
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
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))
27-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)
Imperative Robotersteuerung
• Domain-Specific-Language
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
Roboter-Kommandos
Roboter hat eine momentane Richtung, einen Stift und kann Münzen
legen und sammeln
1 Schritt vor
• Imperativ: Kommandos steuern Roboter
move :: Robot ()
Also Seiteneffekte => Roboter-Monade Robot a
turnRight:: Robot () 90º rechts drehen
Implementierung in Robot.hs, aber nicht zum Lesen gedacht
turnLeft:: Robot ()
90º links drehen
Roboter laufen lassen: runRobot :: Robot () -> IO ()
penDown :: Robot ()
Stift runter
penUp :: Robot ()
Stift hoch
31-34
Beispiel: Winkel zeichnen
Weitere Kommandos
In eine Richung drehen:
data Direction North | South | East | West
turnTo :: Direction -> Robot ()
do penDown
move
turnRight
move
Stiftfarbe wechseln
data Color = Black | Blue | Green | Cyan
| Red | Magenta | Yellow | White
deriving (Eq, Ord, Bounded, Enum, Ix, Show, Read)
setPenColor :: Color -> Robot ()
Reaktion auf Umwelt
Beispiel
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)
Bei Blockade nach rechts drehen
evade :: Robot ()
evade = cond blocked
(do turnRight
evade)
(do move
evade)
• cond1 :: Robot Bool -> Robot () -> Robot () if ohne
Alternative
35-38
Geliftete Operatoren
Liften von && und ||
liftM2 macht Funktion strikt:
isnt :: Robot Bool -> Robot Bool
isnt = liftM not
(>*),(<*) :: Robot Int -> Robot Int -> Robot Bool
(>*) = liftM2 (>)
(<*) = liftM2 (<)
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
Liften von && und ||
while für Roboter
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
while :: Robot Bool -> Robot () -> Robot ()
moveToWall :: Robot ()
moveToWall = while (isnt blocked)
move
39-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