vierseitig

Werbung
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Beispiel 4.8.
Reihenfolge von Klauseln
• Bei der Programmierung in Prolog steht grundsätzlich die Repräsentation logischer
Zusammenhänge im Vordergrund.
• Nichtsdestotrotz ist es unvermeidbar, die Mechanismen der Abarbeitung in Prolog
zu kennen und gegebenenfalls steuernd einzugreifen.
Beispiel 4.7. Man betrachte die folgende Regel:
listsum( [], 0 ).
listsum( [H|T], Summe ) :- listsum( T, Sum1 ),
Summe is Sum1 + H.
fak(N, Fak ) :- N1 is N - 1,
fak(N1, Fak1),
Fak is Fak1 * N.
fak(0,1).
• Eine kürzere Schreibweise ist nicht möglich, da in Verbindung mit der Unifikation
keine Auswertung eines arithmetischen Ausdrucks stattfinden kann.
• Die Anfrage fak(7,X) führt zu einem Fehler.
• Prolog läuft in einen unendlichen Rekursionszyklus, weil die erste Regel immer
wieder anwendbar ist.
• Grund: unvollständige Spezifikation
222
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Aus logischer Sicht ist die Reihenfolge der Klauseln listsum( T, Sum1 ) und
Summe is Sum1 + H unerheblich.
Für die Abarbeitung spielt die Reihenfolge dagegen eine Rolle. Betrachtet man
listsum( [H|T], Summe ) :- Summe is Sum1 + H,
listsum( T, Sum1 ).
224
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
• Abhilfe: Erweiterung der Regel
fak(N, Fak ) :- integer(N),
N > 0,
N1 is N - 1,
fak(N1, Fak1),
Fak is Fak1 * N.
fak(0,1).
so würde dies zu einem Fehler führen.
• Mit dem Prädikat integer/1 wird geprüft, ob das Argument eine Integerzahl ist.
• So spielt auch die Reihenfolge der Regeln keine Rolle mehr.
?- listsum( [2,3,4], X ).
ERROR: Arguments are not sufficiently instantiated
Grund: Sum1 ist zum Zeitpunkt der Auswertung nicht mit einem Wert unifiziert (belegt).
223
225
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Beispiel 4.9. Man betrachte die folgenden Varianten des Pr ädikats vorfahr/2.
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Diese Dinge machen deutlich, daß auch in Prolog prozedurale Aspekte mit berücksichtigt werden müssen. Sonst führt dies zu Prolog-Programmen, die:
1. vorfahr( X, Y ) :- elternteil( X, Y ).
vorfahr( X, Y ) :- elternteil( X, Z ), vorfahr( Z, Y ).
• logisch (deklarativ) korrekt aber
• prozedural inkorrekt sind.
2. vorfahr( X, Y ) :- elternteil( X, Y ).
vorfahr( X, Y ) :- vorfahr( X, Z ), elternteil( Z, Y ).
Stellt man eine Anfrage mit der zweiten Variante, so erhält man eine Fehlermeldung,
falls die Vorfahrbeziehung nicht erfüllt ist:
?- vorfahr2( lars, lars ).
ERROR: Out of local stack
Ursache: In der zweiten Variante wird permanent vorfahr( lars, Z ) aufgerufen.
Hierdurch kommt es zu einem Überlauf des Stacks.
226
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Probleme bei der Verwendung von Rekursion:
228
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Cut zur Kontrolle des Backtracking
• Nicht-Termination
Durch eine ungünstige Reihenfolge der Klauseln ensteht eine unendlicher Zyklus.
• Speicherprobleme
Zur Vermeidung dieser Probleme sollte man nach Möglichkeit die folgenden Grundprinzipien beachten:
Prolog sucht mittels Backtracking bei der Abarbeitung nach Alternativen.
Beispiel 4.10. Gegeben seien die folgenden Prolog-Regeln zur Klassifikation von
Einkommen:
einkommen( X, gering ) :- X < 1000.
einkommen( X, mittel ) :- X >= 1000, X =< 2000.
einkommen( X, hoch )
:- X > 2000.
• Einfache Dinge zuerst!
Hierdurch findet eine Filterung für die aufwendigen Berechnungen statt.
• Einsatz von Endrekursion
Die rekursiven Aufrufe sollte als letzte Klausel des Regelrumpfs erfolgen.
Es wird die Anfrage gestellt:
?- einkommen( 950, B ), B = hoch.
227
229
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Es passiert folgendes:
Sei eine Klausel der folgenden Struktur gegeben:
• X wird mit 950 unifiziert, B mit gering. Der Regelrumpf X < 1000 ist erfüllt, das
Anfrageliteral B = hoch nicht.
• Mittels Backtracking wird die zweite und dritte Regel ausprobiert.
• Es werden jeweils die Unifikationen durchgeführt und der Regelrumpf getestet. Der
Regelrumpf ist nicht erfüllt.
• Ausgabe: No.
H :- B1,..., Bn, !, A1,..., Am.
Das Backtracking ist hier vollkommen unnötig, da für ein Einkommen nur genau eine
Regel in Frage kommt.
Sind B1,..., Bn erfüllt, so werden alle Alternativen, d.h. alle eventuell noch anwendbaren Regeln zum Beweis für B1,..., Bn und H abgeschnitten.
Beispiel 4.12. Das Prolog-Prädikat für den Test, ob ein Element in einer Liste enthalten ist:
memberchk( X, [X|_] ) :- !.
memberchk( X, [_|Y] ) :- memberchk( X, Y ).
230
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
232
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Mit dem Cut (geschrieben !) wird Backtracking verhindert.
Der Cut kann die Bedeutung eines logischen Programms verändern.
Beispiel 4.11.
p :- a,b.
p :- c.
Entspricht der Implikation
einkommen( X, gering ) :- X < 1000, !.
einkommen( X, mittel ) :- X >= 1000, X =< 2000, !.
einkommen( X, hoch )
:- X > 2000.
a∧b∨c→p
Wird der Cut ! überschritten, findet kein Backtracking mehr statt.
p :- a,!,b.
p :- c.
Entspricht der Implikation
a ∧ b ∨ ¬a ∧ c → p
• Ein Cut, der die Bedeutung eines Programms verändert, heißt roter Cut.
• Ein Cut, der die Bedeutung eines Programms nicht verändert, heißt grüner Cut.
Der Cut drückt hier folgendes aus:
Diese Regel ist genau die richtige, es kommt keine andere in Frage.
Dementsprechend würde die Anfrage des letzen Beispiels effizienter abgearbeitet.
231
233
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
• fail/0 ist ein vordefiniertes Prädikat, das nie erfüllt ist. Hierdurch wird Backtracking
erzwungen.
• Ist elternteil( X, _ ) erfüllt, so unterdrückt der Cut aber das Backtracking und
bewirkt das Abschneiden der zweiten Regel.
Negation
• Bisher haben wir mit Prädikaten immer “positive” Sachverhalte ausgedrückt: Ist
Elternteil von, ist Vorfahr von, etc.
• Es kann aber auch notwendig sein, einen “negativen” Sachverhalt auszudrücken,
d.h. etwas gilt nicht: Hat keine Kinder, etc.
Beispiel 4.13. Man möchte für das Verwandte-Beispiel ein Prädikat für die Kinderlosigkeit definieren:
Dieses Prinzip wird in Prolog allgemein für die Negation benutzt. Die Negation eines
Prädikats Ziel entpricht prinzipiell folgenden Regeln:
not( Ziel ) :- Ziel , !, fail.
not( Ziel ).
• Solch ein Prädikat not/1 ist in Prolog enthalten.
• Das Argument von not/1 ist eine Formel und kein Term!
kinderlos( X ) :- elternteil( X, _ ), !, fail.
kinderlos( X ).
234
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
236
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Beispiel 4.14. Berechnung der Primzahlen bis 100:
Beispiel 4.15.
ziffer(Z) :- member(Z, [0,1,2,3,4,5,6,7,8,9]).
zahl(Zahl) :- ziffer(Z), ziffer(E), Zahl is 10 * Z + E.
?- not( vater( theo, peter ) ).
Yes
?- not( vater( peter, lars ) ).
No
?- not( vater( peter, X ) ).
No
keineprimzahl(Z) :- zahl(T), echterTeiler(T, Z).
echterTeiler(T,Z) :- T > 1, T < Z, 0 is Z mod T.
primzahl(Z) :- keineprimzahl(Z),!,fail.
primzahl(Z) :- Z > 1.
• Die letzte Anfrage hat die Bedeutung: Ist Peter kein Vater?
Anfrage:
¬∃ X V ater(peter, X)
?- zahl( X ), primzahl( X ).
X = 2 ;
X = 3 ;
...
• Sie hat nicht die Bedeutung: Für welche X gilt, daß Peter nicht der Vater von X
ist.
∃ X ¬V ater(peter, X)
235
237
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Programmiertechniken in Prolog
Man bezeichnet dieses Prinzip der Negation als Negation as failure.
☞ Eine negierte Anfrage in Prolog ist wahr, wenn die Anfrage nicht bewiesen werden
kann.
• Man beachte, daß not/1 keine Variablenbelegung liefert.
• Es entspricht somit nicht der logischen Negation im Sinne der Negation eines
Prädikats.
• not/1 sollte nur verwendet werden, wenn in dem negierten Pr ädikat keine ungebundenen Variablen mehr auftauchen.
• Die Kurzschreibweise für not ist \+.
Wir betrachten zunächst die Verarbeitung von Listen.
Test auf Exsistenz: Wir wollen prüfen, ob eine Kollektion von Objekten mindestens
ein Objekt enthält, das eine gewisse Eigenschaft hat.
existence_test( Info, [H|T] ) :- has_property( Info, H ).
existence_test( Info, [H|T] ) :- existence_test( Info, T ).
Info steht hierbei für eine beliebige Anzahl an Argumenten, die ben ötigt werden, um
die gewünschte Eigenschaft zu prüfen.
Beispiel 4.17. Wir möchten prüfen, ob eine Liste mindestens eine weitere Liste als
Element enthält. ✎
238
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
240
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Beispiel 4.16. Das Beispiel für die Berechnung der Primzahlen mit direkter Verwendung von not/1 bzw. \+.
Test für alle Listenelemente: Wir wollen prüfen, ob alle Elemente einer Liste eine
Eigenschaft haben (erfüllen).
ziffer(Z) :- member(Z, [0,1,2,3,4,5,6,7,8,9]).
zahl(Zahl) :- ziffer(Z), ziffer(E), Zahl is 10 * Z + E.
forall_test( Info, [] ).
forall_test( Info, [H|T] ) :- has_property( Info, H ),
forall_test( Info, T ).
keineprimzahl(Z) :- zahl(T), echterTeiler(T, Z).
echterTeiler(T,Z) :- T > 1, T < Z, 0 is Z mod T.
Beispiel 4.18. Wir möchten prüfen, ob alle Elemente einer Liste Ziffern sind. ✎
primzahl(Z) :- Z > 1, \+ keineprimzahl(Z).
239
241
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Rückgabe eines Resultatswertes (1): Die vorangegangenen Pr ädikate lieferten kein
Resultat.
In Prolog kann ein Resultat nur über eine freie Variable, die an ein Pr ädikat übergeben
wird, geliefert werden.
Wir wollen eine Resultat liefern, sobald ein Listenelement eine gewisse Eigenschaft
hat.
return_if_property( Info, [H|T], Result ) :has_property( Info, H ),
result( Info, H, T, Result ).
return_if_property( Info, [H|T], Result ) :return_if_property( Info, T, Result ).
Beispiel 4.19. Ein Prädikat soll zu einer Liste die Subliste ab einem Element mit dem
Wert X liefern. ✎
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Allgemeine Programmierschemata in Prolog
Generate and Test:
• Backtracking wird genutzt, um mögliche Lösungen zu erzeugen
• Anschließend wird die mögliche Lösung getestet.
• Erfüllt die mögliche Lösung nicht die Bedingungen für eine Lösung wird durch
Backtracking eine neue Lösung generiert.
generate_and_test( Info, X ) :...
generate( Info, X ),
test( Info, X ),
...
242
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
244
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Rückgabe eines Resultatswertes (2): Wir wollen ein Resultat nach der Verarbeitung
aller Listenelemete liefern.
Ein Generator zur Implementierung des Prädikats generate kann endlich oder unendlich sein. Ein endlicher Generator wurde in dem Primzahlbeispiel verwendet:
process_all( Info, [H1|T1], Result ) :process_one( Info, H1, H2 ),
process_all( Info, T1, T2 ),
Result = [H2|T2].
ziffer(Z) :- member(Z, [0,1,2,3,4,5,6,7,8,9]).
zahl(Zahl) :- ziffer(Z), ziffer(E), Zahl is 10 * Z + E.
Er liefert durch Backtracking nur endlich viele Werte.
Beispiel 4.20. Wir wollen zu einer Liste von Zahlen eine Liste der Quadrate diese
Zahlen bestimmen. ✎
Ein unendlicher Generator für natürliche Zahlen könnte so aussehen:
int( 1 ).
int( N ) :- int( N1 ), N is N1 + 1.
Problem: Durch das Backtracking bricht der Prozeß der Generierung niemals ab.
243
245
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
4. Logikprogrammierung und Prolog
Dies ist aber kein Problem, wenn man nur eine Lösung haben möchte. Man unterdrückt das Backtrackig dann mit dem Cut:
generate_and_test( Info, X ) :...
generate( Info, X ),
test( Info, X ),!,
...
Steuerung der Abarbeitung in Prolog
Wichtige Prolog-Prädikate
• true/0 ist immer wahr.
• repeat/0 zur Programmierung von Iterationen Dieses Prädikat entspricht der Definition:
repeat.
repeat:- repeat.
Beispiel 4.21. Berechnung des KGV verschiedener Zahlen. ✎
• call/1 ruft den Interpreter des Prolog-Systems auf. Das Argument von call ist
kein Term sondern eine Prolog-Zielklausel.
• nl/0 schreibt ein Newline auf die Standardausgabe.
• write/1 schreibt das Argument auf die Standardausgabe.
• read/1 liest ein Argument von der Standardeingabe und führt dabei eine Unifikation durch.
246
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
Failure-Driven Loop:
• Manchmal möchte man Prädikate definieren, die gewisse Dinge tun, wobei das
Prädikat aber immer erfüllt sein soll.
• Beispiel: Gebe alle elternteil-Beziehungen aus.
• Im Vordergund stehen hier die Seiteneffekte (z.B. die Ausgabe).
• Vorsicht: Dies ist eine sehr prozedural orientierte Programmiertechnik!
248
4. Logikprogrammierung und Prolog
Steuerung der Abarbeitung in Prolog
• assert/1 fügt eine Klausel dem Prolog-Programm hinzu. Die Varianten asserta/1
bzw. assertz/1 fügen die Klausel am Anfang bzw. Ende des Prolog-Programms
ein.
• retract/1 entfernt eine Klausel aus dem Prolog-Programm.
failure_driven_loop( Info ) :- generate( Info, Term ),
side_effect( Term ),
fail.
failure_driven_loop( Info ).
Beispiel 4.22. Ausgabe aller elternteil-Beziehungen. ✎
247
249
Herunterladen