10: Cut, Negation

Das Prädikat fail/0

  • Das Prädikat fail/0 scheitert immer.
  • Es erzwingt Backtracking und kann zur Ausgabe aller Lösungen eingesetzt werden:
all(L):-
    member(X,L),
    write(X),nl,
    fail.
all(_).
?- all([a,b,c]).
a
b
c
true.

Umgang mit dem Cut

Schattenseiten des Cuts

  • Der Cut zerstört die Deklarativität von Prolog-Programmen.
  • Die Interpretation einer Prädikatsdefinition mit roten Cuts ist i.d.R. nur noch unter Berücksichtigung der Reihenfolge der Beweisschritte möglich.
  • Deshalb: Cut nur einsetzen, wenn ein offensichtlicher Vorteil erzielt werden kann.

Gründe für die Verwendung des Cuts

  • Beschneiden des Suchraums.
  • Erzwingen von Determinismus.
  • Modellierung von Defaults.
  • Modellierung von Negation.

Der Cut

Der Cut „!“ ist ein eingebautes Prädikat, mit dem Backtracking kontrolliert werden kann.

Der Cut kann folgendes bewirken:

  • Effizienzsteigerung
  • Speichereinsparung
  • Kürzere Programme

Wirkungsweise

  • Der Cut wird im Rumpf von Regeln eingesetzt und verhindert Backtracking.
  • Der Top-Down-Beweis des Cut gelingt immer.
  • Nach dem Passieren eines Cuts in einem Regelrumpf sind
    • die Teilziele, die in demselben Regelrumpf vor dem Cut stehen, und
    • alle weiteren Klauseln desselben Prädikats, die hinter der Regel stehen,
  • vom weiteren Backtracking ausgeschlossen.

Grüne und rote Cuts

In der Literatur wird oft zwischen roten und grünen Cuts
unterschieden.

  • Ein grüner Cut kann aus einem Programm entfernt werden, ohne dass sich die Bedeutung des Programms ändert.
  • Ein roter Cut kann nicht aus einem Programm entfernt werden, ohne dass sich die Bedeutung des Programms ändert.

max/3 mit grünem, rotem und ohne Cut

ohne Cut:

max(X,Y,Y) :- X =< Y.
max(X,Y,X) :- X>Y.

ineffizient!

?- max(3,5,X).
1 Call: max(3,5,_487) ?
2 Call: 3=<5 ?
2 Exit: 3=<5 ?
1 Exit: max(3,5,5) ?
X = 5 ? ;
1 Redo: max(3,5,5) ?
2 Call: 3>5 ?
2 Fail: 3>5 ?
1 Fail: max(3,5,_487) ?
false.

mit grünem Cut:

max(X,Y,Y) :- X =< Y,!.
max(X,Y,X) :- X>Y.

gut!

?- max(3,5,X).
1 Call: max(3,5,_487) ?
2 Call: 3=<5 ?
2 Exit: 3=<5 ?
1 Exit: max(3,5,5) ?
X = 5 ? ;
false.

mit rotem Cut

max(X,Y,Y) :- X =< Y,!.
max(X,Y,X).

falsch!

?- max(3,5,3).
true.

Cut: Beschneiden des Suchraums

my_member(H,[H|_]):- !.
my_member(H,[_|T]):- my_member(H,T).
?- my_member(b,[a,b,c]).
true.
?- my_member(d,[a,b,c]).
false.
?- my_member(X,[a,b,c]). X=a.
true.
?- my_member(a,X). X=[a|G_1].
true.

Die Beschneidung des Suchraums führt zu Determinismus.

Beschneidung des Suchraums – delete_first

L2 entsteht durch das Löschen eines Vorkommens von X aus L1.

% delete_once(X,L1,L2)
delete_once(X,[X|L1],L1).
delete_once(X,[H|T1],[H|T2]):-
delete_once(X,T1,T2).
?- delete_once(a,[a,b,c,a,d],L).
L = [b, c, a, d] ;
L = [a, b, c, d] ;
false.

L2 entsteht durch das Löschen des ersten Vorkommens von X aus L1.

% delete_first(X,L1,L2).
delete_first(X,[X|L1],L1):-!.
delete_first(X,[H|T1],[H|T2]):-
delete_first(X,T1,T2).
?- delete_first(a,[a,b,c,a,d],L).
L = [b, c, a, d].

Erzwingung von Determinismus – Fakultät

Lösung nicht deterministisch:

fak(N,R):-
    fak(N,1,R).
fak(0,Acc,Acc).
fak(N,Acc,R):-
    AccNew is N * Acc,
    NNew is N - 1,
    fak(NNew,AccNew,R).

Problem: Prädikat im Backtracking ⇒ Endlosschleife

Verbesserung durch Kontrollabfrage

fak(N,R):-
    fak(N,1,R).
