voriges Kapitel | Startseite | Inhalt | nächstes Kapitel

© 2001 François Bry
Dieses Lehrmaterial wird ausschließlich zur privaten Verwendung angeboten. Eine nichtprivate Nutzung (z.B. im Unterricht oder eine Veröffentlichung von Kopien oder Übersetzungen) dieses Lehrmaterials bedarf der Erlaubnis des Autors.


PROLOG-Hilfsprädikate und -programme


  1. Terme lesen, schreiben und klassifizieren
  2. Arithmetische Systemprädikate
  3. Schreiben in Dateien, lesen aus Dateien
  4. Memoisierung
  5. Scheiterngetriebene Schleife
  6. Quantoren ausdrücken
  7. All-Antwort-Prädikate
  8. Hilfsprogramme

1. Terme lesen, schreiben und klassifizieren

:- var(X). X = X yes. :- var(28). no (more) solution. :- var(a). no (more) solution. :- var(f(X)). no (more) solution. :- var([a, b]). no (more) solution. :- var([]). no (more) solution. :- integer(X). no (more) solution. :- integer(28). yes. :- integer(a). no (more) solution. :- integer(f(X)). no (more) solution. :- integer([a, b]). no (more) solution. :- integer([]). no (more) solution. :- atom(X). no (more) solution. :- atom(28). no (more) solution. :- atom(a). yes. :- atom(f(X)). no (more) solution. :- atom([a, b]). no (more) solution. :- atom([]). yes. :- compound(X). no (more) solution. :- compound(28). no (more) solution. :- compound(a). no (more) solution. :- compound(f(X)). X = X yes. :- compound([a, b]). yes. :- compound([]). no (more) solution. :-
Die Beziehung zwischen atomic einerseits und atom und integer andererseits kann wie folgt erläutert werden:
atomic(X) :- atom(X). atomic(X) :- integer(X).
nonvar kann wie folgt erläutert werden:
nonvar(X) :- \+ var(X).
PROLOG-Regeln können wie PROLOG-Terme behandelt werden, wobei :- als ein zweistelliges Funktionssymbol behandelt wird, das in Infixform geschrieben werden kann.
:- Regel = (p(X) :- q(X,Y), r(Y)). Regel = p(X) :- q(X,Y), r(Y) X = X Y = Y yes. :- Regel = :-(p(X), (q(X,Y), r(Y))). Regel = p(X) :- q(X,Y), r(Y) X = X Y = Y yes.
Man beachte die Klammerung des zweiten Arguments von :- und die unterschiedliche Bedeutung der Kommata: :-( p(X), (q(X,Y), r(Y)) ) ist ein PROLOG-Term mit zwei Argumenten, die durch das Komma nach p(X) getrennt werden. Das zweite Argument ist die Konjunktion von q(X,Y) mit r(Y). Das Komma nach q(X,Y) ist der Operator für die Konjunktion. Die Konjunktion q(X,Y), r(Y) muß hier geklammert werden, weil andernfalls q(X,Y) als zweites und r(Y) als drittes Argument von :- behandelt würde.
:- (Kopf :- Rumpf) = :-( p, (q, r) ). Kopf = p Rumpf = q, r yes. :- (Kopf :- Rumpf) = :-( p, q,r ). no (more) solution.
Das Systemprädikat =.. (sprich: univ) dient zur Konvertierung zwischen Termen und Listen und ist sehr nützlich für die Bearbeitung von Termen. Es kann natürlich auch auf Terme angewandt werden, die mit :- gebildet sind:
:- (p :- q, r, s) =.. [F | Argumente]. F = :- Argumente = [p, (q, r, s)] yes.
Man beachte auch hier die unterschiedliche Bedeutung der Kommata. Die Liste der Argumente besteht aus zwei Elementen, die durch das Komma nach p getrennt werden. Das zweite Element der Liste ist eine Konjunktion von drei Elementen.
Der Inhalt der Datenbank mit Regeln und Fakten kann zur Laufzeit verändert werden. Voraussetzung dafür ist, daß die Relationssymbole der hinzugefügten oder gelöschten Regeln und Fakten zuvor als dynamisch vereinbart worden sind:
:- dynamic(p/1). yes. :- listing(p/1). %%% Ausgabe aller Klauseln fuer p/1 %%% Bisher gibt es keine solche Klausel yes. :- asserta( p(X) :- q(X,_) ). X = X yes. :- listing(p/1). p(_g230) :- q(_g230, _g236). yes. :- asserta( p(a) ). yes. :- listing(p/1). p(a). p(_g230) :- q(_g230, _g236). yes. :- assert( p(Y) :- r(Y) ). Y = Y yes. :- listing(p/1). p(a). p(_g230) :- q(_g230, _g236). p(_g230) :- r(_g230). yes. :- retract( p(Arg) :- Rumpf ). Arg = a Rumpf = true More? (;) ; Arg = Arg Rumpf = q(Arg, _) More? (;) ; Arg = Arg Rumpf = r(Arg) yes. :- listing(p/1). yes. :-

