Praktische Informatik 3: Funktionale Programmierung Vorlesung 10 vom 18.12.2018: Aktionen und Zustände
Christoph Lüth Universität Bremen Wintersemester 2018/19
16:03:15 2018-12-18 1 [25]
Organisatorisches
I Probeklausur
IEchte Klausur: 2 Stunden, wahrscheinlich 4 Programmieraufgaben
IProbeklausur und Fragen werdenveröffentlicht.
I Tutorium Do 16– 18:Raumänderung(diese Woche in MZH 1450)
PI3 WS 18/19 2 [25]
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
I Monaden als Berechnungsmuster
I Domänenspezifische Sprachen (DSLs)
I Scala — Eine praktische Einführung
I Rückblich & Ausblick
PI3 WS 18/19 3 [25]
Inhalt
I Ein/Ausgabe in funktionale Sprachen
I Wo ist dasProblem?
I Aktionenund der DatentypIO.
I VordefinierteAktionen
I Beispiel: Wortratespiel
I AktionenalsWerte
PI3 WS 18/19 4 [25]
Ein- und Ausgabe in funktionalen Sprachen
Umwelt Haskell
Aktionen
Umwelt Reine
Funktionen
Haskell
Problem:
I Funktionen mit Seiteneffekten nicht referentiell transparent.
I readString :: . . .→String??
Lösung:
I Seiteneffekte am Typ erkennbar I Aktionen
I KönnennurmitAktionen komponiert werden
I „einmal Aktion, immer Aktion“
PI3 WS 18/19 5 [25]
Aktionen als abstrakter Datentyp
I ADT mit OperationenKompositionundLifting
I Signatur:
typeIOα
(=) :: IOα→ (α→ IOβ) → IOβ
return :: α→IOα
I DazuelementareAktionen (lesen, schreiben etc)
PI3 WS 18/19 6 [25]
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 18/19 7 [25]
Einfache Beispiele
I Echoeinfach echo1 :: IO ()
echo1 = getLine= putStrLn
I Echomehrfach echo :: IO ()
echo = getLine= putStrLn=λ_→ echo
I Was passiert hier?
IVerknüpfenvon Aktionen mit=
IJede Aktion gibtWertzurück
PI3 WS 18/19 8 [25]
Noch ein Beispiel
I Umgekehrtes Echo:
ohce :: IO () ohce = getLine
=λs→ putStrLn ( reverse s ) ohce
I Was passiert hier?
I ReineFunktionreversewird innerhalb vonAktionputStrLngenutzt
I FolgeaktionohcebenötigtWertder vorherigen Aktion nicht
I Abkürzung:
pq = p=λ_→ q
PI3 WS 18/19 9 [25]
Die do-Notation
I Syntaktischer Zucker fürIO:
echo = getLine
=λs→ putStrLn s echo
⇐⇒
echo =
dos← getLine putStrLn s echo
IRechts sind=,implizit.
I Es gilt dieAbseitsregel.
IEinrückungderersten Anweisungnachdobestimmt Abseits.
PI3 WS 18/19 10 [25]
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?
I KombinationausKontrollstrukturenundAktionen
I AktionenalsWerte
I Geschachteltedo-Notation
PI3 WS 18/19 11 [25]
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 “Lazy I/O”: Zugriff auf Dateien erfolgtverzögert
IInteraktion von nicht-strikter Auswertung mit zustandsbasiertem Dateisystem kann überraschend sein
PI3 WS 18/19 12 [25]
Ein/Ausgabe mit Dateien: Abstraktionsebenen
I Einfach:readFile,w r i t e F i l e
I Fortgeschritten: ModulSystem . IOder Standardbücherei
I Buffered/Unbuffered, Seeking, &c.
I Operationen aufHandle
I Systemnah:ModulSystem . Posix
I Filedeskriptoren, Permissions, special devices, etc.
PI3 WS 18/19 13 [25]
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 18/19 14 [25]
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 18/19 15 [25]
Kontrollstrukturen
I VordefinierteKontrollstrukturen (Control .Monad):
when :: Bool→ IO ()→ IO ()
I Sequenzierung:
sequence :: [ IOα]→ IO [α]
ISonderfall:[ ( ) ]als() sequence_ :: [ IO ( ) ]→IO ()
I Map und Filter für Aktionen:
mapM :: (α→IOβ)→ [α]→IO [β] mapM_ :: (α→IO ())→ [α]→IO ()
filterM :: (α→IO Bool) → [α] →IO [α]
PI3 WS 18/19 16 [25]
Fehlerbehandlung
I Fehlerwerden durchExceptionrepräsentiert (Modul Control . Exception)
I ExceptionistTypklasse— kann durch eigene Instanzen erweitert werden
I Vordefinierte Instanzen: u.a.IOError
I FehlerbehandlungdurchAusnahmen(ähnlich Java) throw :: Exception γ⇒γ→α
catch :: Exceptionγ⇒IOα→ (γ→IOα) → IOα try :: Exception γ⇒IOα→IO ( Either γ α)
I Faustregel:catchfür unerwartete Ausnahmen,tryfür erwartete
I Ausnahmen überall, Fehlerbehandlungnur in Aktionen
PI3 WS 18/19 17 [25]
Fehler fangen und behandeln
“Ask forgiveness not permission”
Generelle Regel:FehlerbehandlungdurchAusnahmebehandlung besser als Fehlerbedingungen abzufragen
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 18/19 18 [25]
Ausführbare Programme
I Eigenständiges Programm istAktion I Hauptaktion:main :: IO ()in ModulMain
I . . . oder mit der Option-main-isM.fsetzen
I wcals eigenständiges Programm:
moduleMain where
importSystem . Environment ( getArgs ) import Control . Exception
. . .
main :: IO () main =do
args← getArgs mapM_ wc2 args
PI3 WS 18/19 19 [25]
Beispiel: Traversion eines Verzeichnisbaums
I Verzeichnisbaum traversieren, und für jede Datei eineAktion ausführen:
travFS :: ( FilePath→IO ())→ FilePath→ IO () travFS action p = catch (do
cs← getDirectoryContents p
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 )
(λe →putStrLn $ "ERROR: ␣"++ show (e :: IOError ))
I Nutzt Funktionalität ausSystem . Directory,System . FilePath
PI3 WS 18/19 20 [25]
So ein Zufall!
I Zufallswerte:
randomRIO :: (α,α)→ IOα
I Warum istrandomIOAktion?
I Beispiele:
I Aktion zufällig oft ausführen:
atmost :: Int→IOα→IO [α]
atmost most a =
dol←randomRIO (1 , most) sequence ( r e p l i c a t e l a)
I Zufälligen String erzeugen:
randomStr :: IO String
randomStr = atmost 40 (randomRIO ( ’ a ’ , ’ z ’ ) )
PI3 WS 18/19 21 [25]
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 18/19 22 [25]
Wörter raten: Programmstruktur
I Hauptschleife:
play :: String→ String→ String→ IO ()
I Argumente: Geheimnis, geratene Buchstaben (enthalten, nicht enthalten)
I Benutzereingabe:
getGuess :: String→ String→IO Char
I Argumente: geratene Zeichen (im Geheimnis enthalten, nicht enthalten)
I Hauptfunktion:
main :: IO ()
I Liest ein Lexikon, wählt Geheimnis aus, ruft Hauptschleife auf
PI3 WS 18/19 23 [25]
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:
IPrelude:getLine,putStr,putStrLn,readFile, writeFile
IModule:System.IO,System.Random
I Aktionen sindimplementiertalsZustandstransformationen
PI3 WS 18/19 24 [25]
Frohe Weihnachten und einen Guten Rutsch!
PI3 WS 18/19 25 [25]