5.10 Die Grenzen kontextfreier Sprachen
Im letzten Teilkapitel haben wir den CYK-Algorithmus kennengelernt. Dieser verwendet das Prinzip des Dynamic Programming, um für eine allgemeine kontextfreie Grammatik \(G\) und ein Eingabewort \(\gamma\) einen Ableitungsbaum zu finden (oder festzustellen, dass es keinen gibt und somit \(\gamma \not \in L(G)\)).
In diesem Teilkapitel werden wir sehen, dass viele Sprachen gar nicht kontextfrei sind, und werden hoffentlich ein Gefühl dafür entwickeln, welche Konstruktionen Kontextfreiheit verhindern, ähnlich wie "Verschachtelung" eine Sprache nicht-regulär macht.
Ein Kellerautomat könnte nun alle \(a\) auf den Stack legen und dann, wenn die \(b\) beginnen, den Stapel abarbeiten. Allerdings hätte er dann, wenn die \(c\) kommen, vergessen, wieviele \(a\) es denn waren. Irgendwie scheinen Kellerautomaten nicht für \(L\) geeignet. Dies ist natürlich nur eine Intuition. Vielleicht kennen wir einfach nicht alle Tricks, die man mit Kellerautomaten (und somit mit kontextfreien Grammatiken) anstellen kann.
Wir werden nun beweisen, dass \(L\) nicht kontextfrei ist; dass es also für \(L\) keine kontextfreie Grammatik gibt. Wir gehen wie folgt vor: wir nehmen eine kontextfreie Grammatik \(G\), die jedes Wort \(\gamma \in L\) ableiten kann. Dann sehen wir uns den Ableitungsbaum für ein "sehr langes" \(\gamma\) an und werden sehen, dass wir in \(G\) andere Wörter \(\gamma'\) ableiten können, die nicht in \(L\) sind. Somit haben wir gezeigt, dass \(G\) keine korrekte Grammatik für \(L\) ist. Als erstes verlangen wir, dass \(G\) in Chomsky-Normalform vorliegt. Dann schauen wir uns für ein \(\gamma = a^n b^n c^n\) den Ableitungsbaum an:
Nun holen wir zum entscheidenden Schlag aus: wir "pumpen" das Wort \(\gamma\) auf, indem
wir die Teilbäume mit \(Z\) an der Wurzel wiederholen. Konkret besagt ja oberer Baum,
dass \(\gamma\) wie folgt hergeleitet wurde:
\begin{align*}
S \Step{}^* u Z v \Step{}^* uvZxy \Step{}^* uvwxy
\end{align*}
Insbesondere heißt das, dass \(Z \Step{}^* vZx\) gilt. Wir können diesen
Schritt also wiederholen:
Wir haben also gezeigt, dass die kontextfreie Grammatik \(G\) nicht die Sprache \(L\) erzeugt: wenn wir alle \(\gamma \in L\) erzeugen kann, dann kann sie auch Wörter wie \(uwy \not \in L\) erzeugen. Da unsere Argumentation keine Annahmen über \(G\) getroffen hat, außer, dass sie kontextfrei ist, haben wir gezeigt: \(L\) ist keine kontextfreie Sprache.
Vorsicht! Wir haben angenommen, das \(uwy\) weniger Zeichen hat als \(uvwxy\), weil ja \(v\) und \(x\) fehlen. Kann aber \(v = x = \epsilon\) eintreten? Dies würde unsere Argumentation zerstören. Hier kommt wieder die Chomsky-Normalform zur Hilfe: es gibt keine Produktionen \(X \rightarrow \epsilon\), (außer vielleicht für das Startsymbol \(S\), welches dann aber nicht auf einer rechten Seite auftreten dürfte). Ganz allgemein: wenn eine Grammatik \(G\) in Chomsky-Normalform ein Wort \(\gamma\) herleitet und \(\gamma \ne \epsilon\), dann kommt in der Ableitung keine \(\epsilon\)-Produktion zur Anwendung.
Die obige Argumentationslinie ist als Pumping Lemma für kontextfreie Sprachen bekannt:
Mengenoperationen auf kontextfreien Sprachen
Im Kapitel über reguläre Sprachen hatten wir folgende Abschlusseigenschaften:- \(L_1 \cup L_2\). Das war einfach: wir kombinieren die Grammatiken und fügen die Startproduktionen \(S \rightarrow S_1 \ | \ S_2\) hinzu.
- \(L_1 \circ L_2\). Das war schon schwieriger, ging aber auch irgendwie.
- \(L_1^*\). Das ging ähnlich zum vorherigen.
- \(\bar{L}_1 := \Sigma^* \setminus L_1\), die Komplementsprache. Das ist einfach, sobald man einen endlichen Automaten für \(L_1\) gebaut hat: man tauscht akzeptierende und nicht-akzeptierende Zustände aus.
- \(L_1^R\), die Sprache, die entsteht, wenn man jedes Wort in \(L_1\) von rechts nach links liest. Dies ist gar nicht so offensichtlich, wenn man reguläre Grammatiken oder endliche Automaten ansieht. Wenn man aber für \(L_1\) einen regulären Ausdruck gebaut hat, wird es zur Trivialität: einfach den Ausdruck umdrehen.
- \(L_1 \cap L_2\). Hierfür könnte man für \(L_1, L_2\) jeweils endliche Automaten
\(M_1, M_2\) bauen und die dann "parallel" laufen lassen; man muss nun sehen, dass man
dieses parallele Laufen
mit einem Automaten simulieren kann: dieser hat als Zustandsmenge \(Q_1 \times
Q_2\),
merkt sich also in einem Zustand, in welchen Zuständen \(M_1\) und \(M_2\)
sind.
Oder man macht es sich einfach:
- Nach Punkt 4 sind \(\bar{L}_1\) und \(\bar{L}_2\) regulär.
- Nach Punkt 1 daher auch \(\bar{L}_1 \cup \bar{L}_2\) ist regulär.
- Nach Punkt 4 ist daher wiederum \(\overline{\bar{L}_1 \cup \bar{L}_2}\) regulär, was nach den De-Morganschen Gesetzen die gleiche Menge ist wie \(L_1 \cap L_2\).
- \(L_1 \cup L_2\)
- \(L_1 \circ L_2\)
- \(L_1^*\)
- Überspringen wir \(\bar{L}_1\); dieses ist nämlich im Allgemeinen nicht kontextfrei.
- \(L_1^R\)
- Überspringen wir \(L_1 \cap L_2\); dieses ist nämlich im Allgemeinen nicht kontextfrei.
Tip: dies ist viel einacher als für reguläre Grammatiken.
Tip. Listen Sie alle Möglichkeiten auf, wie ein Wort \(w\) nicht in \(L\) sein kann: (1) es hat nicht die Form \(a^* b^* c^*\); (2) es hat die Form, hat aber mehr \(a\) als es \(b\) hat; (2) es hat mehr \(a\) als es \(c\) hat...