Grössere Programme
Nachträge
Weitere nützliche Prädikate
Programmiertechniken und Debugging Tokenizer Programm I
Nachträge
Weitere nützliche Prädikate
Programmiertechniken und Debugging Tokenizer Programm I
Nachtrag zum Backtracking
Werden im Backtracking bottom-up höhere choice-Punkte erreicht, so werden alle Belegungen die darunter liegen freigegeben. Das muss so sein, da sich durch Backtracking weiter oben über
Abhängigkeiten die Startbelegungen der weiter unten liegenden Ziele ändern können.
Werden im Backtracking bottom-up höhere choice-Punkte erreicht, so werden alle Belegungen die darunter liegen freigegeben. Das muss so sein, da sich durch Backtracking weiter oben über
Abhängigkeiten die Startbelegungen der weiter unten liegenden Ziele ändern können.
Nachtrag zum Backtracking
Beispielprogramm:
p(1).
p(2).
?- p(X), p(Y).
X=1,Y=1;
X=1,Y=2;
X=2,Y=1; %zuerst 1 da Belegung mit 2 wieder gelöscht wurde X=2,Y=2; %jetzt wieder Y bottom-up Backtracking erste Variante no %weiteres Backtracking nicht möglich
Beispielprogramm:
p(1).
p(2).
?- p(X), p(Y).
X=1,Y=1;
X=1,Y=2;
X=2,Y=1; %zuerst 1 da Belegung mit 2 wieder gelöscht wurde X=2,Y=2; %jetzt wieder Y bottom-up Backtracking erste Variante no %weiteres Backtracking nicht möglich
Beispielprogramm zu =..
=.. Term umwandeln zu Funktor, Argumenten in einer Liste
Stellen uns vor, dass wir ein Programm haben, das geometrische Figuren manipuliert. Funktor gibt den Typ der Figur an, Argumente spezifizieren die Größe.
Figuren
square(Side)
square(Side1,Side2,Side3) circle(Radius)
Mögliche Operation enlarge(Figure,Factor,Figure1)
=.. Term umwandeln zu Funktor, Argumenten in einer Liste
Stellen uns vor, dass wir ein Programm haben, das geometrische Figuren manipuliert. Funktor gibt den Typ der Figur an, Argumente spezifizieren die Größe.
Figuren
square(Side)
square(Side1,Side2,Side3) circle(Radius)
Mögliche Operation enlarge(Figure,Factor,Figure1)
Beispielprogramm zu =..
enlarge(square(A),F,square(A1)) :- A1 is F*A.
enlarge(circle(R),F,circle(R1)) :- R1 is F*R.
Verallgemeinerter Lösungsversuch für die Einparameterfiguren:
enlarge(Type(Parameter),F,Type(Parameter1)) :- Parameter1 is F*Parameter.
Funktioniert nicht, da Funktor in Prolog Atom sein muss.
Variable werden nicht als Funktor akzeptiert enlarge(square(A),F,square(A1)) :-
A1 is F*A.
enlarge(circle(R),F,circle(R1)) :- R1 is F*R.
Verallgemeinerter Lösungsversuch für die Einparameterfiguren:
enlarge(Type(Parameter),F,Type(Parameter1)) :- Parameter1 is F*Parameter.
Funktioniert nicht, da Funktor in Prolog Atom sein muss.
Variable werden nicht als Funktor akzeptiert
Beispielprogramm zu =..
Lösung: =.. Benutzen enlarge(Fig, F, Fig1) :-
Fig =.. [Type|Parameters],
multiplylist(Parameters,F,Parameters1), Fig1 =.. [Type|Parameters1].
%Hilfsprädikat
multiplylist([ ],_,[ ])
multiplylist([X|Rest],F,[X1|Rest1]) :-
X1 is X*F, multiplylist(Rest,F,Rest1).
erledigt auch square etc.
Lösung: =.. Benutzen enlarge(Fig, F, Fig1) :-
Fig =.. [Type|Parameters],
multiplylist(Parameters,F,Parameters1), Fig1 =.. [Type|Parameters1].
%Hilfsprädikat
multiplylist([ ],_,[ ])
multiplylist([X|Rest],F,[X1|Rest1]) :-
X1 is X*F, multiplylist(Rest,F,Rest1).
erledigt auch square etc.
Datenbankmanipulation
Prologprogramm kann als relationale Datenbank betrachtet werden.
Die Relationenmenge ist z.T. explizit: Fakten, z.T. implizit, Regeln.
Einige built-in Prädikate erlauben es, die Datenbank während der Programmausführung zu verändern (update).
Klauseln hinzufügen: assert, asserta, assertz.
Klauseln löschen: retract.
Prologprogramm kann als relationale Datenbank betrachtet werden.
Die Relationenmenge ist z.T. explizit: Fakten, z.T. implizit, Regeln.
Einige built-in Prädikate erlauben es, die Datenbank während der Programmausführung zu verändern (update).
Klauseln hinzufügen: assert, asserta, assertz.
Klauseln löschen: retract.
Datenbankmanipulation
assert (C): immer true, Seiteneffekt, C ist in der Datenbank verfügbar.
retract (C): immer true, Seiteneffekt, C ist in der Datenbank nicht mehr verfügbar.
nice :- sunshine, not raining. %Regeln funny :- sunshine, raining.
disgusting :- fog, raining.
rainy. %Fakten fog.
assert (C): immer true, Seiteneffekt, C ist in der Datenbank verfügbar.
retract (C): immer true, Seiteneffekt, C ist in der Datenbank nicht mehr verfügbar.
nice :- sunshine, not raining. %Regeln funny :- sunshine, raining.
disgusting :- fog, raining.
rainy. %Fakten fog.
Datenbankmanipulation
nice :- sunshine, not raining. %Regeln funny :- sunshine, raining.
disgusting :- fog, raining.
rainy. %Fakten fog.
?- nice. no
?- disgusting. yes
?- retract(fog).
?- disgusting. no
?- assert(sunshine).
?- funny. yes
?-retract(raining).
?-nice.yes
nice :- sunshine, not raining. %Regeln funny :- sunshine, raining.
disgusting :- fog, raining.
rainy. %Fakten fog.
?- nice. no
?- disgusting. yes
?- retract(fog).
?- disgusting. no
?- assert(sunshine).
?- funny. yes
?-retract(raining).
?-nice.yes
Datenbankmanipulation
Klauseln jeder Form können mit assert hinzugefügt und mit retract gelöscht werden. In manchen Prolog Implementation müssen soltch Klauseln dynamic deklariert sein.
?- asserta(C). fügt C am Anfang der Datenbank ein
?- assertz(C). fügt C am Ende der Datenbank ein
Als Anwendungsfall: schon berechnete Ergebnisse als Fakten in die Datenbank schreiben.
maketable :- L=[0,1,2,3,4,5,6,7,8,9]
member (X,L),member(Y,L), Z is X*Y,
assert(product(X,Y,Z) fail.
Klauseln jeder Form können mit assert hinzugefügt und mit retract gelöscht werden. In manchen Prolog Implementation müssen soltch Klauseln dynamic deklariert sein.
?- asserta(C). fügt C am Anfang der Datenbank ein
?- assertz(C). fügt C am Ende der Datenbank ein
Als Anwendungsfall: schon berechnete Ergebnisse als Fakten in die Datenbank schreiben.
maketable :- L=[0,1,2,3,4,5,6,7,8,9]
member (X,L),member(Y,L), Z is X*Y,
assert(product(X,Y,Z) fail.
Datenbankmanipulation
?- maketable. fail
Wie sieht jetzt die Datenbank aus? Schauen Sie das Programm im Tracer an, um zu sehen wie es arbeitet.
?- product(A,B,8).
A=1,B=8;A=2,B=4;A=4,B=2;A=8,B=1;fail
Vorsicht mit assert und retract, diese Prädikate führen leicht zu obskuren Programmen.
?- maketable. fail
Wie sieht jetzt die Datenbank aus? Schauen Sie das Programm im Tracer an, um zu sehen wie es arbeitet.
?- product(A,B,8).
A=1,B=8;A=2,B=4;A=4,B=2;A=8,B=1;fail
Vorsicht mit assert und retract, diese Prädikate führen leicht zu obskuren Programmen.
Multiple Lösungen erzwingen
bagof, setof, findall
bagof(X,P,L) Liste L, aller Objekte X, so dass P erfüllt ist.
age(tom,7).
age(pat,4).
age(lisa,7).
?- bagof(Child,age(Child,7),List).
L=[tom,lisa] yes
?- bagof(Child,Age^age(Child,Age),List).
L=[tom,pat,lisa] yes
^ Infix Operator der Age auf beliebig setzt
setof: Liste wird geordnet und ohne Duplikate ausgegeben Termordnung nach @< Prädikat
bagof, setof, findall
bagof(X,P,L) Liste L, aller Objekte X, so dass P erfüllt ist.
age(tom,7).
age(pat,4).
age(lisa,7).
?- bagof(Child,age(Child,7),List).
L=[tom,lisa] yes
?- bagof(Child,Age^age(Child,Age),List).
L=[tom,pat,lisa] yes
^ Infix Operator der Age auf beliebig setzt
setof: Liste wird geordnet und ohne Duplikate ausgegeben Termordnung nach @< Prädikat
Multiple Lösungen erzwingen
findall
wiederum wird eine Liste erstellt, die alle Objekte enthält, die P erfüllen. Der Unterschied zu bagof ist, dass alle X gesammelt werden, egal wie die Variablen in P belegt sind, die nicht mit X geteilt werden.
findall(Child,age(Child,Age),List).
List=[tom,pat,lisa] yes.
findall
wiederum wird eine Liste erstellt, die alle Objekte enthält, die P erfüllen. Der Unterschied zu bagof ist, dass alle X gesammelt werden, egal wie die Variablen in P belegt sind, die nicht mit X geteilt werden.
findall(Child,age(Child,Age),List).
List=[tom,pat,lisa] yes.
Programmierstil und Techniken
Generelle Prinzipien
Prolog Programme im Allgemeinen Guter Programmierstil
Debugging Effizienz
Generelle Prinzipien
Prolog Programme im Allgemeinen Guter Programmierstil
Debugging Effizienz
Programmierstil und Techniken
Korrektheit
Benutzerfreundlichkeit Effizienz
Lesbarkeit
Modifizierbarkeit, Wartbarkeit Robustheit
Dokumentation Korrektheit
Benutzerfreundlichkeit Effizienz
Lesbarkeit
Modifizierbarkeit, Wartbarkeit Robustheit
Dokumentation
Programmierstil und Techniken
Schrittweise Annäherung an das Problem: in jedem Schritt werden einzelne schon verifizierte Prozeduren und Datenstrukturen auf die Lösung des eigentlichen Problems zubewegt
Schritte sollten so klein sein, dass eine Anpassung gut durchführbar ist und man immer Klarheit hat warum das Programm vor der
Änderung noch funktioniert hat
Beispiel: implementiere das Damenproblem erst als Turmproblem Finde lösbare Subprobleme
Schrittweise Annäherung an das Problem: in jedem Schritt werden einzelne schon verifizierte Prozeduren und Datenstrukturen auf die Lösung des eigentlichen Problems zubewegt
Schritte sollten so klein sein, dass eine Anpassung gut durchführbar ist und man immer Klarheit hat warum das Programm vor der
Änderung noch funktioniert hat
Beispiel: implementiere das Damenproblem erst als Turmproblem Finde lösbare Subprobleme
Programmierstil und Techniken
Techniken in Prolog zum finden lösbarer Subprobleme Benutze Rekursion: (1) Triviale Fälle, Grenzfälle
(2) Generelle Fälle= Lösung wird aus Lösungen einfacherer Varianten des ursprünglichen
Problems gewonnen
Techniken in Prolog zum finden lösbarer Subprobleme Benutze Rekursion: (1) Triviale Fälle, Grenzfälle
(2) Generelle Fälle= Lösung wird aus Lösungen einfacherer Varianten des ursprünglichen
Problems gewonnen
Programmierstil und Techniken
Beispiel: List ist eine Eingabeliste, F eine Transformationsregel = binäre Relation, Newlist, die Liste aller transformierten Objekte (1) Grenzfall: List=[ ] → Newlist=[ ] egal wie F aussieht
(2) Genereller Fall
List = [ X|Tail ] → Newlist=[NewX|NewTail ]
Transformiere X unter F in NewX und dann den Rest maplist([ ][ ]).
maplist[X|Tail],F,[NewX|NewTail]) :- G =.. [F,X,NewX],
call(G), maplist(Tail,F,NewTail).
Builtin call(Goal): Argument ist das Goal das ausgeführt werden soll Quadrate ausrechnen:
square(X,Y) :- Y is X*X.
maplist ([2,6,5],square,Squares).
Beispiel: List ist eine Eingabeliste, F eine Transformationsregel = binäre Relation, Newlist, die Liste aller transformierten Objekte (1) Grenzfall: List=[ ] → Newlist=[ ] egal wie F aussieht
(2) Genereller Fall
List = [ X|Tail ] → Newlist=[NewX|NewTail ]
Transformiere X unter F in NewX und dann den Rest maplist([ ][ ]).
maplist[X|Tail],F,[NewX|NewTail]) :- G =.. [F,X,NewX],
call(G), maplist(Tail,F,NewTail).
Builtin call(Goal): Argument ist das Goal das ausgeführt werden soll Quadrate ausrechnen:
square(X,Y) :- Y is X*X.
maplist ([2,6,5],square,Squares).
Programmierstil und Techniken
warum lässt sich Rekursion so natürlich anwenden: Datenobjekte selbst besitzen oft rekursive Strukturen. Listen und Bäume sind solche Strukturen.
Liste: (1) leer
(2) Head und Tail welches selbst Liste ist Binärer Baum
(1) leer
(2) Root und zwei Subbäume, die wieder binäre Bäume sind warum lässt sich Rekursion so natürlich anwenden: Datenobjekte selbst besitzen oft rekursive Strukturen. Listen und Bäume sind solche Strukturen.
Liste: (1) leer
(2) Head und Tail welches selbst Liste ist Binärer Baum
(1) leer
(2) Root und zwei Subbäume, die wieder binäre Bäume sind
Programmierstil und Techniken
Stylistische Konventionen
kurze Klauseln, wenige Ziele im Regelkörper
kurze Prozeduren = Klauseln mit demselben Kopf
sprechende Namen: Bedeutung der Relationen und die Rolle von Datenobjekten sollte angezeigt werden
Layout: spacing, Leerzeilen, Einrückungen konsequent; Klauseln der selben Prozedur zusammen
Cut vorsichtig, nicht wenn vermeidbar, besser grüne als rote; rote nur für not und Alternativenauswahl
Programmmodifikation durch assert und retract gefährlich, da unterschiedliches Programmverhalten zu unterschiedlichen Zeitpunkten
Benutzen von ; kann Klauseln schwer verständlich machen.
Manchmal besser zwei Klauseln zu benutzen Stylistische Konventionen
kurze Klauseln, wenige Ziele im Regelkörper
kurze Prozeduren = Klauseln mit demselben Kopf
sprechende Namen: Bedeutung der Relationen und die Rolle von Datenobjekten sollte angezeigt werden
Layout: spacing, Leerzeilen, Einrückungen konsequent; Klauseln der selben Prozedur zusammen
Cut vorsichtig, nicht wenn vermeidbar, besser grüne als rote; rote nur für not und Alternativenauswahl
Programmmodifikation durch assert und retract gefährlich, da unterschiedliches Programmverhalten zu unterschiedlichen Zeitpunkten
Benutzen von ; kann Klauseln schwer verständlich machen.
Manchmal besser zwei Klauseln zu benutzen
Programmierstil und Techniken
Beispiel für komplizierenden Programmierstil, mergen zweier sortierter Listen, aufsteigende Sortierung
merge(List1,List2,List3) :- List1 = [ ],!,List3=List2;
List2=[ ],!,List3=List1;
List1 = [X|Rest1], List2 = [Y|Rest2], (X<Y,!,
Z=X, %Z wird Head von List3 merge(Rest1,List2,Rest3);
Z=Y, %Z wird Head von List3 merge(List1,Rest22,Rest3) ),
List3 = [Z|Rest3].
Beispiel für komplizierenden Programmierstil, mergen zweier sortierter Listen, aufsteigende Sortierung
merge(List1,List2,List3) :- List1 = [ ],!,List3=List2;
List2=[ ],!,List3=List1;
List1 = [X|Rest1], List2 = [Y|Rest2], (X<Y,!,
Z=X, %Z wird Head von List3 merge(Rest1,List2,Rest3);
Z=Y, %Z wird Head von List3 merge(List1,Rest22,Rest3) ),
List3 = [Z|Rest3].
Programmierstil und Techniken
Einfacheres Programm mit 3 Klauseln d.h. ohne or ; merge( [ ],List,List) :- !, .
merge(List, [ ],List) :- !, .
merge([X|Rest1],[Y|Rest2],[X|Rest3]) :-
X<Y,!,merge(Rest1,[Y|Rest2],Rest3]).
merge(List1],[Y|Rest2],[Y|Rest3]) :-
merge(List1,Rest2,Rest3)
Einfacheres Programm mit 3 Klauseln d.h. ohne or ; merge( [ ],List,List) :- !, .
merge(List, [ ],List) :- !, .
merge([X|Rest1],[Y|Rest2],[X|Rest3]) :-
X<Y,!,merge(Rest1,[Y|Rest2],Rest3]).
merge(List1],[Y|Rest2],[Y|Rest3]) :-
merge(List1,Rest2,Rest3)
Programmierstil und Techniken
Kommentare: nicht zu lang und nicht zu kurz Zweck, top level Prädikate, Hauptkonzepte merge(L1,L2,L3) merge/3
input mit + output mit - kennzeichnen merge(+L1,+L2,-L3)
Kommentare: nicht zu lang und nicht zu kurz Zweck, top level Prädikate, Hauptkonzepte merge(L1,L2,L3) merge/3
input mit + output mit - kennzeichnen merge(+L1,+L2,-L3)
Programmierstil und Techniken
Debugging
Wenn ein Prolog Programm nicht das erwartete Verhalten zeigt, ist das Hauptproblem, den Fehler zu finden. Leichter einen Fehler für ein Teilproblem zu finden als für das ganze Programm
Testen kleiner Programmeinheiten. Wenn diese funktionieren wie erwartet, Übergang zu größeren Modulen
(1) Da Prolog interaktiv ist, kann jeder Programmteil direkt mit Fragen getestet werden.
(2) Jede Prologimplementation stellt Debugging Hilfsprädikate bereit Debugging
Wenn ein Prolog Programm nicht das erwartete Verhalten zeigt, ist das Hauptproblem, den Fehler zu finden. Leichter einen Fehler für ein Teilproblem zu finden als für das ganze Programm
Testen kleiner Programmeinheiten. Wenn diese funktionieren wie erwartet, Übergang zu größeren Modulen
(1) Da Prolog interaktiv ist, kann jeder Programmteil direkt mit Fragen getestet werden.
(2) Jede Prologimplementation stellt Debugging Hilfsprädikate bereit
Programmierstil und Techniken
Basis des Tracing: Informationen, welche die Lösung eines Goals betreffen, werden während der Ausführung angezeigt.
trace, notrace
do Prädikatname + Argumente
exit Im Erfolgsfall Argumentwerte, sonst fail redo Prädikataufruf durch Backtracking
zwischen do und exit kann die Lösung aller Subgoals verfolgt werden
Detailiertes Tracing ist für größere Programme zu extzessiv. Trace für eine spezifizierte Teilmenge von Goals:
spy(Goal), nospy(Goal=
Mehrere Prädikate können gleichzeitig für spy offen sein.
Siehe SWI Seite für weitere Informationen zum debugging.
Basis des Tracing: Informationen, welche die Lösung eines Goals betreffen, werden während der Ausführung angezeigt.
trace, notrace
do Prädikatname + Argumente
exit Im Erfolgsfall Argumentwerte, sonst fail redo Prädikataufruf durch Backtracking
zwischen do und exit kann die Lösung aller Subgoals verfolgt werden
Detailiertes Tracing ist für größere Programme zu extzessiv. Trace für eine spezifizierte Teilmenge von Goals:
spy(Goal), nospy(Goal=
Mehrere Prädikate können gleichzeitig für spy offen sein.
Siehe SWI Seite für weitere Informationen zum debugging.
Tokenizer
● Zeichenweise von einer Datei einlesen
●
● Zerlege die Zeichenfolge in Token
●
● Wandle die Token in Prolog-Atome um
●
● Erkenne das Satzende und gib die Atomliste zurück.
●
Einstiegsprogramm siehe Kursseite, fertiger Tokenizer siehe Leiss-Skript
● Zeichenweise von einer Datei einlesen
●
● Zerlege die Zeichenfolge in Token
●
● Wandle die Token in Prolog-Atome um
●
● Erkenne das Satzende und gib die Atomliste zurück.
●
Einstiegsprogramm siehe Kursseite, fertiger Tokenizer siehe Leiss-Skript
Hausaufgabe
Verstehen Sie den gelieferten Tokenizer lite. Fügen Sie ein tokenize/4 ein, das führende Leerzeichen entfernt.
Machen Sie den Tokenizer robust gegen Dateiende
Nochmals versuchen den Tokenizer im Leiss Skript zu verstehen Programm abschreiben und zum Laufen bringen
Bratko Kapitel 8 lesen
Verstehen Sie den gelieferten Tokenizer lite. Fügen Sie ein tokenize/4 ein, das führende Leerzeichen entfernt.
Machen Sie den Tokenizer robust gegen Dateiende
Nochmals versuchen den Tokenizer im Leiss Skript zu verstehen Programm abschreiben und zum Laufen bringen
Bratko Kapitel 8 lesen