zum Seitenanfang

Terme lesen, schreiben und klassifizieren


PROLOG-Regeln können wie PROLOG-Terme behandelt werden, wobei :- als ein zweistelliges Funktionssymbol behandelt wird, das in Infixform geschrieben werden kann.
:- Regel = (p(X) :- q(X,Y), r(Y)). Regel = p(X) :- q(X,Y), r(Y) X = X Y = Y yes. :- Regel = :-(p(X), (q(X,Y), r(Y))). Regel = p(X) :- q(X,Y), r(Y) X = X Y = Y yes.
Das Systemprädikat =.. (sprich: univ) dient zur Konvertierung zwischen Termen und Listen und ist sehr nützlich für die Bearbeitung von Termen. Es kann natürlich auch auf Terme angewandt werden, die mit :- gebildet sind:
:- (p :- q, r, s) =.. [F | Argumente]. F = :- Argumente = [p, (q, r, s)] yes.

Arithmetische Systemprädikate


Ein Term wie 2 + 3 oder +(2,3) steht in Prolog "für sich selbst", da Funktionssymbole normalerweise nicht ausgewertet werden. Einige wenige Systemprädikate werten ihre Argumente dagegen doch aus und bilden insofern eine Ausnahme. Voraussetzung ist, daß die Argumente zulässige arithmetische Terme sind.
:- X = +(2, 3). %%% Keine Seite wird ausgewertet X = 2 + 3 yes. :- X is 2 + 3. %%% rechte Seite wird ausgewertet X = 5 yes. :- X is +(2, 3). X = 5 yes.
Weitere arithmetische Systemprädikate wie < > >= =< und + - * / sind analog definiert.

Schreiben in Dateien, lesen aus Dateien (1)


Die Systemprädikate tell und told leiten die Ausgabe des PROLOG-Systemes in eine Datei um:
:- tell(sitzung). yes. :- [user]. p(X) :- q(X), r(X). p(a). p(b). yes. :- listing(p/1). yes. :- told. :- listing(p/1). p(_g678) :- q(_g678), r(_g678). p(a). p(b). :- halt. bye

Schreiben in Dateien, lesen aus Dateien (2)


Jedes PROLOG-System gibt die Möglichkeit, Programme aus Dateien zu laden:
tahiti(bry): % cat testprogramm member(M, [M|_]). member(M, [_|R]) :- member(M, R). list_of_integer([]). list_of_integer([H|T]) :- integer(H), list_of_integer(T). tahiti(bry):~ % eclipse ECRC Common Logic Programming System [sepia opium megalog] Version 3.4.5, Copyright ECRC GmbH, Thu Jun 16 11:08 1994 [eclipse 1]: compile(testprogramm). testprogramm compiled traceable 436 bytes in 0.00 seconds yes. [eclipse 2]: member(X, [1, 2, 3, 4]). X = 1 More? (;) ; X = 2 More? (;) yes. [eclipse 3]: list_of_integer([1, 2, 3, 4]). yes. [eclipse 4]:

Memoisierung


Die Idee der Memoisierung besteht darin, Zwischenergebnisse zu speichern und deren wiederholte Auswertung zu vermeiden. Weil eine Anfrage mehrere Antworten haben kann, erweist sich die Memoisierung in der Logikprogrammierung als komplexer als in der funktionalen Programmierung.
Das folgende Programm berechnet die n-te Fibonacci-Zahl, unter Einsatz der Memoisierung:
39
?- dynamic(memo_fib/2). memo_fib(0, 1). memo_fib(1, 1). memo_fib(N, F) :- N > 1, N1 is N - 1, N2 is N - 2, memo_fib(N1, F1), memo_fib(N2, F2), F is F1 + F2, asserta( (memo_fib(N, F) :- !) ).
Das Prinzip der Memoisierung kann wie folgt erläutert werden:
lemma(Anfrage) :- Anfrage, asserta( (Anfrage :- !) ).

Scheiterngetriebene Schleife


PROLOG bietet zwei Möglichkeiten, eine Schleife zu implementieren: per Rekursion oder per Scheitern. Das obige Fibonacci-Programm ist ein Beispiel einer Schleife per Rekursion.
Das folgende Programm erläutert das Prinzip der "Scheitern-getriebenen Schleifen":
40
p(a). p(b). p(c). p(d). p(e). schreib_alle_p :- p(X), write(X), nl, fail. schreib_alle_p.
Die folgende Sitzung bezieht sich auf Programm P40:
:- schreib_alle_p. a b c d e yes. :-

Quantoren ausdrücken


