2. Ein grundlegender vorwärtsschließender Auswerter zur Herleitung von
begründeten Fakten aus monotonen Rechtfertigungen
Der in diesem Absatz eingeführte Auswerter ist für
variablenfreie Fakten und bereichsbeschränkte, monotone
Rechtfertigungen (d.h. Regeln) gedacht.
Jedes hergeleitete Faktum F
hat eine oder mehrere
"Begründungen". Eine Begründung ist eine Liste aller Fakten, die zur
Herleitung von F
beigetragen haben. Ein Faktum zusammen mit
einer Begründung nennen wir ein begründetes Faktum. Die begründeten Fakten
werden in der PROLOG-Datenbank gespeichert durch PROLOG-Fakten der Form
begruendet(F,Begruendung)
, wobei es mehrere begründete Fakten
für dasselbe F
geben kann. Jedes Basisfaktum F
,
dargestellt in der Form [] ---> F
, führt dabei zu einem
begründeten Faktum begruendet(F,[])
.
80
:- op(1200, xfx, --->), dynamic(begruendet/2).
auswerten :-
(
(Rumpf ---> Kopf),
maplist(plusify, Atome, Rumpf),
beweisen(Atome, Rumpf_Begr),
einfuegen(Atome, Rumpf_Begr, Kopf_Begr),
\+ begruendet(Kopf, Kopf_Begr)
->
assert(begruendet(Kopf, Kopf_Begr)),
auswerten
;
true
).
beweisen(Atome, Begruendung) :-
maplist(begruendet, Atome, Begruendungen),
flatten(Begruendungen, Begruendung).
einfuegen(Atome, Liste, Neue_Liste) :-
append(Atome, Liste, Neue_Liste).
plusify(X, +X).
Das Prädikat maplist(+Pred, ?List1, ?List2)
wendet dabei
Pred
auf alle aufeinanderfolgenden Paare von Elementen aus
List1
und List2 an
; es scheitert, falls
Pred
für eines der Paare scheitert.
Mittels diesem Prädikat
kann auch in Prolog ein higher-order-Programmierstil verwendet werden.
Das Prädikat flatten(+List1, -List2)
transformiert die Liste
List1
, die auch selber wieder Listen als Argumente enthalten kann,
in eine "flache" Liste List2
, indem jede Liste rekursiv durch
deren Elemente ersetzt wird.
Das folgende Programm
P81 beseitigt diese Nachteile von
Programm
P80: Es liefert die Antwort.
Durch Rücksetzen werden alle möglichen Begründungen für eine gleiche Antwort
berechnet.
81
:- op(1200, xfx, --->), dynamic(begruendet/2), dynamic(anfrage/1).
auswerten(Anfrage) :-
retractall(anfrage(_)),
assert(anfrage(Anfrage)),
hilfs_auswerten(Kopf, Kopf_Begr),
antwort_reicht(Kopf, Kopf_Begr).
hilfs_auswerten(AKopf, AKopf_Begr) :-
(
(Rumpf ---> Kopf),
maplist(plusify, Atome, Rumpf),
beweisen(Atome, Rumpf_Begr),
einfuegen(Atome, Rumpf_Begr, Kopf_Begr),
\+ begruendet(Kopf, Kopf_Begr)
->
assert(begruendet(Kopf, Kopf_Begr)),
(
anfrage(Kopf),
AKopf=Kopf,
AKopf_Begr=Kopf_Begr
;
hilfs_auswerten(AKopf, AKopf_Begr)
)
;
fail
).
beweisen(Atome, Begruendung) :-
maplist(begruendet, Atome, Begruendungen),
flatten(Begruendungen, Begruendung).
einfuegen(Atome, Liste, Neue_Liste) :-
append(Atome, Liste, Neue_Liste).
plusify(X,+X).
antwort_reicht(Atom, Begruendung) :-
anfrage(Atom),
write('Antwort: '), write(Atom), nl,
write('Begruendung: '), write(Begruendung), nl,
write('weiter? (ja./nein.) '),
read(Eingabe),
Eingabe == nein.
Die folgende Sitzung bezieht sich auf Programm
P81
:- [user].
[] ---> r(a, b).
[] ---> r(b, a).
[+t(X, Y), +t(Y, Z)] ---> t(X, Z).
[+r(X, Y)] ---> t(X, Y).
Yes
:- auswerten(t(a, a)).
Antwort: t(a, a)
Begruendung: [t(a, b), t(b, a), r(a, b), r(b, a)]
weiter? (ja./nein.) ja.
Antwort: t(a, a)
Begruendung: [t(a, b), t(b, a), r(a, b), t(b, a),
t(a, a), r(b, a), t(a, b), t(b, a), r(a, b), r(b, a)]
weiter? (ja./nein.) ja.
Antwort: t(a, a)
Begruendung: [t(a, b), t(b, a), r(a, b), t(b, a),
t(a, a), r(b, a), t(a, b), t(b, a), r(a, b), t(b, a),
t(a, a), r(b, a), t(a, b), t(b, a), r(a, b), r(b, a)]
weiter? (ja./nein.) nein.
Yes
Programm
P82 beseitigt diesen Nachteil von
P81. Die mit
%
markierten Zeilen
\+ memberchk(Kopf, Kopf_Begr), %
sort(Zwischen_Liste, Neue_Liste). %
stellen die einzige Unterschiede zwischen
P82 und
P81 dar. Der Aufruf des
Prolog-Systemprädikats
sort/2
hat zur Folge,
daß die Begründungen sortiert sowie Duplikate aus der
Begründung beseitigt werden.
82
:- op(1200, xfx, --->), dynamic(begruendet/2), dynamic(anfrage/1).
auswerten(Anfrage) :-
retractall(anfrage(_)),
assert(anfrage(Anfrage)),
hilfs_auswerten(Kopf, Kopf_Begr),
antwort_reicht(Kopf, Kopf_Begr).
hilfs_auswerten(AKopf, AKopf_Begr) :-
(
(Rumpf ---> Kopf),
maplist(plusify, Atome, Rumpf),
beweisen(Atome, Rumpf_Begr),
einfuegen(Atome, Rumpf_Begr, Kopf_Begr),
\+ memberchk(Kopf, Kopf_Begr), %
\+ begruendet(Kopf, Kopf_Begr)
->
assert(begruendet(Kopf, Kopf_Begr)),
(
anfrage(Kopf),
AKopf=Kopf,
AKopf_Begr=Kopf_Begr
;
hilfs_auswerten(AKopf, AKopf_Begr)
)
;
fail
).
beweisen(Atome, Begruendung) :-
maplist(begruendet, Atome ,Begruendungen),
flatten(Begruendungen, Begruendung).
einfuegen(Atome, Liste, Neue_Liste_Sortiert) :-
append(Atome, Liste, Neue_Liste),
sort(Neue_Liste, Neue_Liste_Sortiert). %
plusify(X, +X).
antwort_reicht(Atom, Begruendung) :-
write('Antwort: '), write(Atom), nl,
write('Begruendung: '), write(Begruendung), nl,
write('weiter? (ja./nein.) '),
read(Eingabe),
Eingabe == nein.
Die folgende Sitzung bezieht sich auf Programm
P82:
:- [user].
[] ---> r(a, b).
[] ---> r(b, a).
[+t(X, Y), +t(Y, Z)] ---> t(X, Z).
[+r(X, Y)] ---> t(X, Y).
Yes
:- auswerten(t(a, a)).
Antwort: t(a, a)
Begruendung: [r(a, b), r(b, a), t(a, b), t(b, a)]
weiter? (ja./nein.) ja.
No
Die folgende Sitzung zeigt, wie mehrere verschiedene Begründungen für eine
gleiche Antwort erhalten werden können:
:- [user].
[] ---> student(anna).
[] ---> student(berndt).
[] ---> hoert(anna, prolog).
[] ---> hoert(anna, ml).
[+student(X), +hoert(X, prolog)] ---> mag(X, logik).
[+student(X), +hoert(X, ml)] ---> mag(X, logik).
Yes
:- auswerten(mag(X, logik)).
Antwort: mag(anna, logik)
Begruendung: [hoert(anna, prolog), student(anna)]
weiter? (ja./nein.) ja.
Antwort: mag(anna, logik)
Begruendung: [hoert(anna, ml), student(anna)]
weiter? (ja./nein.) ja.
No
zum Seitenanfang
Dabei wird üblicherweise angenommen, daß die Regeln bereichsbeschränkt und die Fakten variablenfrei sind.
Das Beweisverfahren steht im Grunde genommen nicht fest. Oft wird das Vorwärtsschließen angewendet.
In diesem Kapitel behandeln wir Reason-Maintenance-Systeme, die als allgemeines Wissen bereichsbeschränkte Regeln und als spezielles Wissen variablenfreie Fakten voraussetzen. Als Beweisverfahren wird das Vorwärtsschließen angewendet.