Cut und Ablaufsteuerung
Das Cut Goal mit Beispielen Negation als Failure
Probleme mit Cut und Negation
Das Cut Goal mit Beispielen Negation als Failure
Probleme mit Cut und Negation
Cut Einführung
Bislang war die Kontrolle der Abarbeitung nur durch die Ordnung der Klauseln beeinflussbar.
Der Cut: '!'
● verhindert Backtracking
● erhöht die Ausdruckskraft der Sprache mit der Negation als Failure
Zum Verhindern von Backtracking:
Automatisches Backtracking ist an sich sehr nützlich. Es nimmt die Last vom Programmierer Backtracking explizit zu implementieren.
Unkontrolliertes Backtracking kann allerdings in manchen Fällen zur Ineffizienz führen.
→ Manchmal muss Backtracking verhindert oder kontrolliert werden.
Bislang war die Kontrolle der Abarbeitung nur durch die Ordnung der Klauseln beeinflussbar.
Der Cut: '!'
● verhindert Backtracking
● erhöht die Ausdruckskraft der Sprache mit der Negation als Failure
Zum Verhindern von Backtracking:
Automatisches Backtracking ist an sich sehr nützlich. Es nimmt die Last vom Programmierer Backtracking explizit zu implementieren.
Unkontrolliertes Backtracking kann allerdings in manchen Fällen zur Ineffizienz führen.
→ Manchmal muss Backtracking verhindert oder kontrolliert werden.
Ein erstes Beispiel
Implementation einer Sprungfunktion.
Wenn X < 3 dann Y = 0 Wenn 3 ≤und X < 6 dann Y = 2 Wenn 6 ≤ X dann Y = 4
In Prolog als binäre Relation f(X,Y)
Implementation einer Sprungfunktion.
Wenn X < 3 dann Y = 0 Wenn 3 ≤und X < 6 dann Y = 2 Wenn 6 ≤ X dann Y = 4
In Prolog als binäre Relation f(X,Y)
3
6
2 4
Ein erstes Beispiel
Implementation einer Sprungfunktion.
f(X,0) :- X < 3. %Regel 1 f(X,2) :- 3=< X, X < 6. %Regel 2 f(X,4) :- 6 = < X. %Regel 3 Experiment 1: mit zwei Zielen
?- f(1,Y), 2<Y.
→ zunächst macht die erste Regel f(X,0), X =1, 1<3 und Y wird 0 erstes Ziel erfüllt, das zweite Ziel scheitert. 2 < 0 → fail
→ Prolog versucht nun Regel 2 und Regel 3.
Die drei Regeln der Relation f(X,Y) sind jedoch gegenseitig
exklusiv. Wenn eine schon erfüllt war macht es keinen Sinn, die anderen noch auszuprobieren!
Implementation einer Sprungfunktion.
f(X,0) :- X < 3. %Regel 1 f(X,2) :- 3=< X, X < 6. %Regel 2 f(X,4) :- 6 = < X. %Regel 3 Experiment 1: mit zwei Zielen
?- f(1,Y), 2<Y.
→ zunächst macht die erste Regel f(X,0), X =1, 1<3 und Y wird 0 erstes Ziel erfüllt, das zweite Ziel scheitert. 2 < 0 → fail
→ Prolog versucht nun Regel 2 und Regel 3.
Die drei Regeln der Relation f(X,Y) sind jedoch gegenseitig
exklusiv. Wenn eine schon erfüllt war macht es keinen Sinn, die anderen noch auszuprobieren!
Ein erstes Beispiel
Mit dem Cut kann dieses nutzlose Backtracking verhindert werden:
Er wird eingesetzt sobald einer der sich gegenseitig ausschließenden Fälle eintritt.
Implementation der Sprungfunktion mit Cut.
f(X,0) :- X < 3, !. %Regel 1 f(X,2) :- 3=< X, X < 6, !. %Regel 2 f(X,4) :- 6 = < X. %Regel 3
Prolog produziert dieselbe Abarbeitung wie vorher und mit dem Zweiten Ziel an 2<0, da Y mit 0 instantiert wurde. Nun wird
Backtracking versucht. Da der Cut für f(X,0) erreicht wurde, können keine Alternativen mehr innerhalb der rechten Regelseite erreicht werden und auch der Regelkopf ist nicht mehr Alternativen
zugänglich. Der Zustand bis zum Cut wird eingefrohren.
Mit dem Cut kann dieses nutzlose Backtracking verhindert werden:
Er wird eingesetzt sobald einer der sich gegenseitig ausschließenden Fälle eintritt.
Implementation der Sprungfunktion mit Cut.
f(X,0) :- X < 3, !. %Regel 1 f(X,2) :- 3=< X, X < 6, !. %Regel 2 f(X,4) :- 6 = < X. %Regel 3
Prolog produziert dieselbe Abarbeitung wie vorher und mit dem Zweiten Ziel an 2<0, da Y mit 0 instantiert wurde. Nun wird
Backtracking versucht. Da der Cut für f(X,0) erreicht wurde, können keine Alternativen mehr innerhalb der rechten Regelseite erreicht werden und auch der Regelkopf ist nicht mehr Alternativen
zugänglich. Der Zustand bis zum Cut wird eingefrohren.
Ein erstes Beispiel: Trace
Aufruftrace von ?- f(1,Y), 2<Y. ohne Cut
[trace] ?- f(1,Y),2<Y.
Call: (7) f(1, _G807) ? creep Call: (8) 1<3 ? creep
Exit: (8) 1<3 ? creep
Exit: (7) f(1, 0) ? creep %goal 1 erfüllt Call: (7) 2<0 ? creep
Fail: (7) 2<0 ? creep
Redo: (7) f(1, _G807) ? creep %Versuch goal 1 anders zu lösen Call: (8) 3=<1 ? creep
Fail: (8) 3=<1 ? creep
Redo: (7) f(1, _G807) ? creep %letzter Versuch Call: (8) 6=<1 ? creep
Fail: (8) 6=<1 ? creep
Fail: (7) f(1, _G807) ? creep false.
Aufruftrace von ?- f(1,Y), 2<Y. ohne Cut
[trace] ?- f(1,Y),2<Y.
Call: (7) f(1, _G807) ? creep Call: (8) 1<3 ? creep
Exit: (8) 1<3 ? creep
Exit: (7) f(1, 0) ? creep %goal 1 erfüllt Call: (7) 2<0 ? creep
Fail: (7) 2<0 ? creep
Redo: (7) f(1, _G807) ? creep %Versuch goal 1 anders zu lösen Call: (8) 3=<1 ? creep
Fail: (8) 3=<1 ? creep
Redo: (7) f(1, _G807) ? creep %letzter Versuch Call: (8) 6=<1 ? creep
Fail: (8) 6=<1 ? creep
Fail: (7) f(1, _G807) ? creep false.
Ein erstes Beispiel: Trace
Aufruftrace von ?- f(1,Y), 2<Y. mit Cut
?- f(1,Y),2<Y.
Call: (7) f(1, _G3205) ? creep Call: (8) 1<3 ? creep
Exit: (8) 1<3 ? creep %goal 1 erfüllt Exit: (7) f(1, 0) ? creep
Call: (7) 2<0 ? creep
Fail: (7) 2<0 ? creep %kein Backtracking false. %nach Scheitern
Aufruftrace von ?- f(1,Y), 2<Y. mit Cut
?- f(1,Y),2<Y.
Call: (7) f(1, _G3205) ? creep Call: (8) 1<3 ? creep
Exit: (8) 1<3 ? creep %goal 1 erfüllt Exit: (7) f(1, 0) ? creep
Call: (7) 2<0 ? creep
Fail: (7) 2<0 ? creep %kein Backtracking false. %nach Scheitern
Ein erstes Beispiel
Erstes Ergebnis:
Wir haben die Effizienz des Programms durch das Einfügen eines Cuts verbessert. Wenn wir die Cuts wieder entfernen bleibt das Resultat gleich = grüner Cut.
Die prozedurale Bedeutung des Programms bleibt gleich.
Dies ist nicht immer so.
Erstes Ergebnis:
Wir haben die Effizienz des Programms durch das Einfügen eines Cuts verbessert. Wenn wir die Cuts wieder entfernen bleibt das Resultat gleich = grüner Cut.
Die prozedurale Bedeutung des Programms bleibt gleich.
Dies ist nicht immer so.
Ein erstes Beispiel
Zweites Experiment:
?-f(7,Y).
Regel 1: fail 7 < 3, Cut nicht erreicht also Backtracking über den Regelkopf
Regel 2: fail, zwar gilt 3 ≤ X aber fail 7 < 6 Regel 3: 6 ≤ 7 erfolgreich.
Eine weitere Quelle der Inefzienz.
Wenn wir schon wissen: X < 3 fail, dann gilt natürlich 3 ≤ X
Wenn wir schon wissen: X < 6 fail, dann gilt natürlich 6 ≤ X
"if, elsif, else Konstruktion"
Zweites Experiment:
?-f(7,Y).
Regel 1: fail 7 < 3, Cut nicht erreicht also Backtracking über den Regelkopf
Regel 2: fail, zwar gilt 3 ≤ X aber fail 7 < 6 Regel 3: 6 ≤ 7 erfolgreich.
Eine weitere Quelle der Inefzienz.
Wenn wir schon wissen: X < 3 fail, dann gilt natürlich 3 ≤ X
Wenn wir schon wissen: X < 6 fail, dann gilt natürlich 6 ≤ X
"if, elsif, else Konstruktion"
Ein erstes Beispiel
f(X,0) :- X < 3, !. %Regel 1 f(X,2) :- X < 6, !. %Regel 2 f(X,4). %Regel 3
Diese Version ist effizienter als die beiden Ersten und produziert dieselben Resultate. Was aber wenn wir die Cuts entfernen.
?- f(1,Y).
f(X,0) :- X < 3, !. %Regel 1 f(X,2) :- X < 6, !. %Regel 2 f(X,4). %Regel 3
Diese Version ist effizienter als die beiden Ersten und produziert dieselben Resultate. Was aber wenn wir die Cuts entfernen.
?- f(1,Y).
Ein erstes Beispiel
f(X,0) :- X < 3. %Regel 1 ohne Cut f(X,2) :- X < 6. %Regel 2 ohne Cut f(X,4). %Regel 3
?- f(1,Y).
Y=0;
Y=2;
Y=4;no.
Nicht nur ist der prozedurale Ablauf verändert sondern auch das Resultat ist unterschiedlich.
f(X,0) :- X < 3. %Regel 1 ohne Cut f(X,2) :- X < 6. %Regel 2 ohne Cut f(X,4). %Regel 3
?- f(1,Y).
Y=0;
Y=2;
Y=4;no.
Nicht nur ist der prozedurale Ablauf verändert sondern auch das Resultat ist unterschiedlich.
Präzise Bedeutung des Cut
Das Parent-Goal sei das Goal das mit dem Kopf der Klausel
gematcht hatte, die den Cut enthält. Wenn der Cut vom Stack der Zielliste geholt wird, ist er sofort erfüllt. Das System wird auf alle Entscheidungen festgelegt, seit das Parent-Goal aufgerufen wurde und bis der Cut eingetreten ist. Alle Alternativen zwischen dem Parent-Goal und dem Cut können nicht aufgerufen werden.
Beispiel:
H:- B1,B2, ...., Bm,!,..., Bn.
Wurde von einem Parent-Goal G, das mit dem Regelkopf H matcht aufgerufen. Wenn der Cut erreicht wird, hat das System schon
Lösungen für B1,B2...Bm gefunden. Auch ist der Aufrufer G auf diese Klausel festgelegt. Versuche nun G mit irgendeinem anderen Klauselkopf zu matchen werden durch den Cut verhindert.
Das Parent-Goal sei das Goal das mit dem Kopf der Klausel
gematcht hatte, die den Cut enthält. Wenn der Cut vom Stack der Zielliste geholt wird, ist er sofort erfüllt. Das System wird auf alle Entscheidungen festgelegt, seit das Parent-Goal aufgerufen wurde und bis der Cut eingetreten ist. Alle Alternativen zwischen dem Parent-Goal und dem Cut können nicht aufgerufen werden.
Beispiel:
H:- B1,B2, ...., Bm,!,..., Bn.
Wurde von einem Parent-Goal G, das mit dem Regelkopf H matcht aufgerufen. Wenn der Cut erreicht wird, hat das System schon
Lösungen für B1,B2...Bm gefunden. Auch ist der Aufrufer G auf diese Klausel festgelegt. Versuche nun G mit irgendeinem anderen Klauselkopf zu matchen werden durch den Cut verhindert.
Weiteres formales Beispiel
Programmfragment.
C:- P,Q,R,!,S,T,U.
C:- V.
A:- B,C,D.
Anfrage A.
?- A.
Sei B erfüllt, läuft die Abarbeitung der Zielliste auf C. Die erste
Regel matcht. Wenn der Cut erreicht wird sind alle Alternativen von P, Q und R abgeschnitten. Ebenso kann nicht etwa die zweite Regel mit C als Kopf von Anfrage A aus erreicht werden. Alternativen für S,T und U sind jedoch nach wie vor offen.
Programmfragment.
C:- P,Q,R,!,S,T,U.
C:- V.
A:- B,C,D.
Anfrage A.
?- A.
Sei B erfüllt, läuft die Abarbeitung der Zielliste auf C. Die erste
Regel matcht. Wenn der Cut erreicht wird sind alle Alternativen von P, Q und R abgeschnitten. Ebenso kann nicht etwa die zweite Regel mit C als Kopf von Anfrage A aus erreicht werden. Alternativen für S,T und U sind jedoch nach wie vor offen.
Weitere Beispiele zum Cut
Berechnung des Maximum max(X,Y,Max)
X ist das Maximum wenn es größer oder gleich Y ist. Y ist das Maximum wenn X kleiner Y ist.
max(X,Y,X) :- X >= Y.
max(X,Y,Y) :- X < Y.
Regeln sind gegenseitig ausschließend (Fallunterscheidung). Wenn die erste erfolgreich ist, wird die zweite scheitern. Wenn die erste gescheitert ist wird die zweite erfolgreich sein.
Wenn X größer oder gleich Y ist, ist X das Maximum, sonst Y.
Berechnung des Maximum max(X,Y,Max)
X ist das Maximum wenn es größer oder gleich Y ist. Y ist das Maximum wenn X kleiner Y ist.
max(X,Y,X) :- X >= Y.
max(X,Y,Y) :- X < Y.
Regeln sind gegenseitig ausschließend (Fallunterscheidung). Wenn die erste erfolgreich ist, wird die zweite scheitern. Wenn die erste gescheitert ist wird die zweite erfolgreich sein.
Wenn X größer oder gleich Y ist, ist X das Maximum, sonst Y.
Weitere Beispiele zum Cut
Alternative Formulierung mit Cut.
max(X,Y,X) :- X >= Y,!.
max(X,Y,Y).
Allerdings Vorsicht. Das Argument Max bei der Anfrage sollte unbelegt sein:
?- max(3,1,1).
yes. Warum? Regelkopf von max Regel 1 matcht nicht, also wird die Regel nicht aufgerufen, der Cut wird nicht erreicht, die zweite Regel kann noch feuern.
Reformulierung in eine Regel mit oder:
max(X,Y,Max) :- X >= Y,!, Max = X ; Max = Y.
?- max(3,1,1).no. Warum? Cut wird erreicht. Blockiert den oder Aufruf
Alternative Formulierung mit Cut.
max(X,Y,X) :- X >= Y,!.
max(X,Y,Y).
Allerdings Vorsicht. Das Argument Max bei der Anfrage sollte unbelegt sein:
?- max(3,1,1).
yes. Warum? Regelkopf von max Regel 1 matcht nicht, also wird die Regel nicht aufgerufen, der Cut wird nicht erreicht, die zweite Regel kann noch feuern.
Reformulierung in eine Regel mit oder:
max(X,Y,Max) :- X >= Y,!, Max = X ; Max = Y.
?- max(3,1,1).no. Warum? Cut wird erreicht. Blockiert den oder Aufruf
Weitere Beispiele zum Cut
Member Prädikat mit eindeutiger Lösung member(X,L).
Wurde benutzt um festzustellen, ob X in der Liste L enthalten ist.
member(X,[X|_]).
member(X,[_|L]):- member(X,L).
Das Prädikat ist nicht-deterministisch, wenn X mehrere male in der Liste auftaucht, können durch Backtracking unterschiedliche
Lösungen erreicht werden.
Jetzt soll member in ein Prädikat umgeschrieben werden, das immer nur das erste Auftreten findet.
Member Prädikat mit eindeutiger Lösung member(X,L).
Wurde benutzt um festzustellen, ob X in der Liste L enthalten ist.
member(X,[X|_]).
member(X,[_|L]):- member(X,L).
Das Prädikat ist nicht-deterministisch, wenn X mehrere male in der Liste auftaucht, können durch Backtracking unterschiedliche
Lösungen erreicht werden.
Jetzt soll member in ein Prädikat umgeschrieben werden, das immer nur das erste Auftreten findet.
Weitere Beispiele zum Cut
Einfach Backtracking verhindern.
member(X,[X|_]):- !.
member(X,[_|L]):- member(X,L).
?- member(X,[a,b,c]).
X=a;
no.
Einfach Backtracking verhindern.
member(X,[X|_]):- !.
member(X,[_|L]):- member(X,L).
?- member(X,[a,b,c]).
X=a;
no.
Weitere Beispiele zum Cut
Ein Element in eine Liste einfügen, ohne Dubletten. Füge X nur dann ein, wenn X nicht schon in der Liste ist.
add(X,L,L1).
Wenn X schon in L enthalten ist, dann ist L1 identisch mit L. Sonst ist L1 die Liste L in die X eingefügt wird.
→ Was ist die einfachste Möglichkeit in Prolog ein Element in eine Liste einzufügen: mach es zum Kopf der Liste.
Zur Konstruktion wird das member Prädikat und der Cut benutzt.
Ein Element in eine Liste einfügen, ohne Dubletten. Füge X nur dann ein, wenn X nicht schon in der Liste ist.
add(X,L,L1).
Wenn X schon in L enthalten ist, dann ist L1 identisch mit L. Sonst ist L1 die Liste L in die X eingefügt wird.
→ Was ist die einfachste Möglichkeit in Prolog ein Element in eine Liste einzufügen: mach es zum Kopf der Liste.
Zur Konstruktion wird das member Prädikat und der Cut benutzt.
Weitere Beispiele zum Cut
add(X,L,L):- member(X,L),!.
add(X,L,[X|L]).
Beispielaufrufe:
?- add(a,[b,c],L).
L=[a,b,c].
?- add(X,[b,c],L).
L=[b,c]
X=b.
?- add(a,[b,c,X],L).
L=[b,c,a]
X=a.
Wiederum soll add(X,L1,L2) mit L2 uninstantiert aufgerufen werden, sonst kann es zu unerwartetem Verhalten kommen.
add(a,[a],[a,a]) ist erfolgreich. Warum? Regelkopf der ersten Regel macht nicht. Zweite wird angewandt, member nie gerufen!
add(X,L,L):- member(X,L),!.
add(X,L,[X|L]).
Beispielaufrufe:
?- add(a,[b,c],L).
L=[a,b,c].
?- add(X,[b,c],L).
L=[b,c]
X=b.
?- add(a,[b,c,X],L).
L=[b,c,a]
X=a.
Wiederum soll add(X,L1,L2) mit L2 uninstantiert aufgerufen werden, sonst kann es zu unerwartetem Verhalten kommen.
add(a,[a],[a,a]) ist erfolgreich. Warum? Regelkopf der ersten Regel macht nicht. Zweite wird angewandt, member nie gerufen!
Weitere Beispiele zum Cut
Es ist nicht offensichtlich, wie man add ohne Duplikate unter Verzicht auf den Cut implementieren soll. Wenn der Cut einfach weggelassen wird, werden durch Backtracking auch Duplikate
eingefügt. Der Cut ist hier notwendig um die gewünschte Relation zu spezifizieren und nicht nur um die Effizienz zu verbessern.
Es ist nicht offensichtlich, wie man add ohne Duplikate unter Verzicht auf den Cut implementieren soll. Wenn der Cut einfach weggelassen wird, werden durch Backtracking auch Duplikate
eingefügt. Der Cut ist hier notwendig um die gewünschte Relation zu spezifizieren und nicht nur um die Effizienz zu verbessern.
Klassifikation in Kategorien
Beispieldatenbank mit Sportresultaten:
beat(tom,jim).
beat(ann,tom).
beat(pat,jim).
class Prädikat: class(Spieler,Kategorie).
winner: gewinnt immer
fighter: gewinnt mal, verliert mal looser: verliert immer
Versuch als if, elsif ...Regel zu formulieren → in Prolog mit Cut.
Beispieldatenbank mit Sportresultaten:
beat(tom,jim).
beat(ann,tom).
beat(pat,jim).
class Prädikat: class(Spieler,Kategorie).
winner: gewinnt immer
fighter: gewinnt mal, verliert mal looser: verliert immer
Versuch als if, elsif ...Regel zu formulieren → in Prolog mit Cut.
Klassifikation in Kategorien
Versuch als if, elsif, else Regel zu formulieren → in Prolog mit Cut.
if X beats somebody and somebody else beats X then X is a fighter elsif X beats somebody X is a winner
elsif X is beaten by somebody X is a looser class(X,fighter) :- beat(X,_), beat(_,X),! . class(X,winner) :- beat(X,_),! .
class(X,looser) :- beat(_,X).
Cut schneidet Backtracking ab, wenn einer der if Zweige eingetreten ist.
Wiederum sollte beim Aufruf das zweite Argument nicht schon instanitiert sein.
?- class(tom, looser). yes. %warum?
Beachten Sie: der zweite Cut ist nicht prozedural notwendig, nur effizient.
Versuch als if, elsif, else Regel zu formulieren → in Prolog mit Cut.
if X beats somebody and somebody else beats X then X is a fighter elsif X beats somebody X is a winner
elsif X is beaten by somebody X is a looser class(X,fighter) :- beat(X,_), beat(_,X),! . class(X,winner) :- beat(X,_),! .
class(X,looser) :- beat(_,X).
Cut schneidet Backtracking ab, wenn einer der if Zweige eingetreten ist.
Wiederum sollte beim Aufruf das zweite Argument nicht schon instanitiert sein.
?- class(tom, looser). yes. %warum?
Beachten Sie: der zweite Cut ist nicht prozedural notwendig, nur effizient.
Negation als Failure
Mary mag alle Tiere außer Schlangen! Der erste Teil ist leicht!
Mary likes any X, if X is an animal.
likes(mary,X) :- animal(X).
Der zweite Teil:
Wenn X eine Schlange ist, dann soll likes nicht true ergeben.
Zusammen:
Wenn X eine Schlange ist, likes not true, elseif, wenn X ein Tier ist likes ist true.
Wir benutzen das spezielle goal fail, das nie erfolgreich ist und deshalb das aufrufende Ziel Parent-Goal zum Scheitern bringt.
Mary mag alle Tiere außer Schlangen! Der erste Teil ist leicht!
Mary likes any X, if X is an animal.
likes(mary,X) :- animal(X).
Der zweite Teil:
Wenn X eine Schlange ist, dann soll likes nicht true ergeben.
Zusammen:
Wenn X eine Schlange ist, likes not true, elseif, wenn X ein Tier ist likes ist true.
Wir benutzen das spezielle goal fail, das nie erfolgreich ist und deshalb das aufrufende Ziel Parent-Goal zum Scheitern bringt.
Negation as Failure
likes(mary,X) :- snake(X),!, fail.
likes(mary,X) :- animal(X).
Cut dient dazu, das fail einzufrieren.
Implementation von different(X,Y) im Sinne X,Y matchen nicht.
If X and Y match, different(X,Y) fail, else different succeed.
different(X,X) :- !, fail.
different(X,Y).
in einer Klausel:
different(X,Y) :- X=Y, !, fail ; true.
true ist ein Goal das immer erfolgreich ist.
likes(mary,X) :- snake(X),!, fail.
likes(mary,X) :- animal(X).
Cut dient dazu, das fail einzufrieren.
Implementation von different(X,Y) im Sinne X,Y matchen nicht.
If X and Y match, different(X,Y) fail, else different succeed.
different(X,X) :- !, fail.
different(X,Y).
in einer Klausel:
different(X,Y) :- X=Y, !, fail ; true.
true ist ein Goal das immer erfolgreich ist.
Unäres "not"
not(P) :- P,!,fail ;
true.
not(Goal) ist wahr wenn Goal nicht wahr ist. Wenn also Goal erfolgreich ist, ist not(Goal) nicht erfolgreich und umgekehrt.
Wir werden im Folgenden annehmen not(X) sei in der spezifizierten Weise implementiert und auch als Präfix-Operator nutzbar.
Also soll etwa
not snake(X) not(snake(X)) entsprechen.
not(P) :- P,!,fail ;
true.
not(Goal) ist wahr wenn Goal nicht wahr ist. Wenn also Goal erfolgreich ist, ist not(Goal) nicht erfolgreich und umgekehrt.
Wir werden im Folgenden annehmen not(X) sei in der spezifizierten Weise implementiert und auch als Präfix-Operator nutzbar.
Also soll etwa
not snake(X) not(snake(X)) entsprechen.
Unäres "not"
Vorhergehende Beispiele:
likes(marry, X) :- animal(X), not snake(X).
different(X,Y) :- not(X=Y).
Sportklassifikation:
class(X,fighter) :- beat(X,_),beat(_,X).
class(X,winner) :- beat(X,_), not beat(_,X).
class(X,looser) :- beat(_,X), not beat(X,_).
Überlegen Sie, ob diese Implementation so effizient ist, wie die ursprüngliche. Wie verhält sich diese Implementation mit unsinnig instantiertem zweiten Argument?
Vorhergehende Beispiele:
likes(marry, X) :- animal(X), not snake(X).
different(X,Y) :- not(X=Y).
Sportklassifikation:
class(X,fighter) :- beat(X,_),beat(_,X).
class(X,winner) :- beat(X,_), not beat(_,X).
class(X,looser) :- beat(_,X), not beat(X,_).
Überlegen Sie, ob diese Implementation so effizient ist, wie die ursprüngliche. Wie verhält sich diese Implementation mit unsinnig instantiertem zweiten Argument?
Probleme mit Cut und Negation
Vorteile des Cut:
(1) Verbessern der Effizienz von Programmen.
Unsinnige Alternativen können blockiert werden.
(2) Gegenseitig exklusive Regeln können implementiert werden.
Nachteile des Cut:
Der Preis ist der Verlust von Korrespondenz zwischen deklarativer und prozeduraler Bedeutung des Programms. Ohne Cut können die Klauseln umorganisiert werden und dies betrifft nur die Effizienz oder Terminierung. Mit Cut kann Umorganisation zu
unterschiedlichen Resultaten führen.
Vorteile des Cut:
(1) Verbessern der Effizienz von Programmen.
Unsinnige Alternativen können blockiert werden.
(2) Gegenseitig exklusive Regeln können implementiert werden.
Nachteile des Cut:
Der Preis ist der Verlust von Korrespondenz zwischen deklarativer und prozeduraler Bedeutung des Programms. Ohne Cut können die Klauseln umorganisiert werden und dies betrifft nur die Effizienz oder Terminierung. Mit Cut kann Umorganisation zu
unterschiedlichen Resultaten führen.
Probleme mit Cut und Negation
p :- a,b.
p:- c.
deklarative Bedeutung:
p <=> (a&b)Vc p :- a,!,b.
p:- c.
deklarative Bedeutung:
p <=> (a&b)V(¬a&c) tauschen Klauseln
p:- c.
p :- a,!,b.
deklarative Bedeutung:
p <=> cV(a&b)
p :- a,b.
p:- c.
deklarative Bedeutung:
p <=> (a&b)Vc p :- a,!,b.
p:- c.
deklarative Bedeutung:
p <=> (a&b)V(¬a&c) tauschen Klauseln
p:- c.
p :- a,!,b.
deklarative Bedeutung:
p <=> cV(a&b)
Probleme mit der Negation
not human (mary).
Oft mit yes beantwortet, nämlich immer dann, wenn keine Aussagen über mary in der Datenbank sind! "Closed world assumption"
Alles was existiert steht im Programm oder kann daraus abgeleitet werden.
not human (mary).
Oft mit yes beantwortet, nämlich immer dann, wenn keine Aussagen über mary in der Datenbank sind! "Closed world assumption"
Alles was existiert steht im Programm oder kann daraus abgeleitet werden.
Probleme mit der Negation
Restaurant-Beispiel
guter_standard(jeanluis).
teuer(jeanluis).
guter_standard(francesco).
empfehlenswert(Restaurant) :- not teuer(Restaurant).
?- guter_standard(X), empfehlenswert(X).
X = francesco.
?- empfehlenswert(X),guter_standard(X).
no.
Im zweiten Fall ist X unbelegt, wenn das not Goal abgearbeitet wird.
Restaurant-Beispiel
guter_standard(jeanluis).
teuer(jeanluis).
guter_standard(francesco).
empfehlenswert(Restaurant) :- not teuer(Restaurant).
?- guter_standard(X), empfehlenswert(X).
X = francesco.
?- empfehlenswert(X),guter_standard(X).
no.
Im zweiten Fall ist X unbelegt, wenn das not Goal abgearbeitet wird.
Probleme mit der Negation
Das Problem mit nicht instantierten negierten Zielen rührt von der unglücklichen Quantifikation von Variablen im Konzept Negation als Failure her:
?-expensive(X). Existentiell quantifiziert. Existiert in der Datenbank X, so dass expensive(X) wahr ist → jeanlouis.
?-not expensive(X). Nicht so interpretiert: es existiert ein X, so dass not expensive(X) → francesco sondern, universale Quantifikation durch Negation als failure:
not( exists X such that expensive(X))
also all-quantifiziert, for all X: not expensive(X).
Das Problem mit nicht instantierten negierten Zielen rührt von der unglücklichen Quantifikation von Variablen im Konzept Negation als Failure her:
?-expensive(X). Existentiell quantifiziert. Existiert in der Datenbank X, so dass expensive(X) wahr ist → jeanlouis.
?-not expensive(X). Nicht so interpretiert: es existiert ein X, so dass not expensive(X) → francesco sondern, universale Quantifikation durch Negation als failure:
not( exists X such that expensive(X))
also all-quantifiziert, for all X: not expensive(X).
Schleifen mit true und fail
Mit den eingebauten Prädikaten true und fail lassen sich Schleifen simulieren.
p(X) :-
q(X,Y), fail.
p(X).
Da fail nicht beweisbar ist, muß durch Backtracking jede Lösung Y von q(X,Y) gesucht werden.
Schließlich wird nach der letzten Alternative die zweite Regel p(X) erreicht, wodurch die Schleife beendet wird.
Andere Schreibweise mit true:
p(X) :- ( q(X,Y) , fail; true).
Mit den eingebauten Prädikaten true und fail lassen sich Schleifen simulieren.
p(X) :-
q(X,Y), fail.
p(X).
Da fail nicht beweisbar ist, muß durch Backtracking jede Lösung Y von q(X,Y) gesucht werden.
Schließlich wird nach der letzten Alternative die zweite Regel p(X) erreicht, wodurch die Schleife beendet wird.
Andere Schreibweise mit true:
p(X) :- ( q(X,Y) , fail; true).
Hausaufgabe
(1) Arbeiten Sie im Leiss Skript Seite 27 – 38 durch. Versuchen Sie alle Beispiele zu verstehen.
(2) Implementieren Sie die Programme aus der Vorlesung und im Leiss Skript in SWI Prolog, lassen Sie Beispielaufrufe im Tracer laufen.
(1) Arbeiten Sie im Leiss Skript Seite 27 – 38 durch. Versuchen Sie alle Beispiele zu verstehen.
(2) Implementieren Sie die Programme aus der Vorlesung und im Leiss Skript in SWI Prolog, lassen Sie Beispielaufrufe im Tracer laufen.