9. Lösungen
Hier finden Sie Lösungen zu den Aufgaben des vorherigen Kapitels.
9.2 Übungen zu Turingmaschinen
Implementieren Sie auf turingmachinesimulator.com eine Einband-Turingmaschine, die diese Sprache entscheidet.
Zeichnen Sie einen endlichen Automaten für die gleiche Sprache. Schreiben Sie eine reguläre Grammatik (gerne ernstmal erweitert reguläre) und einen regulären Ausdruck.Nun lässt sich die Grammatik einfach in einen endlichen Automaten übersetzen, der auch noch deterministisch ist:
Das ist auch gleich unsere Turingmaschine. Wir haben fünf Zustände und gehen in jedem Schritt nach rechts. Nur dass wir für eine Turingmaschine noch den akzeptierenden Zustand separieren müssen.
name: durch 5 teilbar?
init: 0
accept: accept
0, a
1, a, >
1, a
2, a, >
2, a
3, a, >
3, a
4, a, >
4, a
0, a, >
0, _
accept, _, -
Implementieren Sie auf turingmachinesimulator.com eine Zweiband-Turingmaschine, die diese Sprache entscheidet.
go
gehen wir nach rechts, bis das \(a\) zu Ende
gelesen worden ist; dann spulen wir wieder zurück.
Akzeptieren tun wir, wenn wir gleichzeitig oben und unten ein \(\square\) lesen;
lesen wir oben ein \(\square\) aber unten nicht, so haben wir insgesamt
oben kein Vielfaches von \(k\) gelesen. In diesem Falle lehnen wir ab.
Sonderfälle sind beispielsweise, wenn \(n = 0\), also oben gar kein \(b\) steht.
name: durch k teilbar?
init: copy
accept: accept
copy, a, _
copy, a, a, >, >
copy, b, _
rewind, b, _, -, <
copy, _, _
accept, _, _, -, -
rewind, b, a
rewind, b, a, -, <
rewind, b, _
go, b, _, -, >
go, b, a
go, b, a, >, >
go, _, _
accept, _, _, -, -
go, b, _
rewind, b, _, -, <
Probieren Sie es aus! Probieren Sie insbesondere \(k=0\) aus! Da versucht die Turingmaschine also, durch 0 zu dividieren. Was hat das zur Folge?
Allerdings verrate ich Ihnen was: die Turingmaschine ist read only; sie schreibt immer nur das gelesene Zeichen wieder hin. Das sollte es doch irgendwie sehr beschränken, was sie machen kann. Experimentieren Sie auf turingmachinesimulator.com mit dieser Maschine und versuchen Sie, rauszufinden, welche Sprache sie akzeptiert.// macht seltsames Zeug über dem Alphabet a,b
name: read-only
init: q0
accept: accept
q0, a
q3, a, >
q0, b
q1, b, >
q1, a
q0, a, >
q1, b
q3, b, <
q2, a
q2, a, >
q2, b
q3, b, <
q3, a
q1, a, <
q3, b
q2, b, <
q0, _
accept, _, -
q1, _
accept, _, -
q2, _
accept, _, -
q3, _
accept, _, -
9.3 Kontextfreie Sprachen
- Führen Sie neue Nichtterminale ein, um zu erzwingen, dass alle rechte Seiten höchstens Länge 2 haben.
- Welche Nichtterminale können \(\epsilon\) erzeugen, also \(X \Step{}^* \epsilon\)?
- Eliminieren Sie die Regeln der Form \(X \rightarrow \epsilon\), indem Sie auf den rechten Seiten \(X\) optional weglassen.
- Eliminieren Sie Kettenregeln der Form \(A \rightarrow C\), indem Sie diese "trivialen Produktionen" überspringen, also beispielsweise bei \(X \rightarrow Y\) und \(Y \rightarrow uv\) die Regel \(X \rightarrow uv\) hinzufügen.
- Erlauben Sie Regeln der Form \(U \rightarrow xy\). In der Chomsky-Normalform müssten Sie strenggenommen Hilfs-Nichtterminale \(X\) und \(Y\) einführen. Ersparen Sie sich diesen Schritt.
- Zeichnen Sie einen Ableitungsbaum des Wortes \(sababb\).
- Berechnen Sie die Mengen \(\First_1\) und \(\First_2\) für jedes Nichtterminal. Die Definition können Sie in Kapitel 5.4 nachlesen.
- Ist die Grammatik \(LL(2)\)? Können Sie beim Links-Parsen also immer anhand der nächsten zwei Zeichen erkennen, welche Regel anzuwenden ist?
-
Zeigen Sie, wie ein \(LL(2)\)-Parser das Wort \(sababb\) abzuleiten versucht (oder an
welcher
Stelle er scheitert, falls die Grammatik nicht \(LL(2)\) sein sollte). Ich gebe Ihnen
Starthilfe.
Der erste Schritt ist recht klar:
\begin{align*}
S & \Step{} sAB \Step{}^* \dots \Step{}^* sababb
\end{align*}
Ihr Linksparser hat nun also \(sAB\) auf dem Stack liegen, matcht das oberste
Stacksymbol (das Terminal \(s\))
mit dem nächsten (also ersten) Inputsymbol. Es bleibt \(AB\) auf dem Stack. Die
Restaufgabe ist also, eine
die Ableitung
\begin{align*}
AB \Step{}^* ababb
\end{align*}
zu finden. Der Parser überlegt nun, welche der zwei Produktionen für \(A\) er anwenden
soll:
\begin{align*}
\begin{array}{rcl}
AB \Step{} & aBSB & \Step{}^* \dots \Step{}^* ababb \quad \textnormal{ oder aber }\\
AB \Step{} & B & \Step{}^* \dots \Step{}^* ababb
\end{array}
\end{align*}
Sie müssen nun mit Hilfe Ihrer bereits berechneten Mengen \(\First_2\) die Mengen
\begin{align*}
& \First_2(aBSB) \quad \textnormal{ und}\\
& \First_2(B)
\end{align*}
berechnen und schauen, welche davon \(\First_2(ababb)\) enthält. Wenn beide es
enthalten, dann
schlecht. Weil sich der Parser nicht entscheiden kann. Allerdings haben Sie in diesem
Fall einen Beweis in der
Hand, dass die Grammatik nicht \(LL(2)\) ist und können mir die Schuld zuschieben.
Fahren Sie dann nach diesem Parser-Schritt entsprechend fort.
- Zeichnen Sie einen Ableitungsbaum des Wortes \(babsbs\).
- Zeichnen Sie den nichtdeterministischen DK-Automaten und machen Sie den dann deterministisch. Oder zeichnen Sie gleich den deterministischen. Sie können die Details in Kapitel 5.5 und Kapitel 5.6 nachlesen.
- Ist es eine \(LR\)-Grammatik? Das können Sie dem DK-Automaten direkt ablesen.
- Zeigen Sie, wie ein Rechtsparser vorgeht, wenn er das Wort \(babsbs\) abeiten will. Oder zeigen Sie, dass es zu einem Zeitpunkt mehrere gültige Schritte gibt. Dann wüssten Sie, dass es keine \(LR\)-Grammatik ist.
9.4 Übungen zu regulären Sprachen
- Schreiben Sie einen regulären Ausdruck für \(L\).
- Schreiben Sie eine reguläre Grammatik für \(L\).
- Formen Sie die reguläre Grammatik in einen deterministischen endlichen Automaten um.
- Schreiben Sie nun eine reguläre Grammatik für \(\bar{L}\), die Sprache aller Wörter, die \(abaab\) nicht enthalten.
- Schreiben Sie für \(\bar{L}\) einen regulären Ausdruck.
Zeigen Sie, dass die Sprache auch nicht kontextfrei ist. Benutzen Sie hierfür das Pumping-Lemma aus Kapitel 5.10.