Arithmetik in Prolog Prolog beherrscht die grundlegenden arithmetischen Operationen wie Addition, Multiplikation, Division usw. Dazu werden die üblichen Zeichen verwendet. Die arithmetischen Zeichen sind lediglich Funktoren, sie führen direkt keine arithmetischen Operationen durch, z.B.: ?- X = 3+2. X = 3+2 yes ?- 3+2 = 5. no Ausdrücke wie 3+2 werden als komplexe Terme behandelt, die auch als +(3,2) dargestellt werden können, und diese matchen nicht mit Zahlen (die einfache Terme sind). Es gibt einen besonderen Operator, der Prolog dazu anweist, die Rechnungen durchzuführen. Das ist der eingebaute zweistellige Operator is/2. Das erste Argument von is ist immer eine Zahl (ein einfacher Term), nämlich das Ergebnis, das berechnet wird. Das zweite Argument sind die Ausdrücke, mit denen gerechnet werden soll; das sind in der Regel komplexe Terme, z.B. 3+2. Beispiele: ?- 5 is 5. yes ?- 7 is 5+2. yes ?- 5+2 is 7. no ?- X is 3+2*5. X = 13 Alle Variablen, die im zweiten Argument von is enthalten sind, müssen instantiiert sein. Wenn eine solche Variable nicht instantiiert ist, gibt Prolog eine Fehlermeldung aus: ?- X=3+2, Y is X. X = 3+2 Y = 5 ?- Y is X, X=3+2. ERROR: Unhandled exception: is/2: Arguments are not sufficiently instantiated Oder auch im einfacheren Fall: ?- 3+2*5 is X. ERROR: Unhandled exception: is/2: Arguments are not sufficiently instantiated Man kann mithilfe von arithmetischen Operatoren auch Prädikate definieren, z.B. addiere_2_und_multipliziere_durch_4: addiere_2_und_multipliziere_durch_4(X,Y) :- Y is (X+2)*4. Prolog beherrscht folgende Operationen mit natürlichen Zahlen (auch mit negativen): Addition: 8 is 6+2. Subtraktion: 4 is 6-2. Multiplikation: 12 is 6*2. Division: 3 is 6/2. Wenn das Ergebnis einer Division keine natürliche (d.h. ganze) Zahl ist, wird auf die nächste ganze Zahl abgerundet: 3 is 7/2. Die Zahl, die bei einer solchen Division übrig bleibt, wird durch den Funktor mod/2 berechnet: 0 is mod(6,2). (Bei 6/2 bleibt 0 übrig.) 2 is mod(10,4). (Bei 10/4 bleibt 2 übrig, nämlich 4*2 + 2 = 10.) Es gibt ferner ein Prädikat, das als Abkürzung von X is Y, X is Z dient, nämlich Y =:= Z. In diesem Fall können beide Argumente sowohl Zahlen als auch komplexe Terme sein, aber keines der beiden Argumente darf uninstantiierte Variablen enthalten. Prolog enthält auch weitere vergleichende Operatoren: X < Y. X =< Y. X =:= Y. X =\= Y. X >= Y X>Y x<y x≤y x=y x≠y x≥y x>y Wie im Fall des Gleichheitsoperators =:= können beide Argumente Zahlen wie auch komplexe Terme sein, aber sie dürfen keine uninstantiierten Variablen enthalten. Verarbeitung von Listen und Arithmetik Zählen dient für unsere Zwecke vor allem zu Verarbeitung von (rekursiven) Datenstrukturen, insbesondere von Listen. Ein relativ einfaches Beispiel: die Berechnung der Länge (d.h. der Kardinalität) von Listen. Basisfall: Die Länge der leeren Liste ist 0. Rekursive Regel: Wenn die Länge des Schwanzes einer Liste x ist, dann ist die Länge der Liste x+1. laenge([],0). laenge([_|T],N) :- laenge(T,X), N is X+1. ?- laenge([a,b,c,d,e,[a,b],g],X). X = 7 Dasselbe kann auch durch sog. Akkumulatoren erreicht werden. In Programmiersprachen werden Variablen gewöhnlich zum Speichern von vorläufigen Resultaten verwendet. Diesen Zweck erfüllen in Prolog die Akkumulatoren. Es soll nun folgendes Prädikat definiert werden: akkLaenge(Liste,Akk,Laenge) Wir geben beim Aufrufen dieses Prädikats den Wert 0 für Akk an. Das Prädikat soll dann bei der rekursiven Abarbeitung der Liste schrittweise 1 zu Akk addieren. Wenn es bei der leeren Liste angekommen ist, enthält Akk die Länge der Liste, die dann an die Variable Laenge weitergegeben werden kann. akkLaenge([_|T],A,L) :akkLaenge([],A,A). Aneu is A+1, akkLaenge(T,Aneu,L). Was ist der Unterschied zwischen diesen Prädikaten? Deklarativ gesehen sind sie äquivalent und sie liefern das gleiche Ergebnis. Prozedural arbeiten sie jedoch unterschiedlich. Bei laenge ist das rekursive Prädikat das erste Konjunkt des Rumpfes der Regel, deshalb muss die ganze Liste zuerst abgearbeitet und dann beim zweiten Konjunkt (der Addition) schrittweise quasi wiederaufgebaut werden. Bei akkLaenge wird die Liste nur einmal abgebaut und parallel dazu der Wert des zweiten Arguments jeweils um 1 erhöht. Am Ende der Liste wird der Wert des Akkumulators einfach mit dem dritten Argument gematcht, dadurch erhalten wir die Länge der Liste. Zweites Beispiel für einen Akkumulator: Wir definieren ein dreistelliges Prädikat, dessen erstes Argument eine Liste von Zahlen ist, und dessen drittes Argument die höchste Zahl aus dieser Liste ist. Das zweite Argument ist ein Akkumulator. Das Prädikat arbeitet die Liste von links nach rechts ab und merkt sich die bislang gefundene höchste Zahl im Akkumulator, die es beim Erreichen der leeren Liste als Endergebnis mit dem dritten Argument matcht. accMax([K|S],A,Max) :K > A, accMax(S,K,Max). accMax([K|S],A,Max) :K =< A, accMax(S,A,Max). accMax([],A,A). Die erste rekursive Regel sagt: wenn der Kopf der Liste eine Zahl ist, die größer ist als die Zahl im Akkumulator, dann soll diese Zahl in den Akkumulator eingetragen werden. Die zweite sagt: wenn der Kopf der Liste kleiner ist als die Zahl im Akkumulator, bleibt letzterer unverändert. Es stellt sich schließlich die Frage, mit welcher Zahl der Akkumulator in accMax initialisiert werden soll. Es bietet sich an, die erste Zahl der Liste als Startwert zu nehmen. Wir definieren zu diesem Zweck ein zweites, zweistelliges Prädikat, wo wir lediglich die Liste selbst und die Länge als Argumente angeben. Den Akkumulator setzt dieses Prädikat automatisch: max(Liste,Max) :Liste = [K|_], accMax(Liste,K,Max).