Die folgende Sitzung bezieht sich auf Programm P41:
:- mitarbeiter(M), not spricht(M, franzoesisch). M = marion More? (;) ; M = tim yes. :- exists(M, (mitarbeiter(M), not spricht(M, franzoesisch))). M = M yes. :- all(M, mitarbeiter(M) => spricht(M, deutsch)). M = M yes. :- all(M, mitarbeiter(M) => spricht(M, franzoesisch)). no (more) solution. :-

7. All-Antwort-Prädikate

Das folgende Programm verwendet die Technik der "Scheitern-getriebenen Schleife", um alle Antworten zu einer Anfrage zu generieren und zu sammeln. Dabei werden die Systemprädikate asserta und retract verwendet, um die Zwischenergebnisse zu speichern:
Das Cut in sammeln vermeidet mehrfache Antworten mit Teilergebnissen.
Die folgende Sitzung bezieht sich auf Programm P42:
:- alle(X, member(X, [a,b,c,c,d]), L). X = X L = [a, b, c, c, d] yes. :- alle(X, member(X, []), L). X = X L = [] yes. :-
Es stellen sich aber einige Schwierigkeiten:
:- [user]. mitarbeiter(norbert). mitarbeiter(marion). mitarbeiter(slim). mitarbeiter(tim). spricht(norbert, franzoesisch). spricht(slim, franzoesisch). spricht(X, deutsch). yes. :- alle(X, spricht(X, _), L). X = X L = [norbert, slim, X] yes. :- menge(X, spricht(X, _), L). X = X L = [norbert, slim] yes. :-
Offenbar, hängt das Ergebnis des Aufrufes menge(X, spricht(X, _), L). von der Reihenfolge der Definition für spricht ab, wie die folgende Sitzung zeigt:
:- [user]. spricht(X, deutsch). spricht(norbert, franzoesisch). spricht(slim, franzoesisch). yes. :- alle(X, spricht(X, _), L). X = X L = [X, norbert, slim] yes. :- menge(X, spricht(X, _), L). X = X L = [X] yes. :-
Die Programme P42 und P43 sind zu grob für einen Einsatz in der Praxis, weil sie mehrere Aspekte nicht berücksichtigen:
  • Die Pufferverwaltung ist unzureichend im Falle eines rekursiven Aurfufs von alle.
  • Sind die aufzusammelnden Terme nicht grund, so erweist sich member als unzureichend.
  • Können die aufzusammelnden Terme nicht grund sein, so muß das All-Antwort-Prädikat näher spezifiziert werden, je nach dem, welcher Effekt gewünscht ist.
Jedes PROLOG-System bietet zuverlässige und effiziente Implementierungen von verschiedenen All-Antwort-Prädikaten. Sei hier als Beispiel dafür ein Auszug aus dem Manual des PROLOG-Systems Eclipse gegeben:
[eclipse 1]: help(bagof/3). bagof(?Term, +Goal, ?List) Succeeds if List is the (non-empty) list of all instances of Term such that Goal is provable. ... [eclipse 2]: help(coverof/3). coverof(?Term, +Goal, ?List) Succeeds if List is the (non-empty) list of all the most general instances of Term such that Goal is provable. ... [eclipse 3]: help(findall/3). findall(?Term, +Goal, ?List) List is the (possibly empty) list of all instances of Term such that Goal is provable. ... [eclipse 4]: help(setof/3). setof(?Term, +Goal, ?List) Succeeds if List is the (non-empty) ordered list of all instances of Term such that Goal is provable. ...
Es ist bemerkenswert, daß einige dieser All-Antwort-Prädikate leere Listen liefern können, andere aber scheitern, wenn die auszuwertende Anfrage keine Antwort bekommt.

zum Seitenanfang

All-Antwort-Prädikate


Programm P42 verwendet die Technik der "Scheitern-getriebenen Schleife", um alle Antworten zu einer Anfrage zu generieren und zu sammeln. Dabei werden die Systemprädikate asserta und retract verwendet, um die Zwischenergebnisse zu speichern:
Das Cut in sammeln vermeidet mehrfache Antworten mit Teilergebnissen.
Das Programm P42 ist zu grob für einen Einsatz in der Praxis, weil es mehrere Aspekte nicht berücksichtigt:
  • Die Pufferverwaltung ist unzureichend im Falle eines rekursiven Aurfufs von alle.
  • Sind die aufzusammelnden Terme nicht grund, so erweist sich member als unzureichend.
  • Können die aufzusammelnden Terme nicht grund sein, so muß das All-Antwort-Prädikat näher spezifiziert werden, je nach dem, welcher Effekt gewünscht ist.

Hilfsprogramme


siehe Programm P44
voriges Kapitel | Startseite | Inhalt | nächstes Kapitel

Letzte Änderung: 18. August 1998

Valid
	     XHTML 1.0!