Praktische Informatik 3: Funktionale Programmierung Vorlesung 10 vom 20.12.2016: Aktionen und Zustände
Christoph Lüth Universität Bremen Wintersemester 2016/17
16:02:32 2017-01-17 1 [26]
Fahrplan
I Teil I: Funktionale Programmierung im Kleinen I Teil II: Funktionale Programmierung im Großen I Teil III: Funktionale Programmierung im richtigen Leben
I Aktionen und Zustände
IMonaden als Berechnungsmuster
IDomänenspezifische Sprachen (DSLs)
IScala — Eine praktische Einführung
IRückblich & Ausblick
PI3 WS 16/17 2 [26]
Inhalt
I Ein/Ausgabe in funktionale Sprachen
I Wo ist dasProblem?
I Aktionenund der DatentypIO.
I VordefinierteAktionen
I Beispiel: Wortratespiel
I AktionenalsWerte
PI3 WS 16/17 3 [26]
Ein- und Ausgabe in funktionalen Sprachen
Umwelt Haskell
Aktionen
Umwelt Reine
Funktionen
Haskell
Problem:
IFunktionen mit Seiteneffekten nicht referentiell transparent.
I readString :: . . .→String??
Lösung:
ISeiteneffekte am Typ erkennbar IAktionen
IKönnennurmitAktionen komponiert werden
I„einmal Aktion, immer Aktion“
PI3 WS 16/17 4 [26]
Aktionen als abstrakter Datentyp
I ADT mit OperationenKompositionundLifting
I Signatur:
typeIOα
(=) :: IOα→ (α→IOβ) →IOβ return :: α→ IOα
I DazuelementareAktionen (lesen, schreiben etc)
PI3 WS 16/17 5 [26]
Elementare Aktionen
I Zeile von Standardeingabe (stdin)lesen:
getLine :: IO String
I Zeichenkette auf Standardausgabe (stdout)ausgeben:
putStr :: String→ IO ()
I Zeichenkette mit Zeilenvorschubausgeben:
putStrLn :: String→ IO ()
PI3 WS 16/17 6 [26]
Einfache Beispiele
I Echoeinfach echo1 :: IO ()
echo1 = getLine= putStrLn
I Echomehrfach echo :: IO ()
echo = getLine= putStrLn=λ_→ echo I Was passiert hier?
I Verknüpfenvon Aktionen mit=
I Jede Aktion gibtWertzurück
PI3 WS 16/17 7 [26]
Noch ein Beispiel
I Umgekehrtes Echo:
ohce :: IO () ohce = getLine
=λs→ putStrLn ( reverse s ) ohce
I Was passiert hier?
IReineFunktionreversewird innerhalb vonAktionputStrLngenutzt
IFolgeaktionohcebenötigtWertder vorherigen Aktion nicht
IAbkürzung:
pq = p=λ_→ q
PI3 WS 16/17 8 [26]
Die do-Notation
I Syntaktischer Zucker fürIO:
echo = getLine
=λs→ putStrLn s echo
⇐⇒
echo =
do s← getLine putStrLn s echo
I Rechts sind=,implizit.
I Es gilt dieAbseitsregel.
I Einrückungderersten Anweisungnachdobestimmt Abseits.
PI3 WS 16/17 9 [26]
Drittes Beispiel
I Zählendes, endliches Echo echo3 :: Int→ IO () echo3 cnt =do
putStr (show cnt ++ " : ␣" ) s← getLine
i f s6= "" then do
putStrLn $ show cnt ++ " : ␣"++ s echo3 ( cnt+ 1)
else return () I Was passiert hier?
IKombinationausKontrollstrukturenundAktionen
IAktionenalsWerte
IGeschachteltedo-Notation
PI3 WS 16/17 10 [26]
Module in der Standardbücherei
I Ein/Ausgabe, Fehlerbehandlung (ModulSystem . IO, Control . Exception)
I Zufallszahlen (ModulSystem .Random)
I Kommandozeile, Umgebungsvariablen (ModulSystem . Environment) I Zugriff auf das Dateisystem (ModulSystem . Directory)
I Zeit (ModulSystem .Time)
PI3 WS 16/17 11 [26]
Ein/Ausgabe mit Dateien
I ImPreludevordefiniert:
IDateien schreiben (überschreiben, anhängen):
type FilePath = String
w r i t e F i l e :: FilePath → String →IO () appendFile :: FilePath → String →IO () I Datei lesen (verzögert):
readFile :: FilePath →IO String
I Mehr Operationenim ModulSystem . IOder Standardbücherei
IBuffered/Unbuffered, Seeking, &c.
IOperationen aufHandle
I Noch mehr Operationen inSystem . Posix
IFiledeskriptoren, Permissions, special devices, etc.
PI3 WS 16/17 12 [26]
Beispiel: Zeichen, Wörter, Zeilen zählen (wc)
wc :: String→IO () wc f i l e =
docont ← readFile f i l e putStrLn $ f i l e ++ " : ␣"++
show ( length ( l i n e s cont ) , length (words cont ) , length cont ) I Datei wird gelesen
I Anzahl Zeichen, Worte, Zeilen gezählt I Erstaunlich (hinreichend) effizient
PI3 WS 16/17 13 [26]
Aktionen als Werte
I AktionensindWertewie alle anderen.
I DadurchDefinitionvonKontrollstrukturenmöglich.
I Endlosschleife:
forever :: IO α→IOα forever a = aforever a
I Iteration (feste Anzahl):
forN :: Int→IOα→ IO () forN n a | n == 0 = return ()
| otherwise = aforN (n−1) a
PI3 WS 16/17 14 [26]
Kontrollstrukturen
I VordefinierteKontrollstrukturen (Control .Monad):
when :: Bool→IO ()→IO () I Sequenzierung:
sequence :: [ IOα]→IO [α]
I Sonderfall:[ ( ) ]als() sequence_ :: [ IO ( ) ]→IO () I Map und Filter für Aktionen:
mapM :: (α→ IOβ)→ [α]→IO [β]
mapM_ :: (α→ IO ())→ [α]→IO () filterM :: (α→ IO Bool) → [α] → IO [α]
PI3 WS 16/17 15 [26]
Fehlerbehandlung
I Fehlerwerden durchExceptionrepräsentiert (Modul Control . Exception)
I ExceptionistTypklasse— kann durch eigene Instanzen erweitert werden
IVordefinierte Instanzen: u.a.IOError
I FehlerbehandlungdurchAusnahmen(ähnlich Java) catch :: Exceptionγ⇒ IOα→ (γ→ IOα) →IOα try :: Exceptionγ⇒ IOα→IO ( Either γ α)
I Faustregel:catchfür unerwartete Ausnahmen,tryfür erwartete I Fehlerbehandlungnur in Aktionen
PI3 WS 16/17 16 [26]
Fehler fangen und behandeln
I Fehlerbehandlung fürwc:
wc2 :: String→ IO () wc2 f i l e =
catch (wc f i l e )
(λe → putStrLn $ " Fehler : ␣"++ show (e :: IOError ))
I IOErrorkann analysiert werden (sieheSystem . IO . Error) I readmit Ausnahme bei Fehler (statt Programmabbruch):
readIO :: Readα⇒ String→IOα
PI3 WS 16/17 17 [26]
Ausführbare Programme
I Eigenständiges Programm istAktion I Hauptaktion:main :: IO ()in ModulMain
I. . . oder mit der Option-main-isM.fsetzen Iwcals eigenständiges Programm:
moduleMainwhere
importSystem . Environment ( getArgs ) importControl . Exception
. . .
main :: IO () main =do
args ← getArgs mapM_ wc2 args
PI3 WS 16/17 18 [26]
Beispiel: Traversion eines Verzeichnisbaums
I Verzeichnisbaum traversieren, und für jede Datei eineAktion ausführen:
travFS :: ( FilePath→ IO ())→ FilePath→IO () I Nutzt Funktionalität ausSystem . Directory,System . FilePath
travFS action p =do
res ← try ( getDirectoryContents p) case res of
Left e → putStrLn $ "ERROR: ␣"++ show (e :: IOError ) Right cs→do let cp = map (p</>) ( cs \\ [ " . " , " . . " ] )
d i r s ← filterM doesDirectoryExist cp f i l e s ← filterM doesFileExist cp mapM_ action f i l e s
mapM_ ( travFS action ) d i r s
PI3 WS 16/17 19 [26]
So ein Zufall!
I Zufallswerte:
randomRIO :: (α, α)→IOα
IWarum istrandomIOAktion?
I Beispiele:
IAktion zufällig oft ausführen:
atmost :: Int→IOα→IO [α]
atmost most a =
do l←randomRIO (1 , most) sequence ( r e p l i c a t e l a)
IZufälliges Element aus einer nicht-leeren Liste auswählen:
pickRandom :: [α]→IOα
pickRandom [ ] = er ro r "pickRandom : ␣empty␣ l i s t "
pickRandom xs =do
i ←randomRIO (0 , length xs−1) return $ xs ! ! i
PI3 WS 16/17 20 [26]
Fallbeispiel: Wörter raten
I Unterhaltungsprogramm: der Benutzer rät Wörter
I Benutzer kann einzelne Buchstaben eingeben oder das ganze Wort
I Wort wird maskiert ausgegeben, nur geratene Buchstaben angezeigt
PI3 WS 16/17 21 [26]
Wörter raten: Programmstruktur
I Trennung zwischen Spiel-Logik und Nutzerschnittstelle I Spiel-Logik (GuessGame):
IProgrammzustand:
data State = St { word :: String−−Zu ratendes Wort , h i t s :: String−−Schon geratene Buchstaben , miss :: String−−Falsch geratene Buchstaben }
IInitialen Zustand (Wort auswählen):
i n i t i a l S t a t e :: [ String ]→IO State
INächsten Zustand berechnen (Charist Eingabe des Benutzers):
data Result = Miss | Hit | Repetition | GuessedIt | TooManyTries processGuess :: Char→State→( Result , State )
PI3 WS 16/17 22 [26]
Wörter raten: Nutzerschnittstelle
IHauptschleife (play)
I Zustand anzeigen
I Benutzereingabe abwarten
I Neuen Zustand berechnen
I Rekursiver Aufruf mit neuem Zustand
I Programmanfang (main)
I Lexikon lesen
I Initialen Zustand berechnen
I Hauptschleife aufrufen play :: State→IO ()
play st =do putStrLn ( render st ) c← getGuess st
case ( processGuess c st ) of ( Hit , st ) → play st
(Miss , st )→doputStrLn "Sorry , ␣no . " ; play st
( Repetition , st )→doputStrLn "You␣already␣ t r i e d ␣that . " ; play st ( GuessedIt , st )→ putStrLn "Congratulations , ␣you␣guessed␣ i t . "
(TooManyTries , st )→
putStrLn $ "The␣word␣was␣"++ word st ++ "␣−−␣you␣lose . "
PI3 WS 16/17 23 [26]
Kontrollumkehr
I Trennung von Logik (State,processGuess) und Nutzerinteraktion nützlich und sinnvoll
I Wird durch Haskell Tysystem unterstützt (keine UI ohneIO) I Nützlich für andere UI mitKontrollumkehr
I Beispiel: ein GUI für das Wörterratespiel (mitGtk2hs)
IGUI ruft Handler-Funktionen des Nutzerprogramms auf
ISpielzustand in Referenz (IORef) speichern I Vgl. MVC-Pattern (Model-View-Controller)
PI3 WS 16/17 24 [26]
Zusammenfassung
I Ein/Ausgabe in Haskell durchAktionen
I Aktionen(TypIOα) sind seiteneffektbehaftete Funktionen I Kompositionvon Aktionen durch
(=) :: IOα→ (α→ IOβ)→IOβ return :: α→ IOα
I do-Notation
I Fehlerbehandlung durch Ausnahmen (IOError,catch,try).
I Verschiedene Funktionen der Standardbücherei:
I Prelude:getLine,putStr,putStrLn,readFile,writeFile
I Module:System.IO,System.Random
I Aktionen sindimplementiertalsZustandstransformationen
PI3 WS 16/17 25 [26]
Frohe Weihnachten und einen Guten Rutsch!
PI3 WS 16/17 26 [26]