9: Terme
Vergleich von Termen
Der Gleichheitsoperator für Terme „==“ vergleicht zwei Terme auf Gleichheit.
?- a == a.
true.
?- X == a.
false.
?- X = a, X==a.
true.
?- 2+3 == +(2,3).
true.
?- (4>5) == >(4,5).
true.
?- [a|[b]] == ’[|]’(a,’[|]’(b,[])).
true.
?- 2+3 == 3+2.
false.
Der Ungleichheitsoperator für Terme „==“ gelingt genau dann, wenn „==“ nicht gelingt.
?- a \== a.
false.
?- a \== X.
true.
Übersicht Matching- und Vergleichsoperatoren
Operator | Negation | Vergleichstyp |
---|---|---|
= | \= | Unifikation |
=:= | =\= | arithmetische Gleichheit |
== | \== | Termgleichheit |
Analyse von nicht zusammengesetzten Termen
Mit den folgenden eingebauten Prädikaten kann man den Typ eines nicht zusammengesetzten Terms überprüfen:
Prädikat | Funktion |
---|---|
atom/1 | Testet ob das Argument ein Atom ist. |
integer/1 | Testet ob das Argument eine natürliche Zahl ist |
number/1 | Testet ob das Argument eine Zahl ist |
atomic/1 | Testet ob das Argument eine Konstante ist |
var/1 | Testet ob das Argument eine uninstantiierte Variable ist |
nonvar/1 | Testet ob das Argument keine uninstantiierte Variable ist |
?- atom(a).
true.
?- number(7.3).
true.
?- var(X).
true.
?- integer(7).
true.
?- atomic(7).
true.
?-nonvar(a).
true.
Analyse zusammengesetzter Terme
- Die Struktur eines zusammengesetzten Terms besteht aus (1) dem Funktor, (2) der Stelligkeit und (3) dem Typ der Argumente.
- Die folgenden eingebauten Prädikate ermöglichen die Analyse der Struktur zusammengesetzter Terme:
- Das Prädikat functor/3 ermöglicht den Zugriff auf den Funktor und die Stelligkeit eines komplexen Terms.
- Das Prädikat arg/3 ermöglicht den Zugriff auf einzelne Argumente eines komplexen Terms.
- Zusätzlich kann man mit dem univ genannten Prädikat „=../2“ einen komplexen Term in eine Liste umwandeln.
Testen ob ein Term zusammengesetzt ist
Wie können wir testen, ob ein Term zusammengesetzt ist?
complexterm(X):-
nonvar(X), % Variablen sind nicht zusammengesetzt
functor(X,_,A),
A > 0. % die Stelligkeit muss groesser 0 sein.
?- complexterm(X).
false.
?- complexterm(4).
false.
?- complexterm(mag(popeye,food(X))).
true.
Das Prädikat: arg/3
Das Prädikat arg/3 ermögliche den Zugriff auf einzelne Argumente eines komplexen Terms.
% arg(+Number, +ComplexTerm, ?NthArgument)
?- arg(1, mag(popeye, spinat), Argument).
Argument = popeye.
?- arg(2, mag(popeye,spinat), Argument).
Argument = spinat.
?- arg(2, essen(spinat), Argument).
false. % scheitert, da essen/1 nur ein Argument hat.
Das Prädikat arg/3 kann auch zur Instantiierung von Argumenten genutzt werden.
?- arg(1, liebt(X,olivia), popeye).
X = popeye.
Das Prädikat: functor/3
Das Prädikat functor/3 ermöglicht den Zugriff auf den Funktor und die Stelligkeit eines komplexen Terms.
% functor(+ComplexTerm, ?Functor, ?Arity)
% functor(?ComplexTerm, +Functor, +Arity)
?- functor(f(a,b),F,A). F=f A=2
?- functor(a,F,A). F=a A=0
?- functor([1,2,3],F,A). F=’[|]’ A=2
Prolog wäre nicht Prolog, wenn man das Prädikat functor/3 nicht auch zur Generierung komplexer Terme einsetzen könnte.
?- functor(T,f,4). T=f(_A,_B,_C,_D).
Allerdings muss entweder das erste oder das zweite und dritte Argument instantiiert sein:
?- functor(C,f,A).
ERROR: Arguments are not sufficiently instantiated
?- functor(C,F,3).
ERROR: Arguments are not sufficiently instantiated
Das univ-Prädikat: =../2
- Das univ genannte Prädikat =../2 ermöglicht die Umwandlung eines komplexen Terms in eine Liste und umgekehrt.
- Der Funktor des komplexen Terms wird zum ersten Element der Liste.
- Das univ-Prädikat kann auch als Infixoperator verwendet werden.
?- f(a,b,c,d) =.. X. X= [f,a,b,c,d].
?- X =.. [f,a,b,c,d].
X = f(a,b,c,d).
?- spielt(olivia,X) =.. Y, X= 20.
X = 20.
Y = [spielt, olivia, 20].
?- 6-8+9 =.. X.
X = [+,6-8,9].
Bildschirmausgabe: write_canonical/1 und write/1
Das Prädikat write_canonical/1 gibt die Struktur eines (zusammengesetzten Terms) auf dem Bildschirm aus:
?- write_canonical(5+6*3).
+(5,*(6,3))
true.
?- write_canonical(5-3 < 4+7).
<(-(5,3),+(4,7))
true.
Das Prädikat write/1 schreibt einen Term in der externen Notation auf den Bildschirm:
?- write(5+6*3).
5+6*3
true.
?- write(5-3 < 4+7).
5-3 < 4+7
true.
strukturierte Bildschirmausgabe: nl/0 und tab/1
Das Prädikat nl/0 erzeugt einen Zeilenumbruch und das Prädikat tab/1 erzeugt die angegebene Menge an Leerzeichen auf dem Bildschirm.
? - write(a),write(b),write(c),write(d).
abcd
? - write(a),nl , write(b),tab(2),write(c),tab(5),write(d).
a
b c d
Ausgabe von Bäumen: pprint/1
In Kapitel 8 haben wir gesehen, wie man einen Ableitungsbaum als komplexen Prologterm repräsentieren kann:
s(np(det(die), n(katze)), vp(v(klaut), np(det(die), n(maus))))
Das Prädikat pprint/1 nimmt als Argument einen Baum in Prolog-Term-Notation und erzeugt dazu eine Ausgabe auf dem Bildschirm, die besser lesbar ist.
% Initialisierung des Tab-Zaehlers.
pprint(Term):- pprint(Term ,0).
% Baum drucken.
pprint(Term ,N):-
Term =.. [F|Args], % Struktur zu Liste.
tab(N), write(F), nl, % Ausgabe des Mutterknotens.
N1 is N+3, % Tabulator erhoehen.
pprintl(Args,N1). % Unterbaeume drucken.
% Unterbaeume drucken.
pprintl([H|T],N):-
pprint(H,N), % Drucke eine Schwester.
pprintl(T,N). % Drucke die anderen Schwestern.
pprintl([],_). % Termination.
Operatoren – externe und interne Notation
Operatoren sind Prädikate, die eine zusätzliche nutzerfreundliche externe Notation erlauben:
interne Notation | nutzerfreundliche externe Notation |
---|---|
+(1,2) | 1+2 |
is(X,+(2,3)) | X is 2+3 |
+(8,-(2)) | 8 + -2 |
(4,1) | 4 > 1 |
==(a,a) | a == a |
=(X,a) | X = a |
Operatoren werden durch den Typ, die Priorität und die Assoziativität definiert.
Assoziativität von Operatoren
Die Assoziativität bestimmt die Klammerung der Argumente in einem Ausdruck mit mehreren Operatoren gleicher Präzedenz.
links-assoziative Operatoren fordern, dass ihr rechtes Argument eine kleinere Präzedenz hat (d.h. Prolog klammert den Ausdruck von links):
?- write_canonical(x op1 y op1 z op1 w).
op1(op1(op1(x,y),z),w)
true.
rechts-assoziative Operatoren fordern, dass ihr linkes Argument eine kleinere Präzedenz hat (d.h. Prolog klammert den Ausdruck von rechts):
?- write_canonical(x op1 y op1 z op1 w).
op1(x,op1(y,op1(z,w)))
true.
nicht-assoziative Operatoren fordern, dass beide Argumente eine kleinere
Präzedenz haben (Prolog kann solche Ausdrücke nicht klammern):
?- 2 =:= 3 == =:=(2,3).
ERROR: Syntax error: Operator priority clash
Definition eines eigenen Operators
Definition eines neuen Infixoperators in, welcher testet ob etwas Element einer Liste ist (analog zum member/2-Prädikat).
:-op(500,xfx,in).
in(X,[X|_]).
in(X,[H|T]):-
in(X,T).
% Wir können nun Anfragen wie diese stellen:
?- 5 in [3,7,w,5,l].
true.
?- k in [3,7,w,5,l].
false.
Beispiel:
Typen von Operatoren
Der Typ eines Operators bestimmt ob der Operator vor, zwischen oder nach seinen Argumenten geschrieben wird.
- Infix-Operatoren wie =, <, is, +, \== usw. sind zweistellig und werden zwischen die Argumente geschrieben (3<4).
Bsp: x Op y - Präfix-Operatoren wie -, + usw. sind einstellig und werden vor das Argument geschrieben (z.B. -3).
Bsp: Op x - Postfix-Operatoren sind einstellig und werden hinter das Argument geschrieben.
Bsp: x Op
Präzedenz von Operatoren
- Die Präzedenz eines Operators legt fest, in welcher Reihenfolge die Operatoren binden.
- Der Operator mit der höchsten Präzedenz ist der Hauptoperator eines Ausdrucks. Beispiel:
- Operatoren geordnet nach absteigender Präzedenz:
prec(op1) > prec(op2) > prec(op3)
?- write_canonical(x op2 y op3 z op1 w).
op1(op2(x,op3(y,z)),w)
true.
Hinweis: einfache Terme und Terme in Klammern haben die Präzedenz 0. Die Präzedenz von komplexen Termen wird durch die Präzedenz des Hauptoperators bestimmt.
Definition eigener Operatoren
Eigene Operatoren können definiert werden:
:-op(Praezedenz, Typ (+Assoz.), Name)
- Praezedenz ∈ {1, ….1200}
- Typ (+Assoz) ∈
- {xfx, xfy, yfx} wenn f Infix ist,
- {fx, fy} wenn f Präfix ist,
- {xf , yf } wenn f Postfix ist
- x bedeutet das die Präzedenz dieses Arguments kleiner als die des Operators ist.
- y bedeutet das die Präzedenz dieses Arguments kleiner oder gleich der des Operators ist.
- Name: Name des Operators oder Liste von Operatornamen, die alle dieselbe Eigenschaft bekommen sollen.
yfx: links-assoziativ, xfy: rechts-assoziativ, xfx: nicht-assoziativ.
Definition bestehender Operatoren
:- op( 1200, xfx, [ :-, --> ]).
:- op( 1200, fx, [ ?- ]).
:- op( 1100, xfy, [ ; ]).
:- op( 1000, xfy, [ ’,’ ]).
:- op( 700, xfx, [ =, is, =.., ==, \==, =:=, =\=, <, >, =<, >= ]).
:- op( 500, yfx, [ +, -]).
:- op( 200, fx, [ +, - ]).
:- op( 400, yfx, [*,/,mod]).
Zusammenfassung Kapitel 9
- Wir haben verschiedene Prädikate zur Analyse vor zusammengesetzten Termen kennengelernt:
- functor/3
- arg/3
- =../2 (univ)
- Wir haben gesehen, wie wir verschiene Ausgaben auf dem Bildschirm erzeugen können und damit ein Prädikat pprint/1 zur Ausgabe von Bäumen definiert.
- write_canonical/1 und write/1
- nl/0 und tab/1
- Wir haben gesehen, wie Operatoren definiert werden und die zentralen Eigenschaften von Operatoren kennengelernt:
- Typ
- Präzedenz
- Assoziativität
- Keywords: functor/3, arg/3, =../2, pprint/1, Operatoren