Deklarative Programmierung
Deklarative Programmierung
Deklarative Programmierung
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
Was bisher geschah<br />
◮ Algebraische Datentypen<br />
◮ Typklassen und Instanzen<br />
◮ strukturelle Induktion<br />
◮ Rekursionsschemata<br />
◮ Funktionen höherer Ordnung<br />
◮ Monaden<br />
103
Wiederholung Monaden<br />
◮ Datentyp mit Grundoperationen<br />
class Monad m where<br />
return :: a -> m a<br />
( >>= ) :: m a -> (a -> m b) -> m b<br />
◮ Axiome (Monaden-Gesetze)<br />
(return x) >>= f == f x<br />
m >= return x == m<br />
( m >>= f) >>= g == m >>= (\ x -> f x >>= g)<br />
104
Beispiele<br />
◮ Listen (Nichtdeterminismus)<br />
instance Monad [] where<br />
return = \ x -> [x]<br />
m >>= f = concat ( map f m )<br />
◮ Maybe<br />
data Maybe a = Nothing | Just a<br />
instance Monad Maybe where<br />
return = \ x -> Just x<br />
m >>= f = case m of<br />
Nothing -> Nothing<br />
Just x -> f x<br />
◮ IO<br />
data World = ...<br />
data IO = World -> World<br />
data IO a = IO { World -> (a, World) }<br />
Anwendung:<br />
do cs
Übersetzer für Programme<br />
Höhere Programmiersprachen (z.B. Java, Haskell, C) erfordern<br />
Übersetzung von Quell- in Maschinen- oder Byte-Code<br />
Beispiel: Übersetzung von Java-Programmen<br />
Quellcode<br />
↓<br />
Zwischendarstellung<br />
↓<br />
Java-Bytecode<br />
Übersetzung in zwei Phasen:<br />
1. Analyse-Phase (Front-End):<br />
Transformation des Quellcodes in eine<br />
Zwischendarstellung<br />
2. Synthese-Phase (Back-End):<br />
Transformation der Zwischendarstellung in Java-Bytecode<br />
106
Analyse-Phase<br />
Quellcode Scanner −→<br />
Folge von Token Parser −→<br />
Syntaxbaum<br />
lexikalische Analyse (Scanner)<br />
lineare Analyse des Quelltextes, Aufteilung in<br />
Einheiten (Token)<br />
z.B. Schlüsselwörter, Bezeichner, Zahlen<br />
reguläre Sprachen, endliche Automaten<br />
syntaktische Analyse (Parser)<br />
hierarchische Struktur des Quelltextes<br />
z.B. Ausdrücke, Verzweigungen, Schleifen<br />
kontextfreie Sprachen, Kellerautomaten<br />
semantische Analyse Annotationen im Syntaxbaum,<br />
z.B. Typprüfungen<br />
107
Einsatz ähnlicher Methoden<br />
◮ Übersetzung von Daten zwischen verschiedenen Formaten<br />
◮ Verarbeitung von Domain-spezifischen Sprachen<br />
◮ Textformatierung<br />
◮ kontextabhängige Hilfe in Entwicklungsumgebungen<br />
◮ statische Analyse zur Fehlersuche in Programmen<br />
◮ Interpreter<br />
◮ graphische Editoren (z.B. für UML-Diagramme) mit<br />
Programmerzeugung<br />
108
Parser<br />
Eingabe:<br />
Ausgabe:<br />
Beispiele:<br />
String (Liste von Char)<br />
Syntaxbaum o.Ä.<br />
◮ arithmetische und logische Ausdrücke<br />
◮ HTML-Code im Web-Browser<br />
◮ Haskell-Code in GHC / GHCI<br />
type Parser = String -> Tree<br />
parse :: Parser<br />
i.A. nur teilweise Verarbeitung der Eingabe<br />
type Parser = String -> (Tree, String)<br />
Ergebnis:<br />
◮ Syntaxbaum o.Ä. des verarbeiteten Teiles der Eingabe<br />
◮ unverarbeiteter Teil der Eingabe<br />
109
Nichtdeterminismus<br />
Behandlung (temporär) uneindeutiger Ableitungen<br />
mehrdeutige Grammatiken, z.B. verschiedene Ableitungen für<br />
5 − 3 − 2<br />
E ::= n | E − E<br />
mit n ∈ N<br />
Nichtdeterministische Ausgabe (Ergebnisliste)<br />
type Parser = String -> [(Tree, String)]<br />
Ergebnis:<br />
Liste möglicher Syntaxbäume<br />
◮ []: Fehler<br />
◮ [ x ]: eindeutig interpretierbar (Erfolg)<br />
◮ [ x, y, ..<br />
]: mögliche Interpretationen<br />
110
Typabstraktion<br />
Verschiedene Typen von „Eingaben“ c und „Syntaxbäumen“ a<br />
data Parser c a = Parser ( [c] -> [(a, [c])] )<br />
parse :: Parser c a -> [c] -> [(a, [c])]<br />
parse ( Parser f ) s = f s<br />
(oft Char für c)<br />
A Parser for Things<br />
is a functions from Strings<br />
to Lists of Pairs<br />
of Things and Strings!<br />
111
Parser<br />
◮ Elementare Parser<br />
◮ Operationen zur Kombination von Parsern:<br />
◮ sequentielle Kombination<br />
◮ parallele Kombination (Auswahl)<br />
◮ Iteration<br />
112
Elementare Parser<br />
◮ return (immer erfolgreich und eindeutig)<br />
return :: a -> Parser c a<br />
return v = Parser $ \x -> [(v, x)]<br />
◮ reject (nie erfolgreich)<br />
reject :: Parser c a<br />
reject = Parser $ \_ -> []<br />
◮ item (vearbeitet das erste Symbol der Eingabe)<br />
item :: Parser c c<br />
item = Parser $ \x -> case x of<br />
[] -> []<br />
(x : xs) -> [( x, xs )]<br />
113
Anwendung<br />
parse :: Parser String a -> String -> [(a, String )]<br />
parse p ein = p ein<br />
Beispiele:<br />
◮ parse (return 1) "abc"<br />
◮ parse reject "abc"<br />
◮ parse item "abc"<br />
◮ parse item ""<br />
114
Test des ersten Symbols<br />
◮ satisfy (akzeptiert das erste Symbol, falls es die<br />
Bedingung pred erfüllt)<br />
satisfy :: (c -> Bool) -> Parser c c<br />
satisfy pred = do<br />
x c -> Parser c c<br />
expect c = satisfy ( == c )<br />
Beispiele:<br />
◮ parse (expect ’a’) "abc"<br />
◮ parse (expect ’a’) ""<br />
◮ parse (expect ’b’) "abc"<br />
◮ parse (satisfy isDigit) "1a4"<br />
115
Sequentielle Verknüpfung<br />
Verkettung von Sprachen: L ◦ L ′ = {uv | u ∈ L ∧ v ∈ L ′ }<br />
seq :: Parser c a -> ( a -> Parser c b) -> Parser c b<br />
kennen wir schon als bind-Operation für Monaden<br />
>>= :: Parser c a -> ( a -> Parser c b) -> Parser c b<br />
p >>= f = Parser $ \ s -> do<br />
( v, t ) >= ( \v1 -> p2 >>= \v2 -> ...<br />
(\vn -> return (f v1 v2 ... vn))...)<br />
oder in do-Notation:<br />
do v1
parse (parens item) "(r)"<br />
parse (parens p) "(abcd)" 117<br />
Beispiele<br />
p :: Parser Char (Char, Char)<br />
p = do x
Parallele Verknüpfung<br />
Vereinigung von Sprachen: L ∪ L ′<br />
() :: Parser c a -> Parser c a -> Parser c a<br />
p q = Parser $ \ s -><br />
( parse p s ) ++ ( parse q s )<br />
Beispiel:<br />
s :: Parser Char ()<br />
s = do { expect ’a’ ; s ; expect ’b’ ; s }<br />
return ()<br />
parse (do s ; eof) "abab"<br />
118
Iteration<br />
bekannt aus LV Theoretische Informatik:<br />
L ∗ = {ε} ∪ L +<br />
L + = L ◦ L ∗<br />
many :: Parser c a -> Parser c [a]<br />
many p = many1 p return []<br />
many1 :: Parser c a -> Parser c [a]<br />
many1 p = do<br />
x
Akzeptanz formaler Sprachen<br />
s :: Parser Char ()<br />
akzeptiert alle von der Grammatik mit den Regeln<br />
S → aSbS | ε<br />
erzeugten Wörter<br />
s = do { expect ’a’ ; s ; expect ’b’ ; s }<br />
return ()<br />
parse (do s ; eof) "abaabb"<br />
120
Arithmetische Ausdrücke<br />
Grammatik (mit Operator-Präferenzen):<br />
E ::= P(+P) ∗<br />
P ::= F (∗F ) ∗<br />
F ::= (E) | nat<br />
mit Parsec-Bibliothek ( import Text.Parsec)<br />
summe :: Parsec String () Integer<br />
summe = do<br />
xs