Aufgaben Logische Programmierung
Aufgaben Logische Programmierung
Aufgaben Logische Programmierung
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
<strong>Aufgaben</strong> <strong>Logische</strong> <strong>Programmierung</strong><br />
Elmar Eder<br />
20. Juni 2013<br />
Aufgabe 1 Programm verwandtschaft.pl erweitern<br />
Dateiname: verwandtschaft.pl<br />
Erweitern Sie das Programm verwandtschaft.pl:<br />
• Datenbank erweitern (weitere Personen)<br />
• Weitere Verwandtschaftsbeziehungen hinzfügen<br />
Aufgabe 2 Anfragen stellen<br />
Dateiname: verwandtschaft.dialog<br />
Konsultieren Sie die ursprüngliche Datei http://www.cosy.sbg.ac.at/~eder/lehre/<strong>Logische</strong>_<br />
<strong>Programmierung</strong>/verwandtschaft.pl in Prolog! Stellen Sie an Prolog Anfragen zur Beantwortung<br />
der folgenden Fragen (ohne das ursprüngliche Programm zu verändern und ohne neue<br />
Klauseln hinzuzufügen):<br />
0. Wessen Kind ist Emil?<br />
1. Wer ist Kind von Dora?<br />
2. Wer ist Kind von wem?<br />
3. Wer ist Kind von sich selbst?<br />
4. Welche zwei Personen sind die Eltern (Vater bzw. Mutter) von welcher dritten Person?<br />
5. Wer ist Großmutter von Gustav?<br />
Fragen Sie Prolog dabei gegebenenfalls mit Strichpunkt „;“ nach allen Antworten! Kopieren<br />
Sie nun Ihren Dialog mit Prolog in eine Datei verwandtschaft.dialog und laden Sie diese<br />
auf https://prolog.cosy.sbg.ac.at/ hoch! Der Anfang der Datei verwandtschaft.dialog<br />
könnte etwa folgendermaßen ausschauen:<br />
?- [verwandtschaft].<br />
% verwandtschaft compiled 0.00 sec, 2,016 bytes<br />
true.<br />
?- kind(emil,P).<br />
P = anton ;<br />
P = berta.<br />
?-<br />
Aufgabe 3 Programm nationalratswahl.pl mit Kommentaren versehen und erweitern<br />
Dateiname: nationalratswahl.pl<br />
1
Bei der österreichischen Nationalratswahl 2008 hat die SPÖ 57 Mandate bekommen, die ÖVP<br />
51, die FPÖ 34, das BZÖ 21 und die Grünen 20. Für die Bildung einer Mehrheitsregierung<br />
musste eine Koalition mit insgesamt mindestens 92 Mandaten gebildet werden. Lassen Sie das<br />
Programm nationalratswahl.pl mit der Anfrage<br />
?- mehrheitsregierung(SPOe,OeVP,FPOe,BZOe,Gruene).<br />
laufen! Lassen Sie sich dabei von Prolog alle Antworten ausgeben, indem Sie nach jeder Antwort<br />
einen Strichpunkt eingeben! Erklären Sie, was die Ausgaben von Prolog in Bezug auf die anschließende<br />
Regierungsbildung bedeuteten! Erklären Sie, was die im Prologprogramm definierten<br />
Prädikate nullodereins/1 und mehrheitsregierung/5 bedeuten! Fügen Sie dem Programm<br />
entsprechende Kommentare mit all diesen Erklärungen hinzu! Ändern Sie das Programm nun so<br />
ab, dass Prolog anstatt 0 und 1 in den Antworten nein bzw. ja ausgibt!<br />
Aufgabe 4 Fortbewegung von Tieren<br />
Dateiname: tiere.pl<br />
Drücken Sie die folgenden Aussagen über verschiedene Tiergruppen und ihre bevorzugten Fortbewegungsmethoden<br />
in Form eines Prologprogramms aus:<br />
1. Löwe, Rind, Wal und Fledermaus sind Säugetiere.<br />
2. Star, Strauß und Wasseramsel sind Vögel.<br />
3. Löwe, Rind und Strauß gehen gerne.<br />
4. Forelle, Wasseramsel und Wal schwimmen gerne.<br />
5. Fledermaus, Star und Wasseramsel fliegen gerne.<br />
Fragen Sie Prolog<br />
1. nach einem Säugetier, das gerne schwimmt<br />
2. nach einem Vogel, der gerne fliegt<br />
3. nach einem Vogel, der gerne fliegt und gerne schwimmt<br />
Schreiben Sie diese Anfragen als Kommentare in das Programm hinein!<br />
Aufgabe 5 Cliquen-Problem<br />
Dateiname: clique.pl<br />
Ein bekanntes schwieriges Problem der Informatik ist das Cliquenproblem. In einer Menge von<br />
Personen gibt es einige Paare von miteinander befreundeten Personen. Eine Clique ist nun eine<br />
Teilmenge von Personen, in der jede Person mit jeder anderen Person befreundet ist. Hier ein<br />
Beispiel:<br />
• Anton und Berta sind miteinander befreundet.<br />
• Anton und Christa sind miteinander befreundet.<br />
• Anton und Dora sind miteinander befreundet.<br />
• Anton und Ernst sind miteinander befreundet.<br />
• Berta und Dora sind miteinander befreundet.<br />
• Berta und Ernst sind miteinander befreundet.<br />
• Berta und Franz sind miteinander befreundet.<br />
• Christa und Dora sind miteinander befreundet.<br />
• Christa und Ernst sind miteinander befreundet.<br />
• Dora und Ernst sind miteinander befreundet.<br />
• Dora und Franz sind miteinander befreundet.<br />
• Ernst und Franz sind miteinander befreundet.<br />
2
Schreiben Sie ein Prolog-Programm, mit dem Sie alle Cliquen von vier Personen finden können!<br />
Das Cliquen-Problem gehört zur Klasse der sogenannten NP-vollständigen Probleme. Für diese<br />
Probleme ist es bis heute keinem Forscher gelungen, einen effizienten Algorithmus anzugeben.<br />
Es wird vermutet, dass es für NP-vollständige Probleme keine effizienten Algorithmen gibt, aber<br />
auch das konnte bisher niemand beweisen. Die Frage, ob es einen effizienten Algorithmus dafür<br />
gibt (P=NP? seit 1971), ist das bekannteste bis heute ungelöste Problem der Theoretischen<br />
Informatik. Für kleine Mengen von Personen ist das Problem mit Prolog leicht lösbar, aber für<br />
große Mengen wird der Rechenaufwand gigantisch.<br />
Aufgabe 6 Nachkomme<br />
Dateiname: nachkomme.pl<br />
Kopieren Sie das ursprüngliche Programm verwandtschaft.pl von meiner Web-Seite in eine<br />
neue Datei nachkomme.pl und fügen Sie noch zwei Fakten hinzu, die besagen, dass Heinz Kind<br />
von Emil ist und dass Inge Kind von Heinz ist.<br />
Der Begriff des Nachkommen ist durch die folgenden Regeln definiert:<br />
1. Jedes Kind einer Person ist Nachkomme dieser Person.<br />
2. Jeder Nachkomme eines Kindes einer Person ist Nachkomme dieser Person.<br />
Erweitern Sie Ihr Prologprogramm um entsprechende Klauseln und testen Sie es mit verschiedenen<br />
Anfragen! Anschließend laden Sie es hoch!<br />
Aufgabe 7 Nuklide<br />
Dateiname: nuklide.pl<br />
Wir haben uns bisher hauptsächlich mit zwei Arten von Prolog-Termen beschäftigt:<br />
• Konstanten. Dazu gehören<br />
– Prolog-Atome. Beispiel: berta<br />
– Zahlen. Beispiel: 3.456<br />
• Variablen. Beispiel: X<br />
In Prolog gibt es aber auch zusammengesetzte Terme, auch Strukturen genannt. Eine Struktur<br />
hat die Form funktor(Term,...,Term), wobei der Funktor ein Prologatom ist, also klein zu<br />
schreiben ist.<br />
• Strukturen. Beispiel: nuklid(kohlenstoff,12)<br />
Mit diesem Prolog-Term können wir z.B. ein Nuklid – dh. eine bestimmte Sorte von Atomkernen<br />
– bezeichnen, hier das Nuklid Kohlenstoff 12. Jedes Nuklid ist eindeutig bestimmt durch Angabe<br />
des chemischen Elements (hier Kohlenstoff) und der Massenzahl (hier 12). Zu einem Element<br />
kann es mehrere verschiedene Nuklide geben, z.B. Kohlenstoff 12 und Kohlenstoff 14. Man sagt<br />
dann, diese Nuklide sind Isotope voneinander.<br />
Schreiben Sie ein Prologprogramm, das nur aus Fakten besteht, die die folgenden Aussagen<br />
wiedergeben.<br />
• Wasserstoff 1 ist stabil.<br />
• Wasserstoff 2 ist stabil.<br />
• Helium 3 ist stabil.<br />
• Helium 4 ist stabil.<br />
• Beryllium 9 ist stabil.<br />
• Kohlenstoff 12 ist stabil.<br />
• Kohlenstoff 13 ist stabil.<br />
3
• Wasserstoff 3 hat eine Halbwertszeit von 3, 9 · 10 8 Sekunden.<br />
• Beryllium 7 hat eine Halbwertszeit von 4, 6 · 10 6 Sekunden.<br />
• Kohlenstoff 11 hat eine Halbwertszeit von 1200 Sekunden.<br />
• Kohlenstoff 14 hat eine Halbwertszeit von 1, 8 · 10 11 Sekunden.<br />
• Uran 234 hat eine Halbwertszeit von 7, 7 · 10 12 Sekunden.<br />
• Uran 235 hat eine Halbwertszeit von 2, 2 · 10 16 Sekunden.<br />
• Uran 237 hat eine Halbwertszeit von 580000 Sekunden.<br />
• Uran 238 hat eine Halbwertszeit von 1, 4 · 10 17 Sekunden.<br />
Ihr Programm sollte sich folgendermaßen verhalten (hier nur jeweils die ersten zwei Antworten,<br />
es gibt aber mehr):<br />
?- stabil(Nuklid).<br />
Nuklid = nuklid(wasserstoff, 1) ;<br />
Nuklid = nuklid(wasserstoff, 2)<br />
?- halbwertszeit(Nuklid,Zeit).<br />
Nuklid = nuklid(wasserstoff, 3),<br />
Zeit = 3.9e+08 ;<br />
Nuklid = nuklid(beryllium, 7),<br />
Zeit = 4.6e+06<br />
Wir wollen nun ein Nuklid kurzlebig nennen, wenn seine Halbwertszeit höchstens 30 Tage beträgt.<br />
Wir wollen es langlebig nennen, wenn es stabil ist oder seine Halbwertszeit mehr als 10 12 Sekunden<br />
beträgt. Ergänzen Sie nun Ihr Programm, indem Sie Prädikate kurzlebig/1, langlebig/1<br />
und isotope/2 definieren. Dabei bedeute kurzlebig(N), dass N ein kurzlebiges Nuklid ist; analog<br />
langlebig(N). Weiter soll isotope(L,S) bedeuten, dass L und S Isotope voneinander sind,<br />
wobei L das leichtere und S das schwerere von den beiden ist. Verwenden Sie in Ihrem Programm<br />
nicht das in Prolog eingebaute Konstrukt „oder“. Stattdessen definieren Sie z.B. das Prädikat<br />
langlebig/1 durch zwei Regeln. Das Programm sollte sich folgendermaßen verhalten:<br />
?- kurzlebig(Nuklid).<br />
Nuklid = nuklid(kohlenstoff, 11) ;<br />
Nuklid = nuklid(uran, 237)<br />
?- langlebig(Nuklid).<br />
Nuklid = nuklid(wasserstoff, 1) ;<br />
Nuklid = nuklid(wasserstoff, 2)<br />
?- isotope(Leichteres,Schwereres).<br />
Leichteres = nuklid(wasserstoff, 1),<br />
Schwereres = nuklid(wasserstoff, 2) ;<br />
Leichteres = nuklid(wasserstoff, 1),<br />
Schwereres = nuklid(wasserstoff, 3)<br />
Aufgabe 8 Bevölkerungsdichte<br />
Dateiname: bevoelkerungsdichte.pl<br />
Die Unifikation (also das Gleichungslösen) in Prolog arbeitet mit rein formalen Ausdrücken.<br />
Dabei wird nichts mit Zahlen gerechnet. Probieren Sie das aus mit der Anfrage<br />
4
?- 2*3 = 1+5.<br />
Wenn man in Prolog mit Zahlen rechnen will, muss man explizit ein Prädikat verwenden, das<br />
arithmetische Terme mit Zahlen auswertet. Die folgenden Prädikate werten beide Seiten einer<br />
Gleichung oder Ungleichung aus: =:= (gleich), =\= (ungleich), < (kleiner als), > (größer als), =<<br />
(kleinergleich), >= (größergleich). is wertet nur die rechte Seite aus. Probieren Sie die Anfragen<br />
?- 2*3 =:= 1+4.<br />
?- 2*3 =:= 1+5.<br />
?- 2*3 =\= 1+4.<br />
?- 2*3 =\= 1+5.<br />
?- X is 1+5.<br />
?- 2*3 is 1+5.<br />
aus. Sie können auch kompliziertere Ausdrücke, auch mit **, sqrt, sin, cos, exp, log, usw.<br />
verwenden. Wenn Ihr primäres Interesse ist, einen arithmetischen Ausdruck auszuwerten (also<br />
auszurechnen, was heraukommt), dann verwenden Sie bitte das Prolog-Prädikat is !<br />
Hier ist eine Tabelle mit den geschätzten Einwohnerzahlen für das Jahr 2005 und den Flächen<br />
einiger Länder (aus Wikipedia):<br />
Land Einwohnerzahl Fläche [km 2 ]<br />
USA 301 140 000 9 629 091<br />
Russland 143 201 600 17 098 242<br />
V.R. China 1 323 324 000 9 596 961<br />
Kanada 33 390 141 9 970 610<br />
Australien 21 050 000 7 682 300<br />
Deutschland 82 689 210 357 022<br />
Österreich 8 189 444 83 858<br />
Schreiben Sie diese Angaben in Form eines Prologprogramms und definieren Sie dazu ein zweistelliges<br />
Prologprädikat bevoelkerungsdichte/2, sodass Sie z.B. die Bevölkerungsdichte von<br />
Österreich mit der Anfrage<br />
?- bevoelkerungsdichte(oesterreich,Bevoelkerungsdichte).<br />
erfragen können. Definieren Sie nun ein Prädikat dichtbevoelkert/1, das genau auf diejenigen<br />
Länder zutrifft, die eine Bevölkerungsdichte von mehr als 80 Einwohnern pro Quadratkilometer<br />
haben! Probieren Sie dieses Prädikat mit einer entsprechenden Anfrage aus!<br />
Aufgabe 9 EL · MAR = EDER<br />
Dateiname: elmareder.pl<br />
Lösen Sie das folgende Rätsel mit Prolog! In der Rechnung<br />
EL · MAR = EDER<br />
ist jeder Buchstabe durch eine Ziffer zu ersetzen – gleiche Buchstaben durch gleiche Ziffern und<br />
verschiedene Buchstaben durch verschiedene Ziffern – so, dass eine wahre Gleichung herauskommt.<br />
Keine der auftretenden Zahlen soll mit der Ziffer 0 anfangen. Z.B. ist A = 3, D = 8,<br />
E = 0, L = 6, M = 1, R = 4 als Lösung nicht erlaubt, obwohl 06 · 134 = 0804 ist, da 06 und<br />
0804 mit der 0 anfangen.<br />
5
Aufgabe 10 Komplexe Zahlen<br />
Dateiname: komplex.pl<br />
In der Mathematik, Physik und Technik werden oft komplexe Zahlen verwendet. Dabei wird<br />
√ −1 mit i (unter Technikern auch mit j) bezeichnet. Dann hat jede komplexe Zahl die Form<br />
A + Bi, wobei A und B reelle Zahlen sind (der Realteil bzw. der Imaginärteil der komplexen<br />
Zahl). In Prolog kann man eine solche komplexe Zahl darstellen als zusammengesetzten Term,<br />
also als Struktur komplex(A,B). Die Zahl 2 + 3i wird dann z.B. dargestellt durch die Struktur<br />
komplex(2,3). Schreiben Sie ein Prologprogramm zur Addition zweier komplexer Zahlen, zur<br />
Multiplikation zweier komplexer Zahlen und zur Berechnung des Betrags einer komplexen Zahl!<br />
Beispiele:<br />
Addition: (2 + 3i) + (10 + 20i) = 12 + 23i<br />
Multiplikation: (2 + 3i) · (10 + 20i) = −40 + 70i<br />
Betrag: |2 + 3i| ≈ 3, 60555<br />
Ihr Programm sollte sich also so verhalten:<br />
?- add(komplex(2,3),komplex(10,20),Summe).<br />
Summe = komplex(12, 23).<br />
?- mal(komplex(2,3),komplex(10,20),Produkt).<br />
Produkt = komplex(-40, 70).<br />
?- betrag(komplex(2,3),Betrag).<br />
Betrag = 3.60555.<br />
Aufgabe 11 Euklidischer Algorithmus<br />
Dateiname: ggT.pl<br />
Probieren Sie zunächst die folgende Anfrage zur Berechnung des Restes von 100 bei der Division<br />
durch 7 aus:<br />
?- X is 100 mod 7.<br />
Der Euklidische Algorithmus zur Berechnung des größten gemeinsamen Teilers (ggT) zweier<br />
natürlicher Zahlen beruht auf der folgenden Idee:<br />
Nehmen wir an, wir wollen z.B. ggT(18,42) berechnen. Jeder gemeinsame Teiler von 18 und<br />
42 muss auch Teiler von 42 − 18, also von 24 sein und damit gemeinsamer Teiler von 18 und<br />
24. Umgekehrt ist jeder gemeinsame Teiler von 18 und 24 auch gemeinsamer Teiler von 18<br />
und 42. Daher ist ggT(18, 42) = ggT(18, 24). Allgemein ist ggT(a, b) = ggT(a, b − a), wenn<br />
b die größere der beiden Zahlen ist. Man kann das gleiche Argument nun wiederholen und<br />
sagen, ggT(18, 24) = ggT(18, 24 − 18), also ggT(18, 24) = ggT(18, 6). Nun ist 18 die größere der<br />
beiden Zahlen und wir rechnen 18 − 6 = 12 und daher ggT(18, 6) = ggT(12, 6). Allgemein gilt<br />
ggT(a, b) = ggT(a − b, b), wenn a > b ist. Wenn man das so weiter führt, erhält man insgesamt<br />
6
die Rechnung<br />
ggT(18, 42) = ggT(18, 24)<br />
= ggT(18, 6)<br />
= ggT(12, 6)<br />
= ggT(6, 6)<br />
= 6.<br />
Das ganze kann man noch etwas effizienter machen, indem man etwa die beiden Substraktionen<br />
42-18=24 und 24-18=6 auf einmal durchführt mittels der Funktion mod (Rest einer<br />
Zahl modulo einer Zahl): 42 mod 18 = 6. Daher ist ggT(18, 42) = ggT(18, 6). Allgemein ist<br />
ggT(a, b) = ggT(a, b mod a). Das bringt uns natürlich nicht weiter, wenn b < a ist. Dann muss<br />
man mit ggT(a, b) = ggT(a mod b, b) vereinfachen. Zum Beispiel ist 18 mod 6 = 0. Also<br />
ggT(18, 42) = ggT(18, 6)<br />
= ggT(0, 6)<br />
= 6.<br />
Da jede Zahl ein Teiler der 0 ist, ist ggT(0, 6) = 6. Man kann sich die Abfrage, ob b < a ist,<br />
ersparen, wenn man die Argumente von ggT jedesmal vertauscht und die folgenden Gleichungen<br />
zur Vereinfachung verwendet:<br />
ggT(0, b) = b<br />
ggT(a, b) = ggT(b mod a, a), wenn a > 0.<br />
Schreiben Sie ein Prolog-Programm, das durch Rekursion den größten gemeinsamen Teiler zweier<br />
beliebiger natürlicher Zahlen mit Hilfe dieser beiden Gleichungen berechnen kann:<br />
?- ggT(18,42,T).<br />
T = 6<br />
Testen Sie Ihr Programm, indem Sie es mit dieser Anfrage und mit ähnlichen Anfragen laufen<br />
lassen! Tun Sie das bitte auch im Trace-Modus, z.B.<br />
?- trace, ggT(18,42,T).<br />
und beobachten Sie dabei die rekursiven Aufrufe von ggT, die Prolog dabei durchführt (Calls<br />
von ggT), und die Fakten über ggT, die Prolog dabei zurückliefert (Exits von ggT)!<br />
Aufgabe 12 Multiplikation natuerlicher Zahlen<br />
Dateiname: multiplikation_nat.pl<br />
Wir haben gesehen, dass man in Prolog natürliche Zahlen auch ohne den eingebauten Datentyp<br />
der Integers darstellen kann mittels einer Konstanten 0 und eines einstelligen Funktors n<br />
für die Nachfolgerfunktion. Man kann z.B. die 3 darstellen durch n(n(n(0))). In der Datei<br />
natuerliche_Zahlen.pl wird ein Prolog-Prädikat nat/1 definiert, das alle natürlichen Zahlen<br />
in dieser Darstellung erzeugen oder testen kann. Lassen Sie das Programm mit der dort angegebenen<br />
Anfrage laufen und fragen Sie Prolog mit „;“ nach immer weiteren Lösungen!<br />
Als nächstes schauen Sie sich die Datei addition_nat.pl an! Dort stehen in einem Kommentar<br />
die beiden Rekursionsgleichungen, mit denen man in der Mathematik die Addition zweier natürlichen<br />
Zahlen definiert. Schauen Sie sich genau an, wie ich diese Gleichungen dann als zwei<br />
7
Prolog-Klauseln umformuliert habe! Lassen Sie das Programm dann laufen mit den in der Datei<br />
angegebenen Anfragen, wobei Sie Prolog ggf. mit „;“ nach weiteren Antworten fragen! Verfolgen<br />
Sie auch mit trace, wie Prolog die Anfragen löst! Sie sehen, dass dieses extrem kurze Programm<br />
sehr vielseitig verwendbar ist. Achtung! Prolog antwortet nicht immer so, wie man vielleicht<br />
zunächst erwarten würde. Warum?<br />
Für die Multiplikation zweier natürlicher Zahlen gibt es, ebenso wie für die Addition, zwei Rekursionsgleichungen:<br />
A · 0 = 0<br />
A · n(B) = A · B + A<br />
Erstellen Sie eine neue Datei multiplikation_nat.pl und kopieren Sie in diese Datei zunächst<br />
die beiden Klauseln der Datei addition_nat.pl! Formulieren Sie nun die beiden Rekursionsgleichungen<br />
für die Multiplikation um als zwei Prolog-Klauseln zur Definition eines Prolog-Prädikats<br />
mul/3 ähnlich, wie ich das zuvor für die Addition gemacht hatte. Verwenden Sie dabei das<br />
Prädikat add/3! Ihr Programm sollte also jetzt vier Klauseln enthalten: die zwei Klauseln aus<br />
addition_nat.pl, die das Prädikat add/3 definieren, und zwei Klauseln, die das Prädikat mul/3<br />
definieren. Auf die Anfrage<br />
?- mul(n(n(0)), n(n(n(0))), Produkt).<br />
sollte Prolog jetzt<br />
Produkt = n(n(n(n(n(n(0))))))<br />
antworten, da 2 · 3 = 6 ist.<br />
Aufgabe 13 Quadratzahlen<br />
Dateiname: quadratzahl.pl<br />
Schreiben Sie ein Prologprogramm, das ein Prädikat quadratzahl/1 definiert, das alle Quadratzahlen<br />
aufzählt:<br />
?- quadratzahl(Q).<br />
Q = 0 ;<br />
Q = 1 ;<br />
Q = 4 ;<br />
Q = 9 ;<br />
Q = 16 ;<br />
Q = 25 ;<br />
Q = 36 ;<br />
Q = 49<br />
Die Menge der Quadratzahlen wird von diesem Programm zusammen mit der Anfrage aufgezählt.<br />
Wir sagen, die Menge der Quadratzahlen ist rekursiv aufzählbar. Das Wort „rekursiv“ in<br />
„rekursiv aufzählbar“ bezieht sich darauf, dass eine Aufzählung der Quadratzahlen durch Rekursion<br />
möglich ist. Prolog basiert ja auf Rekursion. Schauen Sie sich Ihr Programm daraufhin an,<br />
an welcher Stelle oder an welchen Stellen darin eine Rekursion auftritt.<br />
Aufgabe 14 Translation und Rotation von geometrischen Objekten<br />
Dateiname: a14_Bewegung.pl<br />
8
In einem Geometrie-Software-System werden die folgenden Arten von geometrischen Objekten<br />
der Ebene als Prologterme dargestellt:<br />
Punkt (X,Y)<br />
als Prologterm punkt(X,Y).<br />
Beispiel: punkt(3,5).<br />
Strecke AB, wobei A und B Punkte sind<br />
als Prologterm strecke(A,B).<br />
Beispiel: strecke(punkt(3,5),punkt(2,1)).<br />
Dreieck ABC<br />
als Prologterm dreieck(A,B,C).<br />
Beispiel: dreieck(punkt(3,5),punkt(2,1),punkt(-1,4)).<br />
Kreis mit Mittelpunkt M und Radius R<br />
als Prologterm kreis(M,R).<br />
Beispiel: kreis(punkt(3,5),2).<br />
Weiter stellt das Geometrie-Software-System dar:<br />
Vektor (F,G)<br />
als Prologterm vektor(F,G).<br />
Beispiel: vektor(2,1).<br />
sowie die folgenden Arten von Transformationen (Bewegungen):<br />
Translation (Verschiebung) um einen Vektor V<br />
als Prologterm translation(V).<br />
Beispiel: translation(vektor(2,1)).<br />
Rotation (Drehung) um einen Drehpunkt D herum und um einen Drehwinkel Phi (Radians und<br />
entgegen dem Uhrzeigersinn)<br />
als Prologterm rotation(D,Phi).<br />
Beispiel: rotation(punkt(3,5),3.14159).<br />
Definieren Sie ein Prologprädikat transformiere/3 zum Transformieren (Verschieben, Drehen)<br />
von Objekten mit der Spezifikation<br />
% transformatiere(+Objekt,+Transformation,-Objekt_neu) bedeutet:<br />
% Objekt_neu ergibt sich durch Anwendung von Transformation auf Objekt.<br />
Hier ist ein Beispiel für eine mögliche Anfrage und Antwort von Prolog:<br />
?- A = punkt(1,3), B = punkt(4,4), AB = strecke(A,B), D = punkt(2,1),<br />
Pi_halbe is 3.14159265/2, Rot = rotation(D,Pi_halbe),<br />
transformiere(AB,Rot,Strecke_neu).<br />
A = punkt(1, 3),<br />
B = punkt(4, 4),<br />
AB = strecke(punkt(1, 3), punkt(4, 4)),<br />
D = punkt(2, 1),<br />
Pi_halbe = 1.570796325,<br />
Rot = rotation(punkt(2, 1), 1.570796325),<br />
Strecke_neu = strecke(punkt(-1.794896453688466e-9, 3.5897930298375673e-9),<br />
punkt(-0.9999999964102071, 3.0000000053846896)).<br />
In der Anfrage wurden zunächst zwei Punkte A = (1, 3) und B = (4, 4) spezifiziert, dann die<br />
9
Strecke AB und der Punkt D = (2, 1), dann die Zahl π/2 ausgerechnet sowie die Transformation<br />
Rot= „Vierteldrehung gegen den Uhrzeigersinn um den Drehpunkt D herum“ spezifiziert.<br />
Schließlich wurde das Prädikat transformiere/3 aufgerufen, um diese Transformation auf die<br />
gegebene Strecke anzuwenden. Das Ergebnis der Transformation ist eine neue Strecke vom Punkt<br />
(0, 0) zum Punkt (−1, 3). Da ich π nur näherungsweise angegeben habe und auch beim Rechnen<br />
durch Prolog kleine Rundungsfehler auftreten, ist die Ausgabe von Prolog auch nur näherungsweise.<br />
In SWI-Prolog (aber nicht in allen Prologs) kann man statt 3.14159265 auch einfach pi<br />
schreiben.<br />
Aufgabe 15 Strukturbäume<br />
Dateiname: a15_Strukturbaeume.pdf<br />
Lassen Sie sich von jedem der folgenden Terme mit write_canonical die kanonische Form<br />
anzeigen! Zeichnen Sie dann den Strukturbaum für den betreffenden Term! Fassen Sie all diese<br />
Strukturbäume in einer Datei a15_Strukturbaeume.pdf zusammen!<br />
a<br />
f(g(a,h(b,c),a),h(c,a))<br />
f(X,g(Y,X),a)<br />
3*sin(2*x)-2*cos(3*x)+X<br />
C is sqrt(A**2+B**2)<br />
[1,2,3,4]<br />
[1,2,3|4]<br />
[[a1,a2,a3],[b1,b2],[c1,c2]]<br />
[]<br />
[a]<br />
[[a]]<br />
Vektor = [3,4,8]<br />
Aufgabe 16 dag<br />
Dateiname: a16_dag.pdf<br />
Lassen Sie das Programm a16_dag.pl mit den darin als Kommentare angegebenen Anfragen<br />
laufen! Beantworten Sie die folgenden Fragen und schreiben Sie Ihre Antworten in eine Datei<br />
a16_dag.pdf!<br />
1. Wieviele a’s enthält der Term, den Prolog bei der Anfrage ?- p(0,T). für T ausgibt?<br />
2. Wieviele a’s enthält der Term, den Prolog bei der Anfrage ?- p(1,T). für T ausgibt?<br />
3. Wieviele a’s enthält der Term, den Prolog bei der Anfrage ?- p(2,T). für T ausgibt?<br />
4. Wieviele a’s enthält der Term, den Prolog bei der Anfrage ?- p(3,T). für T ausgibt?<br />
5. Allgemein: Sei n eine natürliche Zahl. Wieviele a’s enthält der Term, den Prolog bei der<br />
Anfrage ?- p(n,T). für T ausgibt?<br />
6. Wieviele a’s enthält der Term, den Prolog bei der Anfrage ?- p(1000,T). für T ausgibt?<br />
7. Was sagt das über die interne Darstellung des Terms im Computer aus?<br />
Aufgabe 17 Buchstabieren<br />
Dateiname: a17_buchstabieren.pl<br />
Beim Buchstabieren verwendet man zwecks Vermeidung von Missverständnissen gerne eine Buchstabiertafel,<br />
die jedem Buchstaben ein Wort zuordnet. Hier ist ein Ausschnitt aus einer solchen<br />
Buchstabiertafel:<br />
10
a Anton<br />
b Berta<br />
c Caesar<br />
d Dora<br />
e Emil<br />
f Friedrich<br />
Wenn man nun ein Wort buchstabieren will, sucht man jeden Buchstaben dieses Wortes in der<br />
linken Spalte dieser Tabelle und wandelt ihn in das entsprechende Wort der rechten Spalte der<br />
Tabelle um. Aus dem Wort „Affe“ wird so z.B. „Anton Friedrich Friedrich Emil“.<br />
Für ein Prolog-Programm zum Buchstabieren stellen wir jeden Buchstaben der linken Spalte<br />
der Tabelle dar als ein Prologatom, das nur aus einem Kleinbuchstaben besteht, z.B. a. Jedes<br />
Wort der rechten Spalte der Tabelle stellen wir ebenfalls als Prologatom dar, das aber mit<br />
einem Großbuchstaben beginnt. Wir müssen es daher zwischen einfache Anführungszeichen setzen,<br />
z.B. ’Anton’. Das zu buchstabierende Wort (z.B. „Affe“) stellen wir in Prolog als Liste von<br />
Kleinbuchstaben dar, z.B. [a,f,f,e]. Das Ergebnis des Buchstabierens stellen wir in Prolog<br />
als Liste von Atomen dar. So soll z.B. die Liste [a,f,f,e] umgewandelt werden in die Liste<br />
[’Anton’,’Friedrich’,’Friedrich’,’Emil’] und umgekehrt. Der Einfachheit halber beschränken<br />
wir uns dabei auf die Kleinbuchstaben a, b, c, d, e und f und auf die oben abgebildete<br />
Buchstabiertafel.<br />
Schreiben Sie nun ein Prolog-Programm, das diese Aufgabe löst, indem Sie ein Prolog-Prädikat<br />
buchstabiere/2 definieren, sodass z.B.<br />
buchstabiere([a,f,f,e],[’Anton’,’Friedrich’,’Friedrich’,’Emil’])<br />
gilt! Testen Sie Ihr Programm auch mit Anfragen wie<br />
?- buchstabiere([a,f,f,e],Woerterliste).<br />
?- buchstabiere(Buchstabenliste,[’Anton’,’Friedrich’,’Friedrich’,’Emil’]).<br />
Aufgabe 18 Bitlisten<br />
Dateiname: a18_bitliste.pdf<br />
In heutigen digitalen Computern werden Informationen als endliche Folgen von Bits 0 oder 1<br />
dargestellt. Eine solche Folge kann man in Prolog darstellen als Bitliste, also als Liste von Bits<br />
0 oder 1, z.B. [1,1,0,1]. Wir wollen uns überzeugen, dass die Menge aller Bitlisten rekursiv<br />
aufzählbar ist. Dazu schreiben wir ein Prologprogramm und dazu eine Anfrage, die die Menge<br />
aller Bitlisten aufzählen soll. In der Datei a18_bitliste.pl steht ein solches Programm und<br />
einige Anfragen dazu. Lassen Sie das Programm mit jeder der vier Anfragen laufen, wobei Sie<br />
sich jeweils mit Strichpunkt nacheinander Antworten ausgeben lassen! Beantworten Sie dann die<br />
folgenden Fragen:<br />
1. Zwei der dort definierten Prädikate sind zueinander logisch äquivalent. Welche Prädikate<br />
sind das? Geben Sie sie in der Form „Prädikatsname/Stelligkeit“ an!<br />
2. Welche der Anfragen 1) bis 4) zählen die Menge aller Bitlisten auf?<br />
3. Warum zählen diese Anfragen die Menge auf und warum tun die anderen Anfragen es<br />
nicht?<br />
4. Worin unterscheidet sich das Antwortverhalten der vier Anfragen und warum?<br />
Aufgabe 19 Teillisten<br />
Dateiname: a19_teilliste.pl<br />
11
Unter einer Teilliste einer Liste L wollen wir eine Liste T verstehen, die sich dadurch ergibt,<br />
dass man aus L null oder mehr Elemente auswählt und daraus eine Liste bildet, in der die<br />
Reihenfolge der ausgewählten Elemente gleich wie in L ist. So ist z.B. [s,b,g] eine Teilliste der<br />
Liste [s,a,l,z,b,u,r,g]. Definieren Sie ein Prädikat teilliste/2, sodass Prolog z.B. auf die<br />
Anfrage<br />
?- teilliste(T,[s,a,l,z,b,u,r,g]).<br />
bei wiederholter Eingabe des Strichpunkts für T alle 256 Teillisten der Liste [s,a,l,z,b,u,r,g]<br />
liefert! Eine der Antworten lautet dann T=[s,b,g].<br />
Lassen Sie sich mit dem in Prolog eingebauten Prädikat findall/3 durch die Anfrage<br />
?- findall(T, teilliste(T,[a,b]), Teillisten).<br />
die Liste aller Teillisten T der Liste [a,b] ausgeben! Was Prolog dabei macht, ist, dass es für jede<br />
Lösung des Ziels teilliste(T,[a,b]) den entsprechenden Wert von T in eine Liste aufnimmt<br />
und die Variable Teillisten dann mit der resultierenden Liste unifiziert. Lassen Sie sich nun<br />
die Anzahl der Teillisten der Liste [s,a,l,z,b,u,r,g] mit der Anfrage<br />
?- findall(T, teilliste(T,[s,a,l,z,b,u,r,g]), Teillisten),<br />
length(Teillisten, Anzahl_der_Teillisten).<br />
ausgeben! Das Prädikat length/2 zur Berechnung der Länge einer Liste ist in vielen Prologs<br />
eingebaut. Man kann es wie in der Datei listenlaenge.pl gezeigt aber auch leicht selbst definieren.<br />
Die Teillisten der Liste [s,a,l,z,b,u,r,g] entsprechen genau den Teilmengen der<br />
Menge {s, a, l, z, b, u, r, g}. Davon gibt es 2 8 = 256.<br />
Aufgabe 20 Cliquen-Problem 2<br />
Dateiname: a20_clique2.pl<br />
Lesen Sie sich nochmals die Angabe von Aufgabe 5 (Cliquen-Problem) durch! Man kann die<br />
Situation durch einen ungerichteten Graphen darstellen (Datei clique.ps). Eine Clique ist dann<br />
eine Menge von Knoten des Graphen, von denen jeder mit jedem anderen verbunden ist. In<br />
unserem Beispiel ist {franz, berta, dora} eine Clique, da jeder darin genannte Knoten mit jedem<br />
anderen verbunden ist. Eine solche Menge kann man in Prolog als Liste darstellen, etwa als Liste<br />
[franz,berta,dora]. Definieren Sie nun ein Prädikat clique/1, das von einer solchen Liste<br />
testen kann, ob sie eine Clique darstellt. Z.B. sollte die Anfrage<br />
?- clique([franz,berta,dora]).<br />
die Antwort yes oder true liefern. Die Anfrage<br />
?- clique([berta,dora,franz]).<br />
sollte die gleiche Antwort liefern. Alle Cliquen aus mehr als einer Person haben also mehrere<br />
Darstellungen als Liste. Verwenden Sie nun das Prädikat der vorigen Aufgabe und stellen Sie die<br />
Anfrage<br />
?- teilliste(Clique, [anton,berta,christa,dora,ernst,franz]),<br />
clique(Clique).<br />
12
Warum liefert Prolog nun jede Clique nur einmal als Antwort? Schreiben Sie Ihre Antwort auf<br />
diese Frage als Kommentar ans Ende Ihrer Programmdatei! Hier erzeugen (englisch: generate)<br />
wir uns nacheinander alle Teillisten der gegebenen Liste. Von jeder Teilliste testen wir dabei, ob<br />
sie eine Clique darstellt. Man nennt dieses Verfahren „generate and test“. Es wird in der logischen<br />
<strong>Programmierung</strong> häufig verwendet.<br />
Aufgabe 21 Arithmetische Ausdrücke auswerten<br />
Dateiname: a21_ausdruck.pl<br />
Wir betrachten alle arithmetischen Ausdrücke, die man aus Zahlen und dem Buchstaben x mittels<br />
der Zeichen +, -, *, / für die vier Grundrechenarten sowie mittels Klammern ( und ) bilden<br />
kann. Ein Beispiel für einen solchen Ausdruck ist x*x+2*x-7. Definieren Sie ein Prolog-Prädikat<br />
wert/3 mit dem Aufrufmodus wert(+Ausdruck,+Zahl,?Zahl) zur Berechnung des Wertes des<br />
Ausdrucks zu einem gegebenen x-Wert! Beispiel:<br />
?- wert(x*x+2*x-7, 5, W).<br />
W = 28.<br />
berechnet den Wert W des Ausdrucks x*x+2*x-7 für x=5, also W = 5 · 5 + 2 · 5 − 7 = 28. Die<br />
Antwort für W sollte die gleiche sein wie bei der Anfrage<br />
?- X = 5, W is X*X+2*X-7.<br />
Nur, dass oben die Mathematikvariable x dargestellt ist als Prolog-Atom x, während sie darunter<br />
dargestellt ist als Prolog-Variable X.<br />
Testen Sie Ihr Programm mit verschiedenen – auch geklammerten und komplizierter geschachtelten<br />
– Ausdrücken und mit verschiedenen x-Werten!<br />
Aufgabe 22 Wertetabelle<br />
Dateiname: a22_wertetabelle.pl<br />
Definieren Sie als Ergänzung zur vorigen Aufgabe ein Prolog-Prädikat tabelle/3 mit dem Aufrufmodus<br />
tabelle(+Ausdruck,+ganzeZahl,+ganzeZahl), sodass für einen arithmetischen Ausdruck<br />
A wie in der vorigen Aufgabe sowie für zwei ganze Zahlen m und n mit m ≤ n ein Aufruf<br />
des Ziels tabelle(A,m,n) bewirkt, dass eine Wertetabelle für den Ausdruck A und alle ganzen<br />
Zahlen x mit m ≤ x ≤ n ausgegeben wird. Die erste Zeile der Tabelle soll das Zeichen x gefolgt<br />
von einem Leerzeichen und vom Ausdruck A enthalten, die zweite Zeile soll nur 8 Minuszeichen<br />
enthalten. Danach soll nacheinander für jedes x von m bis n eine Zeile kommen, in der zuerst<br />
der Wert von x steht, dann ein Leerzeichen, dann der Wert von A für diesen x-Wert. Beispiel:<br />
?- tabelle(x*x+2*x-7, 3, 6).<br />
x x*x+2*x-7<br />
--------<br />
3 8<br />
4 17<br />
5 28<br />
6 41<br />
Verwenden Sie in Ihrem Programm das Prädikat wert/3 aus der vorigen Aufgabe! Konsultieren<br />
Sie beim Testen beide Dateien, etwa mit<br />
13
?- [a21_ausdruck, a22_wertetabelle].<br />
Aufgabe 23 Plus-Minus-Terme<br />
Dateiname: a23_plusminus.pl<br />
In dieser Aufgabe betrachten wir Ausdrücke, die aus Zahlen mit Hilfe von + und − und Klammern<br />
gebildet sind, z.B. den Ausdruck 8 − (2 + 3). Die übliche Darstellung eines solchen Ausdrucks,<br />
z.B. 8-(2+3) stellt zugleich einen Term in Prolog dar. Wir wollen einen solchen Term einen Plus-<br />
Minus-Term nennen. Wir wollen aber noch eine alternative Darstellung eines solchen Ausdrucks<br />
als Prologterm betrachten. Dazu definieren wir induktiv den Begriff „add-sub-Term“. add-sub-<br />
Terme sind spezielle Prologterme. Hier die induktive Definition:<br />
1. Für jede Zahl z ist der Prologterm zahl(z) ein add-sub-Term.<br />
2. Wenn t1 und t2 zwei add-sub-Terme sind, dann sind auch add(t1,t2) und sub(t1,t2) addsub-Terme.<br />
Jeder Plus-Minus-Term hat auch eine Darstellung als add-sub-Term und umgekehrt. Z.B. entspricht<br />
dem Plus-Minus-Term 8-(2+3) der add-sub-Term sub(zahl(8),add(zahl(2),zahl(3))).<br />
Stellen wir uns nun vor, wir haben bereits ein Programm, das mit dieser Darstellung arbeitet,<br />
aber auch ein von jemand anderem geschriebenes Programm mit ähnlicher Darstellung, nur dass<br />
die Funktoren anders benannt sind: sum statt add, dif statt sub und num statt zahl. Analog wie<br />
oben definieren wir: sum-dif-Terme sind spezielle Prologterme. Hier die induktive Definition:<br />
1. Für jede Zahl z ist der Prologterm num(z) ein sum-dif-Term.<br />
2. Wenn t1 und t2 zwei sum-dif-Terme sind, dann sind auch sum(t1,t2) und dif(t1,t2) sumdif-Terme.<br />
Jeder Plus-Minus-Term hat auch eine Darstellung als sum-dif-Term und umgekehrt. Z.B. entspricht<br />
dem Plus-Minus-Term 8-(2+3) der sum-dif-Term dif(num(8),sum(num(2),num(3))).<br />
Wir wollen beide Programme in einem größeren Softwaresystem verwenden und benötigen daher<br />
Prädikate zur Übersetzung von einer Darstellung in die andere. Definieren Sie dazu<br />
1. ein Prologprädikat addsub_sumdif/2 zur Übersetzung eines add-sub-Terms in einen sumdif-Term<br />
und umgekehrt! Z.B. sollte gelten<br />
addsub_sumdif(sub(zahl(8),add(zahl(2),zahl(3))),<br />
dif(num(8),sum(num(2),num(3))))<br />
2. ein Prologprädikat addsub_plusminus/2 zur Übersetzung eines add-sub-Terms in einen<br />
Plus-Minus-Term! Beispiel:<br />
?- addsub_plusminus(sub(zahl(8),add(zahl(2),zahl(3))), Plusminusterm).<br />
Plusminusterm = 8-(2+3)<br />
3. ein Prologprädikat plusminus_addsub/2 zur Übersetzung eines Plus-Minus-Terms in einen<br />
add-sub-Term! Beispiel:<br />
?- plusminus_addsub(8-(2+3), Addsubterm).<br />
Addsubterm = sub(zahl(8),add(zahl(2),zahl(3)))<br />
Hinweis: Schauen Sie sich die Dokumentation des Prolog-Prädikats number/1 an! Probieren<br />
Sie es auch aus mit Anfragen wie<br />
?- number(5).<br />
?- number(2+3).<br />
14
4. ein Prologprädikat addsub_Wert/2 zur Berechnung des Wertes eines add-sub-Terms als<br />
Zahl! Beispiel:<br />
?- addsub_Wert(sub(zahl(8),add(zahl(2),zahl(3))), Wert).<br />
Wert = 3<br />
Denn 8 − (2 + 3) = 3.<br />
Aufgabe 24 Unifikation<br />
Dateiname: a24_unifikation<br />
Zur Erinnerung: Unifikationsalgorithmus von Robinson:<br />
Vorab die Definition eines Hilfsbegriffs:<br />
Seien s und t zwei verschiedene Terme 1 . Die Nichtübereinstimmungsmenge (engl. disagreement<br />
set) D von s und t ist die Menge, die besteht aus den beiden Teiltermen von s und t, die an der<br />
ersten Stelle, an der sich s und t unterscheiden 2 , anfangen.<br />
Beispiel: Die Nichtübereinstimmungsmenge der Terme f(X,g(X)) und f(g(a),Y) ist die Menge<br />
{X, g(a)}.<br />
Um die Terme s und t zu unifizieren, müssen wir zumindest die Nichtübereinstimmung eliminieren,<br />
also zunächst die Terme der Nichtübereinstimmungsmenge miteinander unifizieren.<br />
Allgemein gilt: Die Nichtübereinstimmungsmenge D hat eine der folgenden beiden Formen:<br />
• {X, r} mit einer Variablen X und einem von X verschiedenen Term r. Dann gilt: Wenn X<br />
in r nicht vorkommt, dann ist {X ← r} ein mgu von D. Wenn X in r vorkommt, ist D<br />
nicht unifizierbar.<br />
• {f(s1, . . . , sm), g(t1, . . . , tn)} mit zwei voneinander verschiedenen Funktoren f/m und g/n. 3<br />
In diesem Fall ist D nicht unifizierbar.<br />
Nun zum Unfikationsalgorithmus. Wir wollen für zwei Terme s und t einen allgemeinsten Unifikator<br />
(mgu) berechnen. Der Ablauf soll hier nur informell am Beispiel der Terme<br />
f(X,g(X))<br />
f(g(a),Y)<br />
demonstriert werden. Wir bestimmen die Nichtübereinstimmungsmenge {X, g(a)} der beiden<br />
Terme und davon wie oben gezeigt einen mgu<br />
{X
{Y
Die Aufgabe:<br />
Wenden Sie nun den oben beschriebenen Unifikationsalgorithmus an, um jeweils die folgenden<br />
Terme miteinander zu unifizieren:<br />
a○ f(a,b) mit f(X,c)<br />
b○ f(g(X,Y),X) mit f(U,h(Y))<br />
c○ f(g(X,Y),X) mit f(U,h(Y,U))<br />
Schreiben Sie den Ablauf des Algorithmus in eine Datei a24_unifikation:<br />
Aufgabe 24a:<br />
...<br />
Aufgabe 24b:<br />
...<br />
Aufgabe 24c:<br />
...<br />
wobei Sie jeweils für „...“ die schematische Darstellung des Ablaufs des Algorithmus wie oben<br />
beschrieben einsetzen!<br />
Aufgabe 25 SLD-Baum nat<br />
Dateiname: a25_sldBaum.pdf<br />
Gegeben sei das Prologprogramm<br />
nat(0).<br />
nat(f(X)) :- nat(X).<br />
mit der Anfrage<br />
?- nat(f(f(0))).<br />
1. Zeichnen Sie dazu eine SLD-Widerlegung als Baum. Führen Sie dabei jedesmal, wenn Sie<br />
eine Programmklausel verwenden, neue Variablen ein.<br />
2. Schreiben Sie bei Ihrem Baum neben jeden Resolutionsschritt den jeweiligen allgemeinsten<br />
Unifikator.<br />
3. Bilden Sie die Komposition der allgemeinsten Unifikatoren aller Resolutionsschritte.<br />
4. Bestimmen Sie daraus die berechnete Antwortsubstitution.<br />
Aufgabe 26 Grammatik<br />
Dateinamen: a26_mannisstapfel.pdf und a26_saftigenapfel.pl<br />
Über Grammatiken<br />
17
Die folgende Aufgabe befasst sich mit dem Problem der Syntaxanalyse (englisch: parsing). Dabei<br />
geht es darum, Sätze in einer natürlichen Sprache (z.B. in Deutsch oder Englisch) oder Ausdrücke<br />
in einer formalen Sprache (z.B. in einer Programmiersprache geschriebene Programme)<br />
zu analysieren, d.h. in ihre Bestandteile zu zerlegen. Hierzu hat der Sprachwissenschaftler und<br />
Informatiker Noam Chomsky seine Grammatiken eingeführt. Insbesondere von Interesse sind hier<br />
die sogenannten kontextfreien Chomsky-Grammatiken.<br />
Nehmen wir hierzu als Beispiel (aus dem Buch „Programming in Prolog“ von Clocksin und<br />
Mellish) den Satz<br />
Der Mann isst den Apfel.<br />
Dieser Satz besteht aus zwei Teilen, und zwar aus einer Nominalphrase „der Mann“ und aus<br />
einer Verbalphrase „isst den Apfel“. Ein Satz kann also bestehen aus einer Nominalphase gefolgt<br />
von einer Verbalphrase. Wir schreiben das als Grammatikregel<br />
satz --> nominalphrase, verbalphrase.<br />
Die Nominalphrase „der Mann“ wiederum besteht aus einem Artikel „der“ und einem Nomen<br />
„Mann“ gemäß der Grammatikregel<br />
nominalphrase --> artikel, nomen.<br />
Die Verbalphrase „isst den Apfel“ besteht aus dem Verb „isst“ und der Nominalphrase „den<br />
Apfel“ gemäß der Grammatikregel<br />
verbalphrase --> verb, nominalphrase.<br />
Die Nominalphrase „den Apfel“ besteht aus dem Artikel „den“ und dem Nomen „Apfel“ gemäß<br />
der oben bereits erwähnten Grammatikregel<br />
nominalphrase --> artikel, nomen.<br />
Auch für die Vokabeln verwenden wir Grammatikregeln. Z.B. schreiben wir<br />
nomen --> [apfel].<br />
Die eckigen Klammern dienen der Unterscheidung zwischen grammatikalischen Begriffen wie<br />
satz, nominalphrase, verbalphrase usw. und Vokabeln wie der, mann, apfel usw. Insgesamt<br />
haben wir die folgende kontextfreie Chomsky-Grammatik (Datei mannisstapfel_gr.pl).<br />
satz --> nominalphrase, verbalphrase.<br />
nominalphrase --> artikel, nomen.<br />
verbalphrase --> verb, nominalphrase.<br />
artikel --> [der].<br />
artikel --> [den].<br />
nomen --> [mann].<br />
nomen --> [apfel].<br />
verb --> [isst].<br />
18
Die grammatikalische Struktur des Satzes „der mann isst den apfel“ lässt sich am anschaulichsten<br />
graphisch als Zerlegungsbaum (englisch: parse tree) darstellen:<br />
satz<br />
nominalphrase verbalphrase<br />
artikel nomen verb nominalphrase<br />
artikel nomen<br />
[der] [mann] [isst] [den] [apfel]<br />
Ein häufig auftretendes Problem ist, ein Programm zu schreiben, das Sätze wie „Der Mann isst<br />
den Apfel.“ analysieren kann, z.B. um automatisch sinnvoll darauf reagieren zu können. Ein<br />
naheliegender erster Schritt ist, den Satz in seine Wörter zu zerlegen. In Prolog würden wir dann<br />
etwa eine Liste<br />
[der,mann,isst,den,apfel]<br />
von Prolog-Atomen erhalten. Dieser erste Schritt ist relativ einfach und soll hier nicht weiter<br />
untersucht werden. Interessanter ist die Zerlegung dieser Liste in ihre Bestandteile (Phrasen).<br />
Ein Computerprogramm, das eine solche Zerlegung (englisch: parsing) durchführt, wird Parser<br />
genannt.<br />
Eine häufig verwendete Vorgangsweise beim Parsing ist die folgende. Wir wollen einen Satz (im<br />
Beispiel [der,mann,isst,den,apfel]) lesen und analysieren. Wir lesen ein Wort nach dem<br />
anderen. Zu jedem Zeitpunkt betrachten wir den noch nicht gelesenen Endteil der Liste. Dieser<br />
Endteil ist anfänglich die gesamte Liste [der,mann,isst,den,apfel] und nach vollständigem<br />
Lesen der Liste die leere Liste [].<br />
Wir wissen, dass ein Satz aus einer Nominalphrase und einer Verbalphrase besteht. Im Beispiel ist<br />
der nach Lesen der Nominalphrase [der,mann] übrig bleibende Endteil die Liste [isst,den,apfel].<br />
Wir wollen das in Prolog so schreiben:<br />
nominalphrase([der,mann,isst,den,apfel],[isst,den,apfel])<br />
Allgemein soll das zweistellige Prologprädikat nominalphrase/2 die folgende Bedeutung haben.<br />
nominalphrase(S0,S) soll genau dann gelten, wenn die Liste S0 mit einer Nominalphrase<br />
anfängt und der restliche Teil der Liste die Liste S ist.<br />
Sinngemäß analog soll die Bedeutung der Prädikate satz/2, verbalphrase/2, usw. sein.<br />
Wie müssen wir diese Prädikate in Prolog definieren? Betrachten wir dazu eine Liste S0 von<br />
Wörtern. Aufgrund unserer Grammatikregel<br />
satz --> nominalphrase, verbalphrase.<br />
19
esteht ein Satz aus einer Nominalphrase und einer Verbalphrase. Um einen Satz von der Liste<br />
S0 wegzulesen, lesen wir also zunächst eine Nominalphrase. Sei S1 der übrigbleibende Endteil der<br />
Liste S0, also nominalphrase(S0,S1). Von diesem Endteil lesen wir nun noch eine Verbalphrase<br />
weg und erhalten einen neuen Endteil S. Also verbalphrase(S1,S). Insgesamt haben wir von<br />
der ursprünglichen Liste S0 einen Satz weggelesen und der neue Endteil war S. Also satz(S0,S).<br />
Insgesamt lässt sich daher unsere Grammatikregel übersetzen in die Prologklausel<br />
satz(S0,S) :- nominalphrase(S0,S1), verbalphrase(S1,S).<br />
Zum Beispiel gilt<br />
nominalphrase([der,mann,isst,den,apfel],[isst,den,apfel]) und<br />
verbalphrase([isst,den,apfel],[]) und daher<br />
satz([der,mann,isst,den,apfel],[]).<br />
Die Aufgabe:<br />
Schauen Sie sich das Programm mannisstapfel.pl an. Erklären Sie die Klauseln für artikel/2,<br />
nomen/2 und verb/2. Stellen Sie Anfragen wie<br />
?- satz([der,mann,isst,den,apfel],[]).<br />
?- satz([der,mann,isst,den,apfel,x,y,z],S).<br />
?- satz(S,[]).<br />
?- satz(S,[x,y,z]).<br />
?- satz([mann,apfel,isst],[]).<br />
Erklären Sie, was dabei passiert. Nun starten Sie SWI-Prolog mit der Grammatik mannisstapfel_gr.pl<br />
als Prolog-Programm. Lassen Sie sich das Programm mit der Anfrage<br />
?- listing.<br />
auflisten. Was hat Prolog aus der Grammatik gemacht? Praktisch alle Prologs haben Unterstützung<br />
für Grammatikregeln eingebaut und statt einer Anfrage<br />
?- satz(S,[]).<br />
kann man die etwas benutzerfreundlichere Anfrage<br />
?- phrase(satz,S).<br />
stellen. Das kann man lesen als: S ist ein Satz. Statt satz kann man natürlich auch nominalphrase,<br />
verbalphrase, usw. verwenden. Probieren Sie das mit verschiedenen Anfragen aus.<br />
Warum liefert das Programm auch Antworten, die gemäß der deutschen Grammatik grammatikalisch<br />
falsch sind? Wie könnte man es verbessern? Fügen Sie weitere Klauseln hinzu, damit<br />
Sätze wie „Der Mann singt“ und „Der Mann isst den saftigen Apfel“ als grammatikalisch richtig<br />
erkannt werden.<br />
20