Deklarative Programmierung

Werbung
Was bisher geschah
Funktionale Programmierung (Haskell):
I
Algebraische Datentypen
I
Typklassen und Instanzen
Standard (z.B. Num, Eq, Ord, Read, Show) oder
selbstdefiniert
I
strukturelle Induktion
I
Rekursionsschemata
I
Funktionen höherer Ordnung
I
Lazy evaluation
I
Monaden
130
Wiederholung Monaden
abstrakter Datentyp mit Grundoperationen
class Monad m where
return :: a -> m a
( >>= ) :: m a -> (a -> m b) -> m b
Axiome (Monaden-Gesetze)
(return x) >>= f == f x
m >>= return x
== m
( m >>= f) >>= g == m >>= (\ x -> f x >>= g)
131
Beispiele
I
I
I
Maybe (kein oder ein Ergebnis)
data Maybe a = Nothing | Just a
instance Monad Maybe where
return = \ x -> Just x
m >>= f = case m of
Nothing -> Nothing
Just x -> f x
Listen (Nichtdeterminismus, mehrere Ergebnisse)
instance Monad [] where
return = \ x -> [x]
m >>= f = concat ( map f m )
IO
data World = ...
data IO = World -> World
data IO a = IO { World -> (a, World) }
Anwendung, z.B.
do cs <- readFile "foo.bar" ; putStrLn cs
132
Übersetzer für Programme
Höhere Programmiersprachen (z.B. Java, Haskell, C) erfordern
Übersetzung von Quell- in Maschinen- oder Byte-Code
Beispiel: Übersetzung von Java-Programmen
Quellcode
↓
Zwischendarstellung (attributierter Syntaxbaum)
↓
Java-Bytecode
Übersetzung in zwei Phasen (oft miteinander verschränkt):
1. Analyse-Phase (Front-End):
Transformation des Quellcodes in eine
Zwischendarstellung
2. Synthese-Phase (Back-End):
Transformation der Zwischendarstellung in
Maschinen- oder Bytecode
133
Analyse-Phase
Quellcode
Scanner
Parser
−→ Folge von Token −→ Syntaxbaum
lexikalische Analyse (Scanner)
lineare Analyse des Quelltextes, Aufteilung in
Einheiten (Token)
z.B. Schlüsselwörter, Bezeichner, Zahlen
reguläre Sprachen, endliche Automaten
syntaktische Analyse (Parser)
hierarchische Struktur des Quelltextes
z.B. Ausdrücke, Verzweigungen, Schleifen
kontextfreie Sprachen, Kellerautomaten
semantische Analyse Annotationen im Syntaxbaum,
z.B. Typprüfungen
134
Anwendung ähnlicher Methoden
I
Übersetzung von Daten zwischen verschiedenen Formaten
I
automatische Code-Generierung
I
Verarbeitung von Domain-spezifischen Sprachen
I
Textformatierung
I
kontextabhängige Hilfe in Entwicklungsumgebungen
I
statische Analyse zur Fehlersuche in Programmen
I
Interpreter
I
graphische Editoren (z.B. für UML-Diagramme) mit
Programmerzeugung
135
Parser
zum Beispiel für
I
arithmetische und logische Ausdrücke
I
HTML-Code im Web-Browser
I
Haskell-Code in GHC / GHCI
type Parser = String -> Tree
parse :: Parser
i.A. nur teilweise Verarbeitung der Eingabe
type Parser = String -> (Tree, String)
Ergebnis:
1. Syntaxbaum (o.Ä.) des verarbeiteten Teiles der Eingabe
2. unverarbeiteter Teil der Eingabe
136
Nichtdeterminismus
Behandlung (temporär) uneindeutiger Ableitungen
mehrdeutige Grammatiken, z.B. verschiedene Ableitungen für
5−3−2
E ::= n | E − E
mit n ∈
N
Nichtdeterministische Ausgabe (Ergebnisliste)
type Parser = String -> [(Tree, String)]
Ergebnis: Liste möglicher Syntaxbäume
I
[]: Fehler
I
[ x ]: eindeutig interpretierbar (Erfolg)
I
[ x, y, ..
]: mögliche Interpretationen
137
Typabstraktion
Idee: verschiedene Typen von
I
„Eingabesymbolen“ c und
I
und „Syntaxbäumen“ a
als Datentyp:
data Parser c a = Parser ( [c] -> [(a, [c])] )
parse :: Parser c a -> [c] -> [(a, [c])]
parse ( Parser f ) s = f s
(oft Char für c)
A Parser for Things
is a functions from Strings
to Lists of Pairs
of Things and Strings!
138
Parser
Konstruktion komplexer Parser durch:
I
I
Elementare Parser
Operationen zur Kombination von Parsern:
I
I
I
sequentielle Kombination
parallele Kombination (Auswahl)
Iteration
Man bemerke die Analogie zu regulären Ausdrücken
E ::= ∅ | ε | a | EE | E + E | E ∗
mit a ∈ A und E ∈ RegExp(A)
139
Elementare Parser
data Parser c a = Parser ( [c] -> [(a, [c])] )
I
return (immer erfolgreich und eindeutig)
return :: a -> Parser c a
return v = Parser $ \x -> [(v, x)]
I
reject (nie erfolgreich)
reject :: Parser c a
reject = Parser $ \_ -> []
I
item (vearbeitet das erste Symbol der Eingabe)
item :: Parser c c
item = Parser $ \x -> case x of
[]
-> []
(x : xs) -> [( x, xs )]
I
eof (nur bei leerer Eingabe erfolgreich)
140
Anwendung
data Parser c a = Parser ( [c] -> [(a, [c])] )
parse :: Parser c a -> [c] -> [(a, [c])]
parse p eingabe = p eingabe
Beispiele:
I
parse (return 1) "abc"
I
parse reject "abc"
I
parse item "abc"
I
parse item ""
141
Test des ersten Symbols
I
satisfy (akzeptiert das erste Symbol, falls es die
Bedingung pred erfüllt)
satisfy :: (c -> Bool) -> Parser c c
satisfy pred = do
x <- item
if pred x then return x else reject
I
expect (akzeptiert das erste Symbol, falls es genau das
erwartete ist)
expect :: Eq c => c -> Parser c c
expect c = satisfy ( == c )
Beispiele:
I
parse (expect ’a’) "abc"
I
parse (expect ’a’) ""
I
parse (expect ’b’) "abc"
I
parse (satisfy isDigit) "1a4"
142
Sequentielle Verknüpfung
Verkettung von Sprachen: L ◦ L0 = {uv | u ∈ L ∧ v ∈ L0 }
seq :: Parser c a -> ( a -> Parser c b) -> Parser c b
kennen wir schon als bind-Operation für Monaden
>>= :: Parser c a -> ( a -> Parser c b) -> Parser c b
p >>= f = Parser $ \ s -> do
( v, t ) <- parse p s
parse (f v) t
Anwendung:
p1 >>= ( \v1 -> p2 >>= \v2 -> ...
(\vn -> return (f v1 v2 ... vn))...)
oder in do-Notation:
do v1 <- p1
v2 <- p2
...
vn <- pn
return (f v1 v2 ... vn)
143
Beispiele
p :: Parser Char (Char, Char)
p = do x <- item
item
y <- item
return (x,y)
parse p "abcde"
parse p "a"
parens :: Parser Char a -> Parser Char a
parens p = do x <- item
expect (’(’)
y <- p
expect (’)’)
return y
parse (parens item) "(r)"
parse (parens p) "(abcd)"
parse (parens p) "(abc)"
144
Parallele Verknüpfung (nichtdeterministisch)
Vereinigung von Sprachen: L ∪ L0
(+++) :: Parser c a -> Parser c a -> Parser c a
p +++ q = Parser $ \ s ->
( parse p s ) ++ ( parse q s )
Beispiel:
s :: Parser Char ()
s =
do { expect ’a’ ; s ; expect ’b’ ; s }
+++ return ()
parse (do s ; eof) "abab"
145
Iteration
bekannt aus LV Theoretische Informatik:
L∗ = {ε} ∪ L+
L+ = L ◦ L∗
many’ :: Parser c a -> Parser c [a]
many’ p = many1’ p +++ return []
many1’ :: Parser c a -> Parser c [a]
many1’ p = do
x <- p
xs <- many’ p
return (x : xs)
Beispiel:
nat’ :: Parser Char Integer
nat’ = do
xs <- many1’ (satisfy isDigit)
return (read xs)
146
Akzeptanz formaler Sprachen
s :: Parser Char ()
akzeptiert alle von der Grammatik mit den Regeln
S → aSbS | ε
erzeugten Wörter
s =
do { expect ’a’ ; s ; expect ’b’ ; s }
+++ return ()
Beispiel:
parse (do s ; eof) "abaabb"
147
Alternative Verknüpfung (deterministisch)
(<|>) :: Parser c a -> Parser c a -> Parser c a
p <|> q = Parser $ \ s -> case ( parse p s ) of
[]
-> ( parse q s )
x : xs -> return x
Determinismus meist gewünscht,
deshalb übliche Definitionen von many und many1:
many :: Parser c a -> Parser c [a]
many p = many1 p <|> return []
many1 :: Parser c a -> Parser c [a]
many1 p = do
x <- p
xs <- many p
return (x : xs)
148
Beispiel: Arithmetische Ausdrücke
Grammatik (mit Operator-Präferenzen):
E
::= P(+P)∗
P
F
::= F (∗F )∗
::= (E) | nat
mit Parsec-Bibliothek ( import Text.Parsec)
expr :: Parsec String () Integer
expr = do
xs <- sepBy1 produkt ( satisfy ( == ’+’ ))
return $ sum xs
produkt = do
xs <- sepBy1 factor ( satisfy ( == ’*’ ))
return $ product xs
factor = parens expr <|> nat
Anwendung: parse expr "egal" "3+4*(5+2)"
149
Beispiel: Prolog-Terme
Beispiele für Prolog-Terme: vater(X), foo, Foo, foo(bar)
Haskell-Datentyp:
data Term = Var String | App String [ Term ]
pterm :: Parsec String () Term
pterm =
do v <- var ; return $ Var v
<|> do f <- fun
( do args <- parens $ sepBy pterm ( string ","
return $ App f args )
<|> ( return $ App f [] )
fun :: Parsec String () String
fun = withFirst isLower
var :: Parsec String () String
var = withFirst isUpper
150
Beispiel: Prolog-Atome
Beispiele für Prolog-Atome:
liest(vater(X), krimis), foo, foo(bar)
Haskell-Datentyp:
data Atom = Atom String [ Term ]
patom :: Parsec String () Atom
patom =
do f <- fun
( do args <- parens $ sepBy pterm ( string "," )
return $ Atom f args )
<|> ( return $ Atom f [] )
151
Beispiel: Prolog-Regeln
Prolog-Regel und -Fakten:
mag(tom, X) :- frau(X), liest (X, krimis).
liest(mimi, krimis).
Haskell-Datentyp:
data Rule = Fact Atom | Rule Atom [ Atom ]
prule :: Parsec String () Rule
prule = do
head <- patom
( do
string ":-"
body <- sepBy patom (string ",")
string "."
return $ Rule head body )
<|> ( do string "."; return $ Fact head )
152
Herunterladen