fak(0,Acc,Acc).
fak(N,Acc,R):-
    N > 0,
    AccNew is N * Acc,
    NNew is N - 1,
    fak(NNew,AccNew,R).
?- fak(5,X). X=120;
false.

Determinismus durch Cut:

fak(N,R):-
    fak(N,1,R).
fak(0,Acc,Acc):-!.
fak(N,Acc,R):-
    AccNew is N * Acc,
    NNew is N - 1,
    fak(NNew,AccNew,R).
?- fak(5,X). X=120.

neg(A):-
    A,
    !,fail.
neg(_).
hund(snoopy).
hund(pluto).
katze(garfield).
?- neg(katze(pluto)).
true.
?- \+ katze(pluto).
true.
?- neg(katze(X)).
false.
?- \+ katze(X).
false.

Negation als „negation as failure“

Negation wird in Prolog durch eine Cut-FailKombination realisiert („negation as failure“)

Z. 2: Wenn der Ausdruck A bewiesen werden kann,

Z. 3: sorgt die Cut-Fail-Kombination dafür, dass der der Beweis von neg(A) scheitert. Der Cut hinter A und vor fail verhindert, dass die zweite Klausel von neg(A) für eine beweisbare Aussage A herangezogen werden kann.

Z. 4: Greift die erste Klausel nicht, ist A nicht beweisbar und die Negation von A ist wahr.

Das Negationsprädikat ist in Prolog als PräfixOperator + vordefiniert.

Vorsicht: Enthält eine Aussage Variablen und gibt es eine Variablenbelegung, die die Aussage wahr macht, ist die Negation der Aussage falsch.


if-then-else

ifthenelse(If,Then,_):-
    If,
    Then.
ifthenelse(If,_,Else):-
    \+ If,
    Else.

Drei Aussagen If, Then und Else erfüllen die
if-then-else-Relation, wenn

Z. 6-8: entweder If und Then beweisbar sind oder

Z. 9-11: If nicht beweisbar und Else beweisbar ist.

Beispiel: max/3 mit ifthenelse/3:

max(X,Y,Z):- ifthenelse(X=<Y,Y=Z,X=Z).

Wenn X kleiner oder gleich Y ist, so ist Y das Maximum von X und Y.
Wenn X nicht kleiner oder gleich Y ist, so ist X das Maximum von X und Y.


Probleme mit cut-fail Definition der Negation

neg(A):-
     A,
     !,fail.
neg(_).
ledigerStudent(X):-
     neg(verheiratet(X)),
     student(X).
student(peter).
verheiratet(klaus).
?- ledigerStudent(peter).
true.
?- ledigerStudent(klaus).
false.
?- ledigerStudent(X).
false.

Z. 6-8: X ist lediger Student, wenn X
nicht verheiratet ist und wenn
X Student ist.

  • „Negation as failure“ ist keine logische Negation. Daher kann das Prädikat ledigerStudent/1 in dieser Form nicht zur Generierung aller ledigen Studenten eingesetzt werden.
  • Sehen Sie einen einfachen Weg, das Prädikat zu verbessern?

cut-fail und default

mit cut-fail direkt

can_fly(X):-
    penguin(X),
    !, fail.
can_fly(X):- bird(X).
bird(X):- penguin(X).
bird(X):- eagle(X).
penguin(tweety).
eagle(klaus).

schlecht!

?- can_fly(tweety).
false.
?- can_fly(klaus).
true.
?- can_fly(popeye).
false.
?- can_fly(X).
false.

mit Negation (Version 1)

can_fly(X):-
    neg(penguin(X)),
    bird(X).
bird(X):- penguin(X).
bird(X):- eagle(X).
penguin(tweety).
eagle(klaus).

ebenso schlecht!

?- can_fly(tweety).
false.
?- can_fly(klaus).
true.
?- can_fly(popeye).
false.
?- can_fly(X).
false.

mit Negation (Version 2)

can_fly(X):-
    bird(X),
    neg(penguin(X)).
bird(X):- penguin(X).
bird(X):- eagle(X).
penguin(tweety).
eagle(klaus).

gut!

?- can_fly(tweety).
false.
?- can_fly(klaus).
true.
?- can_fly(popeye).
false.
?- can_fly(X). X=klaus.
true.

Zusammenfassung

  • Wir haben das Prädikat fail/0 kennengelernt, das immer scheitert.
  • Wir haben den Cut kennengelernt und gesehen, wie man Negation in Prolog als „negation as failure“ definieren kann.
  • Wir haben gelernt zwischen roten und grünen Cuts zu unterscheiden.
  • Wichtig: Cuts zerstören die Deklarativität von Prologprogrammen und sollten daher mit Bedacht eingesetzt werden.
  • Keywords: „negation as failure“, roter und grüner Cut, Cut-Fail-Kombination