Praktische Informatik 3: Funktionale Programmierung Vorlesung 10 vom 18.01.2021: Aktionen und Zustände
Christoph Lüth
Wintersemester 2020/21
11:51:34 2021-02-22 1 [32]
Fahrplan
ITeil I: Funktionale Programmierung im Kleinen ITeil II: Funktionale Programmierung im Großen
ITeil III: Funktionale Programmierung im richtigen Leben IAktionen und Zustände
IMonaden als Berechnungsmuster IFunktionale Webanwendungen IScala — Eine praktische Einführung IRückblick & Ausblick
PI3 WS 20/21 2 [32]
Inhalt
IEin/Ausgabe in funktionale Sprachen IWo ist dasProblem?
IAktionenund der DatentypIO.
IVordefinierteAktionen IBeispiel: Wortratespiel IAktionenalsWerte
PI3 WS 20/21 3 [32]
I. Funktionale Ein/Ausgabe
PI3 WS 20/21 4 [32]
Ein- und Ausgabe in funktionalen Sprachen
Umwelt Haskell
Umwelt Haskell
Aktionen Reine
Funktionen
Haskell
Problem:
IFunktionen mit Seiteneffekten nicht referentiell transparent.
IreadString ::...→String??
Lösung:
ISeiteneffekte am Typ erkennbar IAktionen
IKönnennurmitAktionenkomponiert werden I„einmal Aktion, immer Aktion“
PI3 WS 20/21 5 [32]
Aktionen als abstrakter Datentyp
IADT mit OperationenKompositionundLifting
ISignatur:
typeIOα
(=) :: IOα→(α→IOβ)→IOβ return :: α→IOα
IDazuelementareAktionen (lesen, schreiben etc)
PI3 WS 20/21 6 [32]
Elementare Aktionen
IZeile von Standardeingabe (stdin)lesen:
getLine :: IO String
IZeichenkette auf Standardausgabe (stdout)ausgeben:
putStr :: String→IO ()
IZeichenkette mit Zeilenvorschubausgeben:
putStrLn :: String→IO ()
PI3 WS 20/21 7 [32]
Einfache Beispiele
IEchoeinfach echo1 :: IO ()
echo1=getLine=putStrLn IEchomehrfach
echo :: IO ()
echo=getLine=putStrLn=λ_ →echo IWas passiert hier?
IVerknüpfenvon Aktionen mit= IJede Aktion gibtWertzurück
PI3 WS 20/21 8 [32]
Noch ein Beispiel
IUmgekehrtes Echo:
ohce :: IO ()
ohce=getLine=λs→putStrLn (reverse s)ohce IWas passiert hier?
IReineFunktionreversewird innerhalb vonAktionputStrLngenutzt IFolgeaktionohcebenötigtWertder vorherigen Aktion nicht IAbkürzung:
pq=p=λ_→ q
PI3 WS 20/21 9 [32]
Die do-Notation
ISyntaktischer Zucker fürIO:
echo= getLine
=λs→putStrLn s echo
⇐⇒
echo= dos←getLine
putStrLn s echo IRechts sind=,implizit
IMit←gebundene Bezeichnerüberlagernvorherige IEs gilt dieAbseitsregel.
IEinrückungderersten Anweisungnachdobestimmt Abseits.
PI3 WS 20/21 10 [32]
Drittes Beispiel
IZählendes, endliches Echo echo3 :: Int→IO () echo3 cnt=do
putStr (show cnt++":␣") s←getLine
ifs6="" then do
putStrLn $ show cnt++":␣"++s echo3 (cnt+ 1)
elsereturn () IWas passiert hier?
IKombinationausKontrollstrukturenundAktionen IAktionenalsWerte
IGeschachteltedo-Notation
PI3 WS 20/21 11 [32]
Zeit für eine Pause
Übung 10.1: Say My Name!
Wie sieht ein Haskell-Program aus, das erst nach dem Namen des Gegenübers fragt, und dann mitHallo, Christoph!(oder was eingegeben wurde) freundlich grüßt?
Lösung:
greeter :: IO () greeter=do
putStr "What’s␣your␣name,␣love?␣"
s ←getLine
putStrLn $ "Hullo,␣" ++s++".␣Pleased␣to␣meet␣you."
IputStrstattputStrLnerlaubt „Prompting“
IArgumente vonputStrLnklammern (oder$)
PI3 WS 20/21 12 [32]
II. Aktionen als Werte
PI3 WS 20/21 13 [32]
Aktionen als Werte
IAktionensindWertewie alle anderen.
IDadurchDefinitionvonKontrollstrukturenmöglich.
IEndlosschleife:
forever :: IOα→IOα forever a=aforever a IIteration (feste Anzahl):
forN :: Int→IOα→IO () forN n a | n==0 =return ()
| otherwise=aforN (n-1) a
PI3 WS 20/21 14 [32]
Kontrollstrukturen
IVordefinierteKontrollstrukturen (Control.Monad):
when :: Bool→IO ()→IO () ISequenzierung:
sequence :: [IOα]→IO [α]
ISonderfall:[()]als() sequence_ :: [IO ()]→IO ()
IMap und Filter für Aktionen:
mapM :: (α→IOβ)→[α]→ IO [β]
mapM_ :: (α→IO ())→[α]→IO () filterM :: (α→IO Bool)→[α]→IO [α]
PI3 WS 20/21 15 [32]
Jetzt ihr!
Übung 10.2: Eine „While-Schleife“ in Haskell Schreibt einen Kombinator
while :: IO Bool→IOα→IO ()
der solange das zweite Argument (den Rumpf) auswertet wie das erste Argument zuTrue auswertet.
Lösung:
IErste Lösung:
while c b=doa←c;ifa thenbwhile c belsereturn () IVorteil: istendrekursiv.
IWieso eigentlichIO ()?
PI3 WS 20/21 16 [32]
III. Ein/Ausgabe
PI3 WS 20/21 17 [32]
Ein/Ausgabe mit Dateien
IImPreludevordefiniert:
IDateien schreiben (überschreiben, anhängen):
typeFilePath= String
writeFile :: FilePath→String→IO () appendFile :: FilePath→String→IO () IDatei 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 20/21 18 [32]
Beispiel: Zeichen, Wörter, Zeilen zählen (wc)
wc :: String→IO () wc file=
docont ←readFile file putStrLn $ file++":␣"++
show (length (lines cont))++"␣lines,␣" ++ show (length (words cont))++"␣words,␣"++ show (length cont)++"␣bytes."
IDatei wird gelesen
IAnzahl Zeichen, Worte, Zeilen gezählt IErstaunlich (hinreichend) effizient
PI3 WS 20/21 19 [32]
Ein/Ausgabe mit Dateien: Abstraktionsebenen
IEinfach:readFile,writeFile
IFortgeschritten: ModulSystem.IOder Standardbücherei IBuffered/Unbuffered, Seeking, &c.
IOperationen aufHandle
ISystemnah:ModulSystem.Posix IFiledeskriptoren, Permissions, special devices, etc.
PI3 WS 20/21 20 [32]
IV. Ausnahmen und Fehlerbehandlung
PI3 WS 20/21 21 [32]
Fehlerbehandlung
IFehlerwerden durchExceptionrepräsentiert (ModulControl.Exception) IExceptionistTypklasse— kann durch eigene Instanzen erweitert werden IVordefinierte Instanzen: u.a.IOError
IFehlerbehandlungdurchAusnahmen(ähnlich Java) throw :: Exceptionγ⇒γ→α
catch :: Exceptionγ⇒IOα→(γ→IOα)→ IOα try :: Exceptionγ⇒IOα→IO (Eitherγ α) IFaustregel:catchfür unerwartete Ausnahmen,tryfür erwartete IAusnahmen überall, Fehlerbehandlungnur in Aktionen
PI3 WS 20/21 22 [32]
Fehler fangen und behandeln
“Ask forgiveness not permission” (Grace Hopper)
Generelle Regel:FehlerbehandlungdurchAusnahmebehandlungbesser als vorherige Abfrage von Fehlerbedingungen.
IWarum? Umwelt nichtsequentiell.
IFehlerbehandlung fürwc:
wc2 :: String→IO () wc2 file=
catch (wc file)
(λe →putStrLn $ "Fehler:␣"++show (e :: IOError)) IIOErrorkann analysiert werden (sieheSystem.IO.Error)
Ireadmit Ausnahme bei Fehler (statt Programmabbruch):
readIO :: Readα⇒String→IOα
PI3 WS 20/21 23 [32]
Ausführbare Programme
IEigenständiges Programm istAktion IHauptaktion: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
putStrLn $ "Command␣line␣arguments:␣"++show args mapM_ wc2 args
PI3 WS 20/21 24 [32]
Beispiel: Traversion eines Verzeichnisbaums
IVerzeichnisbaum traversieren, und für jede Datei eineAktionausführen:
travFS :: (FilePath→IO ())→FilePath→IO () travFS action p=catch (do
cs←getDirectoryContents p
let cp=map (p</>) (cs \\ [".", ".."]) dirs ←filterM doesDirectoryExist cp files←filterM doesFileExist cp mapM_ action files
mapM_ (travFS action) dirs)
(λe →putStrLn $ "ERROR:␣"++show (e :: IOError)) INutzt Funktionalität ausSystem.Directory,System.FilePath
PI3 WS 20/21 25 [32]
Alles zählt.
Übung 10.3: Alles zählt
KombiniertTraverseundWCzu einem Programm ls :: FilePath→IO ()
welches in einem gegeben Verzeichnis den Inhalt aller darin enthaltenen Dateien zählt.
Lösung:wc2(mit Fehlerbehandlung) wird einfach die Traversionsfunktion:
ls=travFS wc2 Das ist alles.
PI3 WS 20/21 26 [32]
V. Anwendungsbeispiel
PI3 WS 20/21 27 [32]
So ein Zufall!
IZufallswerte:
randomRIO :: (α,α)→IOα IWarum istrandomIOAktion?
IBeispiele:
IAktion zufällig oft ausführen:
atmost :: Int→IOα→IO [α]
atmost most a=
dol←randomRIO (1, most) sequence (replicate l a) IZufälligen String erzeugen:
randomStr :: IO String
randomStr=atmost 40 (randomRIO (’a’,’z’))
IHinweis: Funktionen ausSystem.Randomzu importieren, muss ggf. installiert werden.
PI3 WS 20/21 28 [32]
Fallbeispiel: Wörter raten
IUnterhaltungsprogramm: der Benutzer rät Wörter
IBenutzer kann einzelne Buchstaben eingeben
IWort wird maskiert ausgegeben, nur geratene Buchstaben angezeigt
PI3 WS 20/21 29 [32]
Wörter raten: Programmstruktur
IHauptschleife:
play :: String→String→String→ IO ()
IArgumente: Geheimnis, geratene Buchstaben (enthalten, nicht enthalten) IBenutzereingabe:
getGuess :: String→String→IO Char
IArgumente: geratene Zeichen (im Geheimnis enthalten, nicht enthalten) IHauptfunktion:
main :: IO ()
ILiest ein Lexikon, wählt Geheimnis aus, ruft Hauptschleife auf
PI3 WS 20/21 30 [32]
Nunc est ludendum.
Übung 10.3: Linguistic Interlude
Ladet den Quellcode herunter, übersetzt das Spiel und ratet fünf Wörter.
Wer noch etwas tun möchte, kann das Spiel so erweitern, dass es nachdem das Wort erfolgreich geraten wurde, ein neues Wort rät, und insgesamt zählt, wieviele Worte schon (nicht) geraten wurden.
PI3 WS 20/21 31 [32]
Zusammenfassung
IEin/Ausgabe in Haskell durchAktionen
IAktionen(TypIO α) sind seiteneffektbehaftete Funktionen IKompositionvon Aktionen durch
(=) :: IOα→(α→IOβ)→IOβ return :: α→IOα
Ido-Notation
IFehlerbehandlung durch Ausnahmen (IOError,catch,try).
IVerschiedene Funktionen der Standardbücherei:
IPrelude:getLine,putStr,putStrLn,readFile,writeFile IModule:System.IO,System.Random
INächste Vorlesung: Wie sind Aktionen eigentlichimplementiert? Schwarze Magie?
PI3 WS 20/21 32 [32]