Prolog (vom Französischen: programmation en logique,A. Colmerauer und P. Roussel: The birth of prolog. History of programming languages II. (PDF; 2,1 MB) ist.psu.edu, 1996, S. 331–367. deutsch: „Programmieren in Logik“) ist eine Programmiersprache, die Anfang der 1970er Jahre maßgeblich von dem französischen Informatiker Alain Colmerauer entwickelt wurde und ein deklaratives Programmieren ermöglicht. Sie gilt als die wichtigste logische Programmiersprache.
Erste Implementierungen wichen in ihrer Syntax stark voneinander ab, aber der Edinburgh-Dialekt setzte sich bald als Quasistandard durch. Er war jedoch nicht formal definiert,In Ermangelung einer formalen Spezifikation für Edinburgh Prolog wurde meist das DEC-10 PROLOG Manual von Bowen u. a. (1982) oder Programming in Prolog von Clocksin und Mellish herangezogen. bis er 1995 zur Grundlage eines ISO-Standards wurde (ISO/IEC 13211-1), der auch ISO-Prolog genannt wird.
Der erste Prolog-Interpreter wurde in Marseille in ALGOL W realisiert. Der erste Ansatz für einen Compiler stammte von David H. D. Warren aus Edinburgh. Dieser hatte als Zielsprache die des Logik-Prozessors Warren’s Abstract Machine und erlaubte deshalb weder dynamische Änderungen noch einen Anschluss rücksetzbarer Prädikate in anderen Programmiersprachen. Der erste voll nutzbare Compiler, der beides erlaubte, wurde von Preben Folkjaer und Christian Pichler in München entwickelt. Er verwandte einen anderen, von der TU Wien stammenden Zwischencode, der inkrementell kompiliert wurde; wurden Prädikate verändert, wurde das Kompilat gelöscht und beim nächsten Aufruf neu kompiliert.
Grundprinzip Prolog-Programme bestehen aus einer Wissensdatenbank, deren Einträge sich Fakten und Regeln nennen. Der Benutzer formuliert Anfragen an diese Wissensdatenbank. Der Prolog-Interpreter benutzt die Fakten und Regeln, um systematisch eine Antwort zu finden. Ein positives Resultat bedeutet, dass die Anfrage logisch ableitbar ist. Ein negatives Resultat bedeutet nur, dass aufgrund der Datenbasis keine Ableitung gefunden werden kann. Dies hängt eng mit der Closed world assumption zusammen (siehe unten).
Das typische erste Programm in Prolog ist nicht wie in prozeduralen Programmiersprachen ein Hallo-Welt-Beispiel, sondern eine Wissensdatenbank mit Stammbauminformationen.
Folgendes Beispiel repräsentiert den Stammbaum einer kleinen Familie. Das erste Faktum in Form einer Aussage mann(tobias). liest sich als: Tobias ist ein Mann. vater(tobias, frank). definiert das Faktum: Tobias ist der Vater von Frank. Für Hinweise zum Laden von Prolog-Texten siehe entsprechenden Abschnitt:
% Prolog-Text mit Fakten mann(adam). mann(tobias). mann(frank). frau(eva). frau(daniela). frau(ulrike). vater(adam, tobias). vater(tobias, frank). vater(tobias, ulrike). mutter(eva, tobias). mutter(daniela, frank). mutter(daniela, ulrike).
In einem Prolog-Interpreter können nun interaktiv Anfragen an die Datenbasis gestellt werden. Das Ausführen eines Prolog-Programms bedeutet immer das Stellen einer Anfrage. Das System antwortet entweder mit yes./true. oder no./false., abhängig davon, ob die Anfrage bewiesen werden konnte. Der Interpreter signalisiert mit der Eingabeaufforderung ?-, dass er eine Anfrage erwartet: ?- mann(tobias). yes. ?- mann(heinrich). no. Eine Anfrage mit einer Variablen liefert als Antwort zusätzlich Belegungen, mit denen die Anfrage wahr wird. Man nennt eine solche Variablenbelegung Unifikation und sagt, die Variable wird mit diesem Wert unifiziert. Variablen sind in Prolog Token, die mit einem Großbuchstaben beginnen: ?- frau(X). X=eva X=daniela X=ulrike no ist die Antwort auf die um die vorher ausgegebenen Antworten reduzierte Faktenliste.
Der Interpreter liefert nur positive Antworten auf Anfragen, die explizit definiert oder folgerbar sind (Closed world assumption). So liegen etwa über heinrich keinerlei Informationen in der Datenbasis: ?- mann(heinrich). no. ?- frau(heinrich). no. Zusätzlich zu Fakten lassen sich in Prolog Regeln formulieren. Der Regeloperator :- ist dabei wie ein umgedrehter Implikationspfeil zu lesen. Beispiel:
% Prolog Text mit Regel grossvater(X, Y) :- vater(X, Z), vater(Z, Y).
Die Regel besagt: X ist Großvater von Y, wenn es ein Z gibt, sodass X Vater von Z ist und Z Vater von Y. Damit ist der Großvater väterlicherseits definiert. Eine zweite Regel für den Großvater mütterlicherseits sieht so aus:
grossvater(X, Y) :- vater(X, Z), mutter(Z, Y).
Der Operator , in dieser Regel definiert eine Konjunktion und wird und gesprochen. Der Term links vom Implikationsoperator nennt sich auch Head oder Konsequenz. Haben zwei Regeln (wie oben) die gleiche Konsequenz, folgt diese, wenn mindestens in einer Regel die Vorbedingung erfüllt ist (Disjunktion).
Durch die Definition von Regeln kann auch auf Fakten geschlossen werden, die nicht explizit in der Datenbasis stehen: ?- grossvater(adam, ulrike). yes. ?- grossvater(X, frank). X=adam
Syntax Boolesche Algebra Das logische Und wird durch ein Komma dargestellt: ?- true,true. true.
?- true,false. false.
?- false,true. false.
?- false,false. false. Das logische Oder wird durch ein Semikolon dargestellt:
?- true;true. true.
?- true;false. true.
?- false;true. true.
?- false;false. false.
Vergleiche ?- 3 < 4. true.
?- 3 > 4. false.
?- anton == anton. % == prüft ob das muster links und rechts übereinstimmt true.
?- 3 == 1+2. % muster stimmt nicht überein false.
?- 3 \== 1+2. % \== prüft ob das muster links und rechts nicht übereinstimmt true.
?- 3 =:= 1+2. % =:= ist der numerische Vergleich true.
?- 3 =\= 4. % =\= die numerische Ungleichheit true.
?- 3 =\= 1+2. % 3=3 false.
?- 3 =< 4. % =< bedeutet kleiner/gleich; '<=' ist nicht zulässig true.
?- 3 >= 4. % >= bedeutet größer/gleich false.
?- 4 \= 2. % \= prüft ob unifikation unmöglich ist true.
?- X + 4 \= 2 + Y. % Durch X=2 und Y=4 (unifikation) werden die muster identisch false.
Regeln % Wenn X Vater von Z ist und Z Vater von Y ist, dann ist X Großvater von Y grossvater(X, Y) :- vater(X, Z), vater(Z, Y).
% Adam ist der Vater von Tobias vater(adam, tobias).
% Tobias ist der Vater von Frank vater(tobias, frank).
% Abfrage ob Adam der Großvater von Frank ist ?- grossvater(adam, frank). true.
Symmetrische Relation % anton und berta sind ein Ehepaar ehepaar(anton, berta).
% das Ehepaar X Und Y ist genau dann ein Ehepaar, % wenn ein Ehepaar Y Und X existiert ehepaar(X, Y) :- ehepaar(Y, X).
% Abfrage ob Berta und Anton ein Ehepaar sind ?- ehepaar(berta, anton). true.
Arithmetik Prolog kennt die Grundrechenarten, also Addition +, Subtraktion -, Multiplikation *, Division / und Modulo mod. Die Zuweisung eines Wertes zu einer Variable erfolgt mit dem Schlüsselwort is. Im Gegensatz zu imperativen Programmiersprachen können in Prolog Variablenwerte nicht überschrieben werden.
Listen Listen sind rekursive Datenstrukturen bestehend aus einem Kopf () und einem Rest (). Der Rest kann hierbei wieder aus Listen bestehen. % Head ist die Zahl 1 % Tail ist die Liste [1, 2]
% Head ist die Zeichenkette 'one' % Tail ist die Liste ['two'] ['one', 'two']
% Listen können auch gemischt sein [1, 'two']
% Head ist die Ziffer 1 % Tail ist die Liste [2,3] [1, 2, 3]
Um zu prüfen ob ein bestimmtes Element in einer Liste enthalten ist, wird die vordefinierte Funktion member verwendet: member(X, [X|_]). member(X, [_|T]) :- member(X, T).
?- member(2, ['anton','berta','caesar']). false.
?- member('berta', ['anton','berta','caesar']). true.
Laden von Prolog-Texten Ein Prolog-Text kann direkt über die Konsole eingegeben werden. Dazu kann man auf der Eingabezeile [user] tippen. Die Eingabe der Klauseln muss mit einem Dateienendezeichen abgeschlossen werden (^D oder ^Z je nach Plattform):
?- [user]. % tippe Prolog-Text direkt ein append([], X, X). append([X|Y], Z, [X|T]) :- append(Y, Z, T). ^D
Alternativ kann ein Prolog-Text in einer Datei gespeichert werden und z. B. mit dem Prädikat consult geladen werden. Das Prädikat nimmt einen physischen Pfad zu einem Prolog-Text entgegen: ?- consult('append.pl'). % lade Prolog Text-aus Datei true.
Weitere Techniken Entscheidend für die Prolog-Programmierung sind die Techniken der Rekursion und die Nutzung von Listen.
Ist die Rekursion in den meisten Programmiersprachen nur eine zusätzliche Variante zur Iteration, ist sie bei der Prolog-Programmierung die einzige Möglichkeit, Schleifen zu produzieren. Benötigt man in obigem Beispiel eine allgemeine Vorfahr-Relation, wird das wie folgt realisiert (Das Semikolon zeigt in der Klausel die Disjunktion bzw. das logische „oder“):
% X ist genau dann ein elternteil von Y, % wenn X die mutter von Y ist Oder % wenn X der vater von Y ist. elternteil(X, Y) :- mutter(X, Y); vater(X, Y).
% X ist einerseits dann ein vorfahr von Z, % wenn X ein elternteil von Z ist. vorfahr(X, Z) :- elternteil(X, Z).
% X ist andererseits dann ein vorfahr von Z, wenn % X ein elternteil von Y ist Und % Y ein vorfahre von Z ist vorfahr(X, Z) :- elternteil(X, Y), vorfahr(Y, Z).
Dies lässt sich wie folgt lesen: X ist ein Vorfahr von Z, wenn X Elternteil von Z ist (Regel 1) oder es ein Y gibt, das Vorfahr von Z ist und gleichzeitig X Elternteil von Y (Regel 2) (Es wurde hier elternteil statt mutter oder vater verwendet.)
Auch Listen sind ein entscheidender Bestandteil von Prolog. Die meisten Prolog-Implementationen bringen dafür viele Basisfunktionen mit („concat“ = Anhängen von Werten, „count“ = Anzahl der Werte etc.), die sich aber auch alle selbst definieren lassen. In einer gedachten Familienstruktur muss die Anzahl der Kinder ja variabel sein. Folgendes wäre denkbar:
familie(heinz, jutta, [peter,laura]). familie(karl, gertrud, []).
Dann ließen sich z. B. mit einer Abfrage alle Männer ohne Kinder anzeigen: ?- familie(X, _, []). X=karl Dabei ist X die Variable, deren verschiedene Werte ausgegeben werden sollen. Der Unterstrich _ ist in Prolog die anonyme Variable, wodurch Prolog veranlasst wird hier jeden Wert zuzulassen. Die eckigen Klammern stehen für die leere Liste, welche die nicht vorhandenen Kinder repräsentiert.
Eine weitere Eigenschaft und Besonderheit gegenüber anderen Programmiersprachen ist, dass Prolog in der Lage ist, während der Laufzeit seine vorhandene Datenbank zu erweitern oder zu löschen. Ein Beispiel für das Löschen eines einzelnen Elements:
auto(bmw, rot). auto(bmw, blau).
autofarbe(Automarke, X) :- retract(auto(bmw, _)), auto(Automarke, X).
Die Abfrage: ?- auto(bmw, X). ergibt (anfänglich) ganz normal: X=rot; X=blau; No Die Abfrage: ?- autofarbe(bmw, X). würde beim ersten Mal: X=blau; No beim zweiten Mal nur noch: No liefern, da die Informationen:
auto(bmw, rot). auto(bmw, blau).
aus der Datenbank gelöscht wurden. Auch ?- auto(bmw,X) liefert jetzt nur noch No. Zum Löschen aller gleichen Elemente (also z. B. auto()) auf einmal benutzt man retractall(), zum Ausgeben asserta() (oben in der Datenbank) und assertz() (unten in der Datenbank).
Beispiele Lösen eines mathematischen Rätsels ABB - CD = EED
FD + EF = CE
EGD * FH = ???
A bis H stehen jeweils für eine Ziffer 0 bis 9, wobei nicht klar ist, welche Ziffer welchem Buchstaben entspricht. Gesucht ist die Zahl, die bei den Fragezeichen stehen muss. Dieses Problem ist in Prolog sehr einfach zu lösen. Man schreibt zunächst eine Regel, die bewirkt, dass A bis H später mit allen möglichen Kombinationen von 0 bis 9 belegt werden (Permutation):
gen(A,B,C,D,E,F,G,H) :- permutation([A,B,C,D,E,F,G,H,_,_], [0,1,2,3,4,5,6,7,8,9]).
Nun müssen nur die fünf entstehenden Gleichungen (ABB – CD = EED, FD + EF = CE, ABB – FD = EGD, CD – EF = FH und EED * CE = EGD * FH = X) in Prolog-Syntax geschrieben werden:
gl1(A, B, C, D, E) :- ((A * 100 + B * 10 + B) - (C * 10 + D)) =:= (E * 100 + E * 10 + D). gl2(C, D, E, F) :- ((F * 10 + D) + (E * 10 + F)) =:= (C * 10 + E). gl3(A, B, D, E, F, G) :- ((A * 100 + B * 10 + B) - (F * 10 + D)) =:= (E * 100 + G * 10 + D). gl4(C, D, E, F, H) :-