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

OperatorNegationVergleichstyp
=\=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ädikatFunktion
atom/1Testet ob das Argument ein Atom ist.
integer/1Testet ob das Argument eine natürliche Zahl ist
number/1Testet ob das Argument eine Zahl ist
atomic/1Testet ob das Argument eine Konstante ist
var/1Testet ob das Argument eine uninstantiierte Variable ist
nonvar/1Testet 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