archetype | title | linkTitle | author | readings | outcomes | attachments | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
lecture-bc |
Reguläre Sprachen, Ausdrucksstärke |
Reguläre Sprachen |
BC George (HSBI) |
|
|
|
:::notes Hier entsteht ein Tafelbild. :::
- Endliche Automaten
- Reguläre Ausdrücke
Def.: Ein Alphabet
Def.: Ein Wort
Def.:
Def.: Seien
Def.: Eine Sprache
Def.: Ein deterministischer endlicher Automat (DFA) ist ein 5-Tupel
-
$Q$ : eine endliche Menge von Zuständen -
$\Sigma$ : ein Alphabet von Eingabesymbolen -
$\delta$ : die Übergangsfunktion$(Q \times \Sigma) \rightarrow Q, \delta$ kann partiell sein -
$q_0 \in Q$ : der Startzustand -
$F \subseteq Q$ : die Menge der Endzustände
Def.: Wir definieren
- Basis:
$\delta^{\ast}(q, \epsilon) = q\ \forall q \in Q$ - Induktion:
$\delta^{\ast}(q, a_1, \ldots, a_n) = \delta(\delta^{\ast}(q, a_1, \ldots , a_{n-1}), a_n)$
Def.: Ein DFA akzeptiert ein Wort
Def.: Die Sprache eines DFA
:::notes Hier entsteht ein Tafelbild. :::
Def.: Ein nichtdeterministischer endlicher Automat (NFA) ist ein 5-Tupel
-
$Q$ : eine endliche Menge von Zuständen -
$\Sigma$ : ein Alphabet von Eingabesymbolen -
$\delta$ : die Übergangsfunktion$(Q \times \Sigma) \rightarrow \mathcal{P}(Q)$ -
$q_0 \in Q$ : der Startzustand -
$F \subseteq Q$ : die Menge der Endzustände
Def.: Wir definieren
-
Basis:
$\delta^{\ast}(q, \epsilon) = q\ \forall q \in Q$ -
Induktion: Sei
$w \in \Sigma^{\ast}, w = xa, x \in \Sigma^{\ast}, a \in \Sigma$ mit$\delta^{\ast}(q, x) = \lbrace p_1,\ \ldots,\ p_k \rbrace, p_i \in Q$ , sei$A = \bigcup\limits_{i = 1}^k \delta(p_i, a) = \lbrace r_1, \ldots r_m \rbrace, r_j \in Q$ .Dann ist
$\delta^{\ast}(q, w) = \lbrace r_1,\ \ldots\ , r_m\rbrace$ .
Pattern Matching geht mit NFAs.
NFAs sind so nicht zu programmieren, aber:
Satz: Eine Sprache
Gegeben: Ein NFA
Wir konstruieren einen DFA
\bigskip
a | b | |
---|---|---|
*$q_2$ | - |
|
a | b |
---|---|---|
|
||
*$\lbrace q_1 q_2\rbrace$ | ||
*$\lbrace q_2\rbrace$ | - | |
*$\lbrace q_0, q_2\rbrace$ | ||
*$\lbrace q_0, q_1, q_2\rbrace$ |
Ist ist der DFA
Dann wird eine Matrix generiert, die für alle Zustandspaare sagt, ob die beiden Zustände zu einem verschmelzen können.
Def.: Seien L und M Sprachen.
$L \cup M = \lbrace w \mid w \in L \vee w \in M \rbrace$ $LM = L \cdot M = L \circ M = \lbrace vw \mid v \in L \land w \in M\rbrace$ - Die Kleene-Hülle einer Sprache:
- Basis:
$L^0 = \lbrace \epsilon\rbrace$ - Induktion:
$L^i = \lbrace xw\mid x \in L^{i-1}, w \in L, i > 0\rbrace$ , \newline$L^{\ast} = \bigcup\limits_{i \ge 0}L^i$ , \newline$L^+ = \bigcup\limits_{i > 0}L^i$
- Basis:
Def.: Induktive Definition von regulären Ausdrücken (regex) und der von ihnen repräsentierten Sprache:
- Basis:
-
$\epsilon$ und$\emptyset$ sind reguläre Ausdrücke mit$L(\epsilon) = \lbrace \epsilon\rbrace$ ,$L(\emptyset)=\emptyset$ - Sei
$a$ ein Symbol$\Rightarrow$ $a$ ist ein regex mit$L(a) = \lbrace a\rbrace$
-
- Induktion: Seien
$E,\ F$ reguläre Ausdrücke. Dann gilt:-
$E+F$ ist ein regex und bezeichnet die Vereinigung$L(E + F) = L(E)\cup L(F)$ -
$EF$ ist ein regex und bezeichnet die Konkatenation$L(EF) = L(E)L(F)$ -
$E^{\ast}$ ist ein regex und bezeichnet die Kleene-Hülle$L(E^{\ast})=(L(E))^{\ast}$ -
$(E)$ ist ein regex mit$L((E)) = L(E)$
-
Vorrangregeln der Operatoren für reguläre Ausdrücke: *, Konkatenation, +
Satz: Sei
Satz: Sei
:::notes Hier entsteht ein Tafelbild. :::
Def.: Eine formale Grammatik ist ein 4-Tupel
-
$N$ : einer endlichen Menge von$Nichtterminalen$ -
T: einer endlichen Menge von Terminalen,
$N \cap T = \emptyset$ -
$S \in N$ : dem Startsymbol -
P: einer endlichen Menge von Produktionen der Form:
$X \rightarrow Y$ mit$X \in (N \cup T)^{\ast} N (N \cup T)^{\ast}, Y \in (N \cup T)^{\ast}$
Def.: Sei
Wir sagen:
Def.: Wir definieren die Relation
- Basis:
$\forall \alpha \in (N \cup T)^{\ast} \alpha \overset{\ast}{\Rightarrow} \alpha$ (Jede Zeichenkette leitet sich selbst ab.) - Induktion: Wenn
$\alpha \overset{\ast}{\Rightarrow} \beta$ und$\beta\Rightarrow \gamma$ dann$\alpha \overset{\ast}{\Rightarrow} \gamma$
Def.: Sei
Def.: Eine reguläre (oder type-3-) Grammatik ist eine formale Grammatik mit den folgenden Einschränkungen:
-
Alle Produktionen sind entweder von der Form
-
$X \to aY$ mit$X \in N, a \in T, Y \in N$ (rechtsreguläre Grammatik) oder -
$X \to Ya$ mit$X \in N, a \in T, Y \in N$ (linksreguläre Grammatik)
-
-
$X\rightarrow\epsilon$ ist in beiden Fällen erlaubt.
Satz: Die von rechtsregulären Grammatiken erzeugten Sprachen sind genau die von linksregulären Grammatiken erzeugten Sprachen. Beide werden reguläre Sprachen genannt.
Satz: Die von regulären Ausdrücken beschriebenen Sprachen sind die regulären Sprachen.
Satz: Das Pumping Lemma für reguläre Sprachen:
Sei
Die Klasse der regulären Sprachen ist abgeschlossen unter
- Vereinigung
- Konkatenation
- Kleene-Stern
- Komplementbildung
- Durchschnitt
Satz: Es ist entscheidbar,
- ob eine gegebene reguläre Sprache leer ist
- ob
$w \in \Sigma^{\ast}$ in einer gegebenen regulären Sprache enthalten ist (Das "Wort-Problem") - ob zwei reguläre Sprachen äquivalent sind
Reguläre Sprachen sind von ihrer Struktur her einfach. Schon Sprachen, in denen etwas "gematcht" werden muss, lassen sich nicht mehr regulär beschreiben, weil z. B. die fixe Anzahl von Zuständen eines DFAs die Erkennung solcher Sprachen verhindert.
Im Compilerbau werden reguläre Ausdrücke benutzt, um die Schlüsselwörter und weitere Symbole der zu erkennenden Sprache anzugeben. Daraus wird mit Hilfe eines Generators, der aus den regulären Ausdrücken DFAs (oder einen großen DFA) macht, der sog. Scanner oder Lexer genannt, generiert. Seine Aufgabe ist es, die Folge von Zeichen in der Quelldatei in eine Folge von sog. Token umzuwandeln. Z. B. wird so aus den Zeichen des Schlüsselwortes while im Programmtext das Token für while gemacht, das in der Syntaxanalyse weiterverarbeitet wird. Die Tokenfolge eines Programms ist ein Wort einer Sprache, die der Parser erkennt. Jedes vom Lexer erkannte Token ist dort also ein terminales Symbol.
Was ist zu beachten:
- Man braucht mindestens eine Liste von Paaren aus regulären Ausdrücken und Tokennamen.
- Neben den Schlüsselwörtern und Symbolen wie (,), *,
$\ldots$ müssen auch Namen für Variablen, Funktionen, Klassen, Methoden,$\ldots$ (sog. Identifier) erkannt werden - Namen haben meist eine gewisse Struktur, die sich mit regulären Ausdrücken beschreiben lassen.
- Erlaubte Token sind in der Grammatik des Parsers beschrieben, d. h. für literale Namen, Strings, Zahlen liefert der Scanner zwei Werte:
- z. B.
<ID, "radius">
,<Integerzahl, 558>
- z. B.
- Kommentare und Strings müssen richtig erkannt werden. (Schachtelungen)
Man kann natürlich auch einen Lexer selbst programmieren, d. h. die DFAs für die regulären Ausdrücke implementieren.
:::notes Hier entsteht ein Tafelbild. :::
- Definition und Aufgaben von Lexern
- DFAs und NFAs
- Reguläre Ausdrücke
- Reguläre Grammatiken
- Zusammenhänge zwischen diesen Mechanismen und Lexern, bzw. Lexergeneratoren
::: slides
Unless otherwise noted, this work is licensed under CC BY-SA 4.0. :::