Praktische Informatik 3: Funktionale Programmierung Vorlesung 11 vom 08.01.2013: Aktionen und Zustände
Christoph Lüth
Universität Bremen
Wintersemester 2012/13
Fahrplan
I Teil I: Funktionale Programmierung im Kleinen
I Teil II: Funktionale Programmierung im Großen
I Abstrakte Datentypen
I Signaturen und Eigenschaften
I Spezifikation und Beweis
I Aktionen und Zustände
I Teil III: Funktionale Programmierung im richtigen Leben
Inhalt
I Ein/Ausgabe in funktionale Sprachen
I Wo ist dasProblem?
I Aktionenund der Datentyp IO.
I Aktionenals Werte
I Aktionenals Zustandstransformationen
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 Aktionenkönnen nurmit Aktionenkomponiert werden
I „einmal Aktion, immer Aktion“
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 Aktionenkönnen nurmit Aktionenkomponiert werden
I „einmal Aktion, immer Aktion“
Aktionen als abstrakter Datentyp
I ADT mit OperationenKomposition und Lifting
I Signatur:
type IO α
(=) :: IO α → (α→ IO β) → IO β
r e t u r n :: α→ IO α
I Pluselementare Operationen (lesen, schreiben etc)
Elementare Aktionen
I Zeile von stdin lesen:
g e t L i n e :: IO S t r i n g
I Zeichenkette auf stdout ausgeben:
p u t S t r :: S t r i n g→ IO ( )
I Zeichenkette mit Zeilenvorschubausgeben:
p u t S t r L n :: S t r i n g→ IO ( )
Einfache Beispiele
I Echoeinfach e c h o 1 :: IO ( )
e c h o 1 = g e t L i n e = p u t S t r L n
I Echomehrfach e c h o :: IO ( )
e c h o = g e t L i n e = p u t S t r L n = λ_ → e c h o
I Was passiert hier?
I Verknüpfenvon Aktionen mit=
I Jede Aktion gibtWertzurück
Noch ein Beispiel
I Umgekehrtes Echo:
o h c e :: IO ( ) o h c e = g e t L i n e
= λs→ p u t S t r L n ( r e v e r s e s ) o h c e
I Was passiert hier?
I ReineFunktion reverse wird innerhalb vonAktionputStrLngenutzt
I FolgeaktionohcebenötigtWertder vorherigen Aktion nicht
I Abkürzung:
p q = p =λ_ → q
Die do-Notation
I Syntaktischer Zucker fürIO:
e c h o = g e t L i n e
= λs→ p u t S t r L n s e c h o
⇐⇒
e c h o =
do s← g e t L i n e p u t S t r L n s e c h o
I Rechts sind=,implizit.
I Es gilt dieAbseitsregel.
I Einrückungderersten Anweisungnachdobestimmt Abseits.
Drittes Beispiel
I Zählendes, endliches Echo e c h o 3 :: I n t→ IO ( ) e c h o 3 c n t = do
p u t S t r ( show c n t ++ " : ␣ " ) s← g e t L i n e
i f s 6= " " then do
p u t S t r L n $ show c n t ++ " : ␣ "++ s e c h o 3 ( c n t+ 1 )
e l s e r e t u r n ( )
I Was passiert hier?
I KombinationausKontrollstrukturenundAktionen
I AktionenalsWerte
I Geschachteltedo-Notation
Module in der Standardbücherei
I Ein/Ausgabe, Fehlerbehandlung (ModulIO)
I Zufallszahlen (ModulRandom)
I Kommandozeile, Umgebungsvariablen (ModulSystem)
I Zugriff auf das Dateisystem (ModulDirectory)
I Zeit (ModulTime)
Ein/Ausgabe mit Dateien
I ImPreludevordefiniert:
I Dateien schreiben (überschreiben, anhängen):
type F i l e P a t h = S t r i n g
w r i t e F i l e :: F i l e P a t h → S t r i n g → IO ( ) a p p e n d F i l e :: F i l e P a t h → S t r i n g → IO ( )
I Datei lesen (verzögert):
r e a d F i l e :: F i l e P a t h → IO S t r i n g
I Mehr Operationenim Modul IOder Standardbücherei
I Buffered/Unbuffered, Seeking, &c.
I Operationen aufHandle
Beispiel: Zeichen, Wörter, Zeilen zählen (wc)
wc :: S t r i n g→ IO ( ) wc f i l e =
do c o n t ← r e a d F i l e f i l e p u t S t r L n $ f i l e++ " : ␣ "++
show ( l e n g t h ( l i n e s c o n t ) , l e n g t h ( w o r d s c o n t ) , l e n g t h c o n t )
I Nicht sehr effizient — Datei wirdim Speicher gehalten.
Beispiel: wc verbessert.
I Effizienter: Dateiinhalteinmal traversieren
c n t :: I n t→ I n t→ I n t→ B o o l→ S t r i n g
→ ( I n t , I n t , I n t ) c n t l w c _ [ ] = ( l , w , c ) c n t l w c s k i p ( x : x s )
| n o t ( i s S p a c e x ) && n o t s k i p = c n t l (w+1 ) ( c+1 ) True x s
| n o t ( i s S p a c e x ) && s k i p = c n t l w ( c+1 ) True x s
| o t h e r w i s e = c n t l ’ w ( c+1 ) F a l s e x s where l ’ = i f x == ’ \n ’ then l+1 e l s e l
I Hauptprogramm:
wc :: S t r i n g→ IO ( ) wc f i l e = do
c o n t ← r e a d F i l e f i l e
p u t S t r L n $ f i l e++ " : ␣ "++ show ( c n t 0 0 0 F a l s e c o n t )
I Datei wirdverzögert gelesen unddabei verbraucht.
Beispiel: wc verbessert.
I Effizienter: Dateiinhalteinmal traversieren
c n t :: I n t→ I n t→ I n t→ B o o l→ S t r i n g
→ ( I n t , I n t , I n t ) c n t l w c _ [ ] = ( l , w , c ) c n t l w c s k i p ( x : x s )
| n o t ( i s S p a c e x ) && n o t s k i p = c n t l (w+1 ) ( c+1 ) True x s
| n o t ( i s S p a c e x ) && s k i p = c n t l w ( c+1 ) True x s
| o t h e r w i s e = c n t l ’ w ( c+1 ) F a l s e x s where l ’ = i f x == ’ \n ’ then l+1 e l s e l
I Hauptprogramm:
wc :: S t r i n g→ IO ( ) wc f i l e = do
c o n t ← r e a d F i l e f i l e
p u t S t r L n $ f i l e++ " : ␣ "++ show ( c n t 0 0 0 F a l s e c o n t )
Aktionen als Werte
I Aktionensind Wertewie alle anderen.
I DadurchDefinitionvon Kontrollstrukturenmöglich.
I Endlosschleife:
f o r e v e r :: IO α→ IO α f o r e v e r a = a f o r e v e r a
I Iteration (feste Anzahl):
f o r N :: I n t→ IO α→ IO ( )
f o r N n a | n == 0 = r e t u r n ( )
| o t h e r w i s e = a f o r N ( n−1) a
I VordefinierteKontrollstrukturen (Control.Monad):
I when,mapM,forM,sequence, . . .
Fehlerbehandlung
I Fehlerwerden durch IOError repräsentiert
I FehlerbehandlungdurchAusnahmen (ähnlich Java) i o E r r o r :: I O E r r o r → IO α −−"throw"
c a t c h :: IO α→ ( I O E r r o r→ IO α) → IO α
I Fehlerbehandlungnur in Aktionen
Fehler fangen und behandeln
I Fehlerbehandlung fürwc:
wc2 :: S t r i n g→ IO ( ) wc2 f i l e =
c a t c h ( wc f i l e )
(λe→ p u t S t r L n $ " F e h l e r : ␣ "++ show e )
I IOErrorkann analysiert werden (siehe Modul IO)
I readmit Ausnahme bei Fehler (statt Programmabbruch):
r e a d I O :: Read a⇒ S t r i n g→ IO a
So ein Zufall!
I Zufallswerte:
randomRIO :: (α, α)→ IO α
I Warum istrandomIOAktion?
I Beispiel:Aktionen zufällig oft ausführen a t m o s t :: I n t→ IO α→ IO [α] a t m o s t most a =
do l← randomRIO ( 1 , most ) s e q u e n c e ( r e p l i c a t e l a )
I Zufälligen String erzeugen r a n d o m S t r :: IO S t r i n g
r a n d o m S t r = a t m o s t 40 ( randomRIO ( ’ a ’ , ’ z ’ ) )
So ein Zufall!
I Zufallswerte:
randomRIO :: (α, α)→ IO α
I Warum istrandomIOAktion?
I Beispiel:Aktionen zufällig oft ausführen a t m o s t :: I n t→ IO α→ IO [α] a t m o s t most a =
do l← randomRIO ( 1 , most ) s e q u e n c e ( r e p l i c a t e l a )
I Zufälligen String erzeugen r a n d o m S t r :: IO S t r i n g
r a n d o m S t r = a t m o s t 40 ( randomRIO ( ’ a ’ , ’ z ’ ) )
Ausführbare Programme
I Eigenständiges Programm istAktionen
I Hauptaktion:main in ModulMain
I wcals eigenständiges Programm:
module Main where
import System . E n v i r o n m e n t ( g e t A r g s ) import Data . Char ( i s S p a c e )
main = do
a r g s ← g e t A r g s mapM wc2 a r g s
Funktionen mit Zustand
Theorem (Currying)
Folgende Typen sindisomorph:
A×B→C ∼=A→B→C
I In Haskell: folgende Funktionen sindinvers:
c u r r y :: ( (α, β) → γ)→ α→ β→ γ u n c u r r y :: (α→ β→ γ)→ (α, β) → γ
Funktionen mit Zustand
I Idee: Seiteneffektexplizit machen
I Funktionf :A→B mit Seiteneffekt in ZustandS:
f :A×S →B×S
∼=
f :A→S →B×S
I Datentyp:S →B×S
I Komposition: Funktionskomposition unduncurry
In Haskell: Zustände explizit
I Datentyp: Berechnung mit Seiteneffekt in TypΣ:
type S t a t e Σ α = Σ→ (α, Σ)
I Komposition zweier solcher Berechnungen:
comp :: S t a t e Σ α→ (α→ S t a t e Σ β)→ S t a t e Σ β comp f g = u n c u r r y g ◦ f
I Lifting:
l i f t :: α→ S t a t e Σ α l i f t = c u r r y i d
Beispiel: Ein Zähler
I Datentyp:
type W i t h C o u n t e r α = S t a t e I n t α
I Zähler erhöhen:
t i c k :: W i t h C o u n t e r ( ) t i c k i = ( ( ) , i+1 )
I Zähler auslesen:
r e a d :: W i t h C o u n t e r I n t r e a d i = ( i , i )
I Zähler zurücksetzen:
r e s e t :: W i t h C o u n t e r ( ) r e s e t i = ( ( ) , 0 )
Implizite vs. explizite Zustände
I Nachteil: Zustand istexplizit
I Kanndupliziertwerden
I Daher: Zustandimplizit machen
I Datentypverkapseln
I Signatur State , comp, lift, elementare Operationen
Aktionen als Zustandstransformationen
I Idee: Aktionen sindTransformationen auf Systemzustand S
I S beinhaltet
I Speicher als AbbildungA*V (AdressenA, WerteV)
I Zustand des Dateisystems
I Zustand des Zufallsgenerators
I In Haskell: TypRealWorld
I “Virtueller” Typ, Zugriff nur über elementare Operationen
I Entscheidend nurReihenfolgeder Aktionen
Zusammenfassung
I Ein/Ausgabe in Haskell durchAktionen
I Aktionen(Typ IOα) sind seiteneffektbehaftete Funktionen
I Kompositionvon Aktionen durch
(=) :: IO α→ (α→ IO β)→ IO β
r e t u r n :: α→ IO α
I do-Notation
I Fehlerbehandlung durch Ausnahmen (IOError,catch).
I Verschiedene Funktionen der Standardbücherei:
I Prelude: getLine,putStr, putStrLn, readFile, writeFile
I Module:IO,Random
I Aktionen sindimplementiertalsZustandstransformationen