Ja. Guten Morgen, alle zusammen. Ja, wie
Monty Pythons sagen, wurde den Lauf versumpling completely different.
Wir werden erstmal den reinen funktionalen Programmierstil verlassen und uns anschauen,
wieder relationale Programmierstil aussieht und eine Einbettung von Prolog in Rackett kennenlernen, die ich übertragen habe.
Bei der Gelegenheit ist mir, sind mir zwei Dinge wichtig. Zum einen,
dass sie sehen,
wie sich die relationale Sicht von der funktionalen Sicht unterscheidet,
aber auch für die beiden Stile zusammengehen,
weil sie werden mit Freude feststellen,
dass wir jederzeit in unseren Pro-Log in Reckett sagen können,
ich habe die Nase voll von einem reinen Pro-Log und ich
werde erstmal eine Zeit lang funktional programmieren und das Schlüpfloch in den funktionalen Programmierstil verwenden.
Also wie geht es zusammen? Wie kann man diese Stile vereinigen?
Und für welche Anwendungen eignet sich das eine oder das andere? Okay. Ich habe,
wie Sie in der Gliederung gesehen haben, zwei Teile übersprungen,
dann nächste Woche nachgeliefert werden, nämlich einmal den Teil,
wo wir überhaupt drüber reden, wie betten wir Programmierspiele ein?
Wie programmieren wir das? Wie machen wir es mit Makros?
Also wir erweitern wir die Sprache, das werden wir uns
nächste Woche anschauen. Nochmal einen Rückblick auf die Stromorientierte Programmierung,
die wir bei Lesy schon kennengelernt haben,
aber die wird da selbst gemacht nochmal machen wollen.
Und auch den Teil über Memo-Funktionen, wo wir kennenlernen,
wie machen wir Wie bringen wir dazu bei, eine Funktion,
sich daran zu erinnern, dass sie schon berechnet hat,
dass sie nicht jedes Mal von vorne anfängt.
Und diese Teile, wie gesagt, die kommen nächste Woche am Freitag. Das Aufgabenblatt,
was wir heute verteilen, enthält schon Teilaufgaben zu Memo-Funktionen,
aber mit dem Teil können sie sich dann gerne Zeit lassen bis zur Woche danach,
weil das wird jetzt das letzte Aufgabenblatt sein,
dass wir austeilen und das werden wir über den Rest des
Semesters strecken.
Ja, vielleicht auch noch für die,
die nicht in meiner Übungsgruppe sind. Sie werden gemerkt haben,
dass Aufgabenblatt, was letzte Woche verteilt wurde, enthält,
einen langen Teil mit Aufgaben, die alle lebensechte Klausuraufgaben sind,
die wir mal gestellt haben.
Also sie können das Blatt gerne nehmen zur Klausurvorbereitung und mit
Aufgaben ähnlicher Art sollten sie in der Klausur rechnen.
Okay, also zur relationalen Programmierung. Der relationale Programmierstil,
da werden wir in dem Zusammenhang über drei Dinge sprechen,
die erst bei dem Stil selbst charakterisieren.
Das Herzstück des Ganzen, die Datenbasis ansprechen und dann die Methode,
wie man Dinge, die wir in der Datenbasis beschrieben haben,
Wenn wir ein relationales Programm schreiben, dann sind wir in einer Welt,
in der wir den Rechner mit Wissen und mit Fakten füttern,
mit Fakten über eine Problemendomäne.
Und bitten den Rechner dann für uns aus dieser Menge an zugesicherten Fakten,
dann Sinn draus zu machen, sie zu kombinieren,
Schuss zu folgern und uns Antworten auf Fragen geben, die diese Domäne betreffen.
Wir sehen den Rechner als eine Inferenzmaschine, die Fakten, die bekannt sind,
kombinieren kann, um daraus neue Fakten zu Schlussfolgern und uns Antworten zu geben.
Also unsere Sicht auf den Computer, wir haben ihn mit allem gefüttert, alle Beweise, die wir gefunden haben. Ja,
wir haben gesehen, aha,
das Opfer hat Blutgruppe so und so und wir haben noch Blutgruppenspuren gefunden von Derblutgruppe und wir wissen von der Person,
die war da, die Person war da, also alles,
was wir an Fakten haben, schreiben wir rein,
ein bisschen strukturiert in Form von Relation und dann können wir fragen,
wie in diesem Beispiel hier, wer hat Denn die Person,
wen hat der Gärtner ermordet?
Was wir hier stehen haben, ist eine Relation,
die sagt, wenn immer irgendjemand X ermordet wurde,
X ist eine Variable, dann ist der Mörder der Gärtner.
Das steht als Zusicherung über das Wissen,
über die Welt von Mordfällen in unserer Datenbank,
die ist ein bisschen beschränkt, was soll's.
Wir können jetzt fragen,
wer ist der Mörder von Paul und geben für die Position,
wo in der Relation der Mörder genannt wird,
eine Variable VRM
In der Rechner durchsucht die Datenbank und sagt uns dann,
diese Anfrage ist wahr,
wenn ich die Variable M an den Wert Gärtner binde.
Ja, das kann ich beweisen, anhand meiner Datenbasis,
die ist mit der Variablen belegen.
N, das zugesichert werden kann. Und so erfahren wir,
also der Mörder ist der Gärtner, in dem Fall.
Also das logische Verarbeitungsmodell können wir immer dann sehr gut nehmen,
wenn es um die Modellierung von Beziehungen zwischen Objekten geht.
Also nicht nur um die Eigenschaften der Objekte selbst,
sondern in welchen Relationen stehen sie
Zum Beispiel, wer hat wen ermordet? Wer liebt wen
? Wer ist bei wem verschuldet und so weiter?
Wenn wir alles wissen, was wir haben,
vor allem gut als Fakten deplarieren können.
Und dann haben wir die Unterstützung für das automatische Beweisen von
Aussagen über die Objekte und deren Beziehungen einfach durch Deduktion.
Das Notenprogrammieren mit Relationen hat eine wohl definierte Semantik,
genauso wie wir es vom funktionalen Programmierstil kennen.
Im Funktionalen hatten wir als zugrunde liegende Semantik
das Landerkalkül
Nationalen Verarbeitungsmodell haben wir die Sprache der Mathematik über Relationen, ja.
Und wir haben die Logik. Wenn wir anfangen,
jetzt im Prolog zu programmieren,
da können wir uns leider nur einen ganz kleinen Ausflug leisten.
Aber das ist eben sehr charakteristisch, dass wir erstmal dadurch programmieren,
indem wir nur die Datenbank aufsetzen und dort Zusicherungen reinschreiben.
Fakten in einer uniformen Datenbasis. Und das,
diese Fakten werden als Klauseln relational hingeschrieben, ja,
und Damit sind wir eigentlich schon fertig. Und die Variablen,
die jetzt bei deinem Arbeiten mit Brüller gibt, sind logische Variable.
Prolock ist referenziell, transparent, genauso Bezugstransparenz gegeben,
wie wir es in der funktionalen Programmiersprachenwelt haben.
Ja, Variable werden nur einmal durch Unifikationen gebunden und werden
dann nie wieder verändert. Was uns Prolock abnimmt,
ist das, was wir im funktionalen Programmierstil zu Fuß programmieren
mussten,
das Backtracking
Dass die Suche in dieser Datenbank, die ist automatisch da.
Wenn wir einen Prolog kaufen, dann kaufen wir eine Suchmaschine,
die unsere Fakten kombinieren kann, auswerten, kann, schlussfolgern können.
Und das macht es mit einem Backtracking. Und das Binden
der Variablen macht es durch die Unifikation, die wir noch besprechen werden.
Ja, ein Abgleichen einer Anfrage mit der Datenbank führt dazu,
dass Variable gebunden werden. Und da bekommen sie ihren Wert
. Und was ich bestechend finde, der Prolog-Programmierwelt und wo
ich immer Diejenigen beneide die zurzeit in 100 programmieren dürfen.
Das ist, das ist ein einzelnes Prolog-Programm,
sehr viele unterschiedliche Programmprogrammabläufe verursachen kann.
Wir haben die Datenbasis und unser Programm läuft los, wenn wir die Datenbasis festfragen.
Und wenn wir was anderes fragen, kriegen wir einen anderen Programmablauf.
Wir müssen dasselbe nicht vor- und zurückschreiben. Es ist in
alle Richtungen da. Und die Relationen, die wir aufgeschrieben haben,
mit ihren Variablinder drin, die benutzen wir als Zugriffsmethode auf Datenstrukturen.
Also all unsere Daten stecken auch in Relationen drin und
das Abgleichen einer Anfrage mit Variablen drin, führt dazu,
dass wir aus diesem Tupel in der Relation einzelne Werte rausnehmen, eine Variable binden.
Und einfach durch die Anfrage kriegen wir einen Zugriff auf Bestandteile
einer Datenstruktur. Wir benutzen das sowohl als Konstruktor,
um Einträge in der Datenbank zu machen oder um eine Um
ein Objekt in der Anfrage zu erzeugen und um die Werte rauszukriegen,
sehen wir einfach nur da, wo wir etwas haben wollen,
eine Variable vor.
Und die Variable wird gebunden und damit haben wir die Selektoren.
Also wir brauchen in Pro-Log keine Datenstrukturen, keine Klassen,
nichts an Datenstrukturen zu schreiben, sondern einfach,
indem wir Dinge hinschreiben als Ausdruck.
Existiert das, ja? Also wir schauen uns in diesem
Kapitel jetzt erstmal an, was so wesentlich ist und sehen dafür,
das wird hoffentlich dann am Beispiel ein klarer.
Wir schauen uns typische Programmierprobleme an, die wir damit gut lösen können.
Und am Ende gebe ich Ihnen noch einen kleinen Einblick wie wir
das Pro-Log implementiert haben in Reckett, um es benutzen zu können.
Also sie können von ihrem Recket aus mit das Pro-Log in Recket aufrufen.
Das ist in der SE3-BIP drin und dann loslegen und
von Reckett aus kleine Teilprobleme eben auch in Pro-Log lösen.
Ich habe das nicht komplett selbst geschrieben,
sondern habe die Commonlisp-Implementation, die Peter Norweg gemacht hat,
für Pro-Log in Commonlisp genommen und das ein bisschen modernisiert und angepasst und in Recket neu geschrieben.
Das ist So, schauen wir uns erstmal die Datenbasis an.
Da stehen Klauseln drin. Ja, also das ist einfach
Zusicherung über die Anwendungsdomäne, über die Welt stehenden Klauseln da.
Und die gibt es in zwei Arten. Das eine sagt einfach,
mein Auto ist rot, der Himmel ist blau.
Das sind eben Fakten. Ja, und die können wir benutzen,
um eine Relation zwischen zwei Sachen, Objekt und Farbe oder so darzustellen.
Und dann haben wir Regeln. Und die Regeln beschreiben,
wie wir aus anderen Fakten neue Fakten folgern können.
Auch das sind also zwei Arten von Klauseln
Ja. Also wir würden, wenn wir Bevölkerung beschreiben wollen,
hier in unserem Pro-Log in Recket einfach machen, Klammer auf,
und wir die für eine Relation Population definieren,
Bevölkerung San Francisco, 750.000, Klammer zu.
Oder Hauptstadt ist, Moment, Hauptstadt von Kalifornien ist Sakramento,
da haben wir jetzt die Capital Relation eingeführt. Wir müssen nicht groß definieren,
es gibt eine Relation und die soll so heißen,
wir können einen Kommentar schreiben, der die Signatur beschreibt,
wir sollten den Kommentar schreiben, der die Signatur beschreibt,
damit man sich erinnert,
in welche Position kommt jetzt die Bevölkerung,
wo kommt der Name
Wir erzeugen die Relationen, indem wir auch nur ein einziges Tupel
aus dieser Relation aufschreiben oder es als Klausel erscheint in unserer Datenbasis.
So in dem eigentlichen Pro-Log würde man es so schreiben,
die Klammer ist ein bisschen verrutscht.
Erst kommt der Name der Relation. Dann kommt die Klammer und
außerdem Kim Komata rein und am Ende noch ein Punkt.
Punkt und Komma ist nicht so gut im Brecket.
Deshalb haben wir einfach diese abkürzende Version genommen. Kein Punkt,
kein Komma und die Klammer am Anfang,
dann passt das sehr schön in die Syntax von Recket
Gut. Also die folgende Relation, die habe ich
mir bei Wolfgang Menzel ausgeborgt, aus seinem Prolog-Skript.
Wir wollen mal eine Relation schreiben, in der wir schreiben, wer mag was.
Und da könnten wir jetzt also sagen, um zu sagen,
dass Kim Robin mag und Sandy Maglie und Kim und Robin mag Katzen,
können wir einfach aufschreiben, Klammer auf, Likes,
Kim Robin, Likes, Sandy Lee, Sex,
Sandy Kim, Likes Robin Cats.
Indem wir einfach nur diese Fakten aufzählen,
da kommen wir ziemlich weit, aber es gibt Probleme,
wo wir das nicht ausreicht.
Wir können damit, wenn wir ungeduldig sind,
wirklich nur endliche Relationen aufzählen
Also für unendliche würden wir ein bisschen länger brauchen und würden
nicht fertig werden. Also alle, das wäre endlich durch Off-Zühlung
im Kopf schreiben können, können wir einfach so machen.
Und um jetzt diese Relation in die Datenbank einzutragen,
da brauchen wir ein neues Sprachelement in Record.
Das ist das Sprachelement, was wir einführen müssen,
um mit Full-Log in Racket Lust zu legen.
Wir brauchen das Assert-Makro. Also ich habe Ihnen in der SE3-Bib definiert,
ein Special-Form-Operator, der beginnt mit so einem Linksfall und einem Strich.
Und das ist der Name eines Makros und dieses Makro
trägt Zusicherung mit Assert in die Datenbasis sein.
Also um jetzt dem System klarzumachen, dass Kim Robin mag,
würden wir, also machen Klammer auf,
dieses Assert-Makro und dann das Fakt, was da rein soll.
Wir würden, wenn wir in Rackic sowas wie ein Like
schreiben wollen, dann müssten wir gerichtet arbeiten.
In Racket können wir eine Funktion definieren,
Likes und Da können wir dann fragen, wen mag Sandy?
Ja? Also nur eine Funktion Likes und wir fragen,
wem mag Sandy denn alles? Da würden wir sagen,
likes Sandy und wir kriegen die Liste Lee und Kim.
Und wenn wir jetzt wissen wollen, die Gegenrichtung,
und wir mag nur unseres Handy, ja?
Oder den Lee oder so, da würden wir eine zweite Funktion brauchen,
Likeers off, und da geben wir jetzt Lee an und kriegen die Sandy raus.
Also in der funktionalen Welt ist die ganze Berechnung gerichtet.
Wir haben zwar Relation, aber wir implementieren sie als Funktion,
wo wir eben für Argumente Werte kriegen
Und wenn wir die Umkehrrichtung brauchen, brauchen wir die Umkehrfunktion.
Also in Relation, in Reckett würde immer mit zweimal geschrieben werden,
für jede Berechnungsrichtung eine in Pro-Look, haben wir richtungsunabhängigkeit.
Und das Berechnen, das Suchen geschieht, indem wir Variable vorsehen.
Also wir haben jetzt gesagt, wen Sandy mag.
Und wir können jetzt Anfragen an Pro-Lok stellen. Die Datenbank ist da,
da haben wir alles, was wir über Vorliebenwissen reingetragen.
Und wenn wir jetzt wissen wollen, na,
wen Marc Sandy, würden wir eine Anfrage stellen?
Likes Sandy, Sandy ist fest vorgegeben als Variablenbindung.
Und dann sehen wir eine Variable vor. Huh, wen
mag sie denn? Und unser Pro-Log in Recket würde uns dann sagen,
dieses, dieser Ausdruck, den wir als Anfrage gegeben haben,
wird erfüllt und es war,
wenn man die Variable Huhm an Liebendet oder wenn wir die Variable Huhm an Kim bindet.
Und wenn wir jetzt die Gegenrichtung haben wollen, wen Wer mag Lee? Ja,
dann fragen, würden wir die zweite Variable hier,
den zweiten Positionen an Liebenden und für die Personen, die gerne hat,
eine Variable vorsehen und da würden wir eine einzige Antwort zurückkriegen, Sandy.
Also im Pro-Log nennen wir diese beiden Arten,
Anfragen zu stellen, Instanzierierungsvarianten. Instanzierungsvarianten, Saliierungsvariante sagt,
welche Ausdrücke haben wir als Konstante vorbelegt und welche haben wir
freigelassen,
um Antworten zu kriegen
Ein Poll-Up-Programm ausführen, besteht darin, dass wir einfach eine Anfrage schreiben.
Und je nachdem, wo wir Variable vorgesehen haben,
wird es in die eine oder die andere Richtung ausgeführt.
Und das funktioniert, solange wir reinen Gewissens komplett in der Prolog-Welt bleiben,
reinstrengen, relational programmieren und nicht auf die Idee kommen,
funktionale Ausdrücke reinzuschreiben. Das sage ich schon mal vorweg.
Genauso wie uns Imperative Sprachelemente im Funktionalen Programmierstil Die Bezugstransparenz vermasseln
, vermasseln wir uns im relationalen Programmierstil. Diese richtungsunabhängigkeit,
dass wir an beliebigen Stellen Variable reinsetzen können und dann ist
die Berechnungsrichtung und Suchrichtung und der andere, die vermasseln wir uns,
wenn unsere Klauseln irgendwo funktionale Ausdrücke enthalten.
So, der zweite Typ von Klauseln, das sind die Regeln.
Und da beginnt eigentlich die Mächtigkeit, die über eine normale relationale Datenbank hinausgeht.
Also mit dem, was ich eben gezeigt habe,
benutzen wir ProLog als relationale Datenbankschreiben,
Relationen können suchen,
können fragen
Brauchen wir nicht wirklich pro Log für. Schönes Prolog,
es kann, aber die Mächtigkeit von Prolog kommt,
wenn wir unendliche Relationen beschreiben wollen.
Und diese unendlichen Relationen können wir beschreiben,
indem wir Ausdrücke schreiben, die Schluss folgern können.
Also hier haben wir jetzt den zweiten Typ von Klauseln,
das sind Regeln und die beschreiben jetzt die Abhängigkeiten zwischen Fakten.
Wenn wir sagen wollen, senden die mag jede Person,
die Katzen liebt, dann würden wir sagen,
die Relation Likes ist wahr für das Argument Sindy,
Und hier ist irgendeine Variable Cen, die liebt irgendeinen X.
Wenn wir beweisen können, dass dieses X jemand ist,
der Katzen mag. Ja? Und auf diese Weise,
indem wir hier Bedingungen formulieren und damit einfach Vorbedingungen prüfen.
Für Variablenbewegungen können wir unendliche Relationen erzeugen.
Und uns euren Totschleifen begeben übrigens. Aber das ist ein
kleiner Nebeneffekt davon. Also das ist das Kernstück von dem,
was man im Pulllock machen kann,
dass wir solche Regeln aufstellen können
Und die können wir in zwei verschiedene Arten lesen.
Also das ist jetzt auch wieder wichtig. Wir können das im ProLab-Programm
erstmal mit rein deklarativ lesen. Es ist eine logische Zusicherung,
in der steht, die Sandy mag fix,
wenn X Katzen mag und für alle X,
die denkbar sind, gilt das.
Aber wir können es auch operationaler prozedural lesen. Und das
ist der Punkt, wo es zu einer Programmiersprache wird,
weil wir nämlich durch eine Anfrage dann Berechnungen auslösen können.
Und dieser Berechnungsprozess, der ausgelöst wird, der sieht die operationale Semantik von dem Ganzen.
Also als Teil des Prolog-Programms sagt uns das,
wenn du beweisen willst, ob Sandy irgendetwas mag,
dann beweise, dass dieses irgendetwas Katzen mag.
Ja? Und hier sind wir an einer Stelle, wo man schon sieht,
dass Prolock natürlich in einer Art Backtracking oder Rekursion eintreten kann.
Wenn wir die Anfrage kriegen, Max, Sandy, John.
Und wir stoßen jetzt auf diese Regel,
dann wird das Prolock-Programm sein, aha,
ich habe hier etwa Wissen über Setteny und ich weiß,
wie ich zeigen kann, ob Sandy etwas mag.
Also stürzt sich mir jetzt drauf, diesen zweiten Teil zu beweisen.
Ja, Mark Tom,
Hat ProLog eine neue Anfrage generiert, die jetzt abgeglichen wird gegen die Datenbank
und wird versucht zu zeigen, ob Tom dann Katzen macht.
Also was wir so oft geschrieben haben, wie die Regeln aufgebaut sind,
das nennen sich Backwatch-Shaning oder auf Deutsch Rückwärtsverkettung.
Und heißt, dass wir vom Ziel ausgehend rückwärts die Voraussetzungen zu beweisen versuchen.
Man könnte Relation prinzipiell auch in die Gegenrichtung machen, ja,
Forward-Shaning. Das passiert zum Beispiel in dem General Problem-Soilver,
den ich Nächste Woche noch zeigen möchte.
Jan? Also pro Lock aber, das verwendet nur das Backdoor-Chaining.
Es wird immer versucht, Fakten zu beweisen und die Regeln zu finden,
das, was in der Anfrage ist, erfüllbar machen.
Deshalb haben wir eben auch in der Semantik diesen Pfeil hier.
Ja, der betont eben die Semantik. Ja,
dass wir hier sagen, eine Regel, so geht es eben wegwärts.
Die zeigt, das ist übrigens jetzt unser Essort-Makro,
das hier nochmal aus der Sicht Backdoor-Chaining gesehen wird,
ja.
Nochmal zu den Komponenten einer Klausel, wie wir sie hier geschrieben haben.
Also das hier ist das Asserd-Makro. Das wird dafür
auch verwenden. Das ist der Kopf der Klausel,
das ist immer das, was gegen die Datenbank abgeglichen wird.
Und das ist der Körper der Klausel oder der Body.
Und das ist das, was wir dann rekursiv versuchen zu beweisen,
wenn das Programm losackert. Und das Schöne ist eben,
wir können die Hände in den Schuss legen und einfach warten,
dass Prolog fertig ist.
Das macht für uns das Backtracking. Das müssen wir nicht selbst
jedes Mal neu programmieren, wenn wir ein Suchtproblem haben.
Und Fakten, die einfach so gelten, Die können wir
als spezielle Klauseln ohne Körper betrachten. Körperlose Klauseln,
die sind eben wahr, an- und für sich,
ohne irgendwelche Sven und Aber, in Vorbedingungen, die zu prüfen werden.
Also die Klausel, die sichert uns zu,
dass der Kopf der Klausel wahr ist, wenn alle Vorbedingungen im Klauselkörper wahr sind.
Das ist hier auch nochmal betont. Also die Vorbedingungen,
die wir hier aufhören, die sind implizit alle mit dem
logischen und verbunden und alle müssen erfüllt sein.
Wir sehen auf diese Stelle auch schon an dieser Stelle,
wie wir in Prolog ein logisches Untin kriegen,
das Logische und zwischen Bedingungen wird eben erzeugt,
indem wir die Dinge alle in einer Klausel als Vorbedingungen auffüllen.
Das Logische, oder? Das bekommen wir, indem wir
einfach für mehrere Klauseln schreiben. Wir haben ein Oder zwischen
den Fakten, indem wir sie einfach separat hingeschrieben haben.
Das eine kann wahr sein, das andere kann wahr sein,
ein logisches oder machen wir, indem wir mehrere Klauseln hinschreiben.
Und kriegen wir im Vorbedingungsteil der Klausel, im Körper der Klausel. So,
was wir hier jetzt geschrieben haben, Kim Marc,
irgendein X, ja, oder eine X, eine X,
wenn dieser X die Clique mag, nämlich Liebenhack und Kim Marc, ne?
Gut, das Binden der Variablen, das ist jetzt neben dem Backtrack in die zweite,
das zweite wichtige Werkzeug in Pro-Look, im relationalen Programmieren die Unifikation, ja?
Und wir können uns Unifikation vorstellen,
in einfacher Weise als Verallgemeinerung des Pattern-Matching,
was wir schon kennen, als das Eliza-Programm besprochen haben,
da haben wir ja, im Wesentlichen auch schon regelbasiert programmiert,
indem wir eine Datenbasis vorgesehen haben, lauter Regeln aufgezählt waren,
die beschrieben haben, wie Eliza auf eine Frage gestellt wurde,
Anfragen antworten soll.
Und da wurde immer versucht, die Anfrage abzugleichen mit diesen Regeln.
Und das geschah mit Pattern Matching. Die Unifikation verallgemeinert,
diesen Pattern Metze, ist nicht unsymit, sie ist symmetrisch.
Also die Ostdrücke auf beiden Seiten der Regeln dürfen Variable haben.
Und Variablen können mit anderen Variablen unifizieren. Die müssen nicht auf Konstanten treffen,
wie wir es im Pattern-Matching hatten. Und das heißt,
dass wir sowohl in der Anfrage als auch im Muster Variablen
haben dürfen und die müssen logisch konsistent gebunden werden mit Ko-Referenz.
Also beim Pattern-Matching in die Leiter hätten wir es so gemacht,
hier ist unser Muster mit zwei Variablen drin,
aber es wurde abgeglichen mit etwas, was keine Variablen hat.
Und da haben wir den rausgekriegt, ja, das kommt,
das trifft zu, wenn wir Y11 binden und X an 2.
Die Unifikation in Prolock und Die Implementation im Prolog in Reckett,
da haben wir stattdessen eine Funktion Unify. Die würde diesen Ausdruck nehmen,
eine Variable X hat und einen zweiten,
eine Variable Y und uns dann sagen, das ist erfüllbar,
wenn wir X an 2 binden und Y an 1.
Und die ist nur Bein, nimmt es hier hinten,
ist viel im Pattern-Matching.
Es muss darauf geachtet werden,
wenn wir jetzt tief in der Rekursion etwas zu unifizieren versuchen,
dass es nicht ein Konflikt ist mit Variablenbindungen, die schon existieren.
Deshalb haben wir hier noch das dusselige No-Bindings stehen. Zu Anfang
haben wir noch keine Bindung, aber wenn wir da ins Tracking einsteigen,
kann da eine Lüste mit schon teilgebundenen Variablen entstehen.
So, wann anifizieren zwei Ausdrücke? Das gilt im Pull-Ock
in Recket, genauso wie im eigentlichen Pull-Up.
Wenn die Stelligkeit des Prädikats stimmt, ja,
wenn die Namen der Prädikate stimmen und wenn gleiche Variablen gleiche Werte bezeichnen,
das ist eben die Eigenschaft der Choreferenz.
Ich kann das X nicht an einer Stelle an zwei und ein anderer an drei binden,
sondern es muss immer an denselben Wert gebunden werden.
Ja. Wir können feststellen, ob zwei Variablen identisch sind,
einfach indem wir sagen. Wir unifizieren F von X mit
F von Y und da kriegen wir das Ergebnis zurück.
Das ist wahr, wenn X gleich Y ist.
Und wir können, das ist eigentlich der schicke Variablen,
als identisch erklären auf diese Weise. Selbst wenn sie beide
noch keinen Wert haben, wir halten nur mal fest, die sind identisch.
Wenn sie an der gleichen Stelle stehen und im Ausdruck
und der wird unifiziert und folgt daraus X und Y sind identisch.
Und wenn sie einen Wert bekommen, muss er für beide derselbe sein. Also Wenn wir jetzt in unserem Prolog,
in Reckett mal die Funktion Uni-Fire aufrufen, müssen wir eigentlich nie selbst tun,
aber beim Testen können wir es tun, ja,
dann würde uns der hier feststellen,
wenn wir als Unifizieren, A plus A,
es soll gleich null sein und X plus Y soll gleich Y sein,
dann wird aufgrund der Stelligkeit unifiziert, A ist gleich X,
Y ist gleich A, daraus folgt,
weil A gleich A ist,
das X gleich Y ist und von Y wissen wir,
dass es null ist und wir kriegen das Ergebnis raus,
0 plus 0
gleich null
Einfach, weil die Unifikation durch die Anfrage der Unifikation
wird gesagt, dieser Ausdruck ist strukturgleich mit dem.
Also such nach Möglichkeiten die Variablen so zu bewegen,
zu belegen, dass beide Ausdrücke erfüllt werden und die einzige Lösung,
die dazu führt,
dass sowohl A plus A gleich Null ergibt und X plus Y gleich Y,
in dem Fall erfolgt dann,
wenn X gleich A ist,
wenn Y gleich A ist,
daraus folgt
Durch die Bindung an dieselbe Variable A, das X-gleich A
ist Y gleich A, dass sie identisch sind.
Und weil uns nichts anderes übrig bleibt,
als Y den Wert 0 zu geben,
haben wir damit auch dann X,
Y und A allen dreien den Wert 0 gegeben.
Und das ist unser Ergebnis. Was hier geschehen,
ist es wie beim Pattern-Matching. Die Ersetzung ist rein syntaktisch
. Pro Log in Record wird nicht drüber nachdenken,
dass das hier eine Addition und eine Gleichheit ist.
Das sieht es nicht als arathmetischen Ausdruck.
Es sieht nur die Syntax dieses Ausdrucks
Also man könnte da auch völlig falsche Dinge rauskriegen.
Ich kann auch diese Weise auch, durch diese Anfrage,
wann ist A plus A gleich 2 und X plus Y gleich Y rauskriegen,
das 2 plus 2 gleich 2 ist und dass wir mathematisch gesehen Unsinn,
aber strukturell im Sinne der Unifikation richtig.
Also die Unifikation ist unser zentrales Programmiermittel. In der Ergänzung zur Datenbasis in Prolog
. Sie dient zum Binden von logischen Variablen an Werte.
Das haben wir schon gesehen. Wir kriegen raus,
Y ist 0, A ist gleich Y und so weiter
Sie ist ein Selektor für die Teile der Struktur, das haben wir hier gesehen.
Also eins, wir haben hier einfach eine Struktur von Dingen,
die mit Plus verknüpft sind und indem wir eine Variable hinschreiben,
selektieren während diesem Teil. Wir können über die Unifikation Strukturen bauen,
wenn wir etwas vorgeben und Variablen reintun und die Variable werden an Werte gebunden,
haben wir anschließend die Struktur,
wo diese Platzhalter mit Werten belegt sind, also konstruieren wir damit.
Wir können damit Listen rekursiv aufbauen und so.
Und wir können mit der Unifikation auf Gleichheit testen,
indem es immer in den gleichen Stellen auftaucht,
können wir testen, ist es gleich oder nicht.
Also wenn wir jetzt im Pro-Log loslegen wollen,
im Programmieren, dann schreiben wir als erstes unsere relevanten Relationen
als Klauseln hin und füllen unsere Datenbasis mit dem Assort-Nakro.
Und damit füllen wir nach und nach unsere Datenbasis. Dann,
um es zu benutzen, stellen wir clevere Fragen im Inshor-Pritte.
Also irgendeinen Ausdruck, wo wir, wenn wir wissen wollen,
Peter Paul ermordet, würden wir fragen, Mörder, Peter Paul.
Und dann kriegen wir vom Pro-Lab wahrscheinlich Not zurück,
weil das einzige, was unser Pro-Look zurzeit über Mörder weiß,
ist, dass der Gärtner der Mörder ist.
Aber wenn wir eben eine Variable vorsehen, wird uns gesagt,
ja, der Mörder ist der Gärtner. So, die Sucher,
die wird jetzt automatisch gemacht, wir haben die Anfrage gestellt,
die wird mit Unifikation, mit der Datenbasis abgeglichen.
Und wenn wir auf Regeln stoßen, die sagen,
ich muss Vorbedingungen prüfen, gehen wir da rekursiv wieder rein, mit Backtracking.
Und an jeder Stelle, wo wir Entscheidungs- Möglichkeiten haben,
in dem Backtracking, wir haben alternative Dinge,
die in Frage kommen,
dann wird dieser Entscheidungspunkt sich gemerkt und das Backtracking geht später an die Stelle zurück,
wo wir eine zweite Klausel hätten ausprobieren können aus der Datenbasis.
Und wenn wir, wenn eine Lösung gefunden wurde,
die unsere Anfrage erfüllt, erfahren wir sie,
die wird angezeigt und dann können wir nach Alternativen fragen.
Wir geben einfach einen Silmitolon ein und dann wird wieder gesucht,
ab der Stelle, wo man die Lösung gefunden hat,
ob es noch weitere gibt und irgendwann wird Prolog entnervt,
Prono sagen,
was heißt,
es gibt keine weiteren Lösungen für unsere Anfrage
Ja. So. Jetzt müssen wir also auch noch Anfragen schreiben.
Das ist das zweite Makro, was wir eingebaut haben.
Und das zweite neue Sprachelement,
beachten Sie unsere Sprache pro Log in Reckett,
besteht im Wesentlichen aus einer Datenstruktur der Datenbasis, die implementiert ist.
Vielen, vielen Funktionen, die aufgerufen werden, will Unify
und Unifeier. Und dann haben wir zwei neue Sprachelemente.
Das ist wirklich alles. Diese beiden Sprachelemente ist einmal
der Pfeil nach links fürs Assert.
Und das andere ist das Anfragemakro
Ich habe das Makro noch nicht erklärt,
das schauen wir uns das nächste Mal an,
wie man die Sprache erweitert, neue Elemente reinbringt.
DevMakro beschreibt hier ein neues, syntaktisches Sprachelement,
in dem mir der Name eingeführt wird.
Das ist eben das Anfrage-Makro. Und mit uns Rest sagen
wir, hier kommt jetzt eine Liste von möglichen Argumenten.
Und das sind die Ziele, die zu beweisen sind.
Und was macht dies Makro? Das ist der,
es erzeugt einen neuen Code. Und dieser Code besteht im
Aufruf der Funktion Top-Level-Proof. Ersetze die Variablen, quotiert,
wo diese ganzen Variablen, die aufgeführt sind.
Also des Komma-Goals expandiert die Liste, die hier übergeben wird,
als Einzelelemente der Liste wird dort angesetzt.
Ja, das macht diese, also das Makro hier als Neues,
syntaktisches Spracheelement, ist transformiert.
Die Anfrage, die wir geschrieben haben, in eine neue.
An der Stelle, wenn wir mit das Makro arbeiten,
verlassen wir die vorgezogene Auswertung,
die wir in Recket haben und haben eine Lazy Evaluation
Die Argumente werden hier einfach als Text weitergegeben und werden hier im Body des Makros eingesetzt.
Und erst wenn das alles hier eingesetzt wurde, textuell,
dann beginnt die Auswertung. Also dafür brauchen wir eigentlich die Makros,
um die vorgezogene Auswertung von Drecke zu unterbinden und einen anderen
Racked-Ausdruck mit diesem Makrotransformer draus zu machen.
Und das so zu erzeugen. Also ich muss gestehen,
an der Stelle habe ich den faulen Weg gewählt.
Wir brauchen nur zwei Sprachelemente und das Makro ist eine sehr
bequeme Art Zwindel, um neue, syntaktische Sprachelemente einzuführen,
ist nicht die heere Wissenschaft, wie sie in Record vertrieben wird.
Also Makros gilt, wenn man eigene syntaktische Sprachelemente einführt als Dirty.
Ich werde noch über die Fallstricke von Makro sprechen,
um ihnen auch zu erklären, warum das eigentlich ganz schön faul von mir war.
Record hat eine saubere Art, um neue Sprachelemente einzuführen,
die feinen Syntax. Nur um ihm die zu erklären,
ich habe es mal durchgerechnet und versuchsweise aufgeschrieben,
müsste ich fünf Doppelstunden drauf verwenden, um die Feinheiten zu erklären.
Dann kommen wir nicht mehr dazu,
das zu benutzen
Deshalb Quick und dort, die Deathmakro schreibt sich schnell und einfach
und wir brauchen nur zwei davon, nehmen wir mal diesen Weg.
Aber ich werde ihnen das nächste Mal noch erzählen,
warum eigentlich die Fallen Syntax das Saubere gewesen wäre.
Und wenn sie je in einem Projekt neue syntaktische Sprachelemente einführen
wollen, nehmen sie die feinen Syntax.
Weil das sauberere ist, wo all diese Makro-Fallstricke nicht auftauchen.
Gut. Ja, also das erzeugt alle Variable durch neue
eindeutige Namen und versucht die Anfrage als wahr zu beweisen.
Die Sreeplace war es, erzeugt eindeutige Namen für den Code
Okay. Eine Anfrage gilt als wahr,
wenn die Zusicherung in der Datenbank dies auch unterstützen.
Ja, und ansonsten wird sie als falsch.
Also wir haben in der ProNock-Welt die Closed World Assumption.
Alles, was wir nicht anhand der Datenbasis beweisen können,
von dem, nehmen wir an, dass es nicht wahr ist.
Also wenn wir eine Anfrage über etwas kriegen,
von dem wir nichts wissen, nehmen wir an,
die ist falsch. Könnte sagen, weiß ich nicht,
aber nö, das ist nicht die Antwort.
Wir behandeln es als logisches Falls. Ja,
wenn eine Anfrage erfolgreich mit der Datenbank unifiziert werden kann,
dann wird ein Jess ausgedruckt
Es werden die Variablenbindungen gedruckt. Und dann werden die Benutzer
gefragt, ob sie noch weitere Ergebnisse sehen möchten.
Und da kann man einen Semikolan eingeben und erfährt die weiteren Ergebnisse.
Okay. Nehmen wir mal ein einfaches Beispiel und gucken wir
uns erst mal an, wie wir Prolog als Datenbanksprache benutzen können.
Und dann als nächstes Prolog, als detektive Datenbank.
Also eine reine relationale Datenbank unter Beschreiben immer Familienverhältnisse.
Ja, für eine Relation Parents. Und die ist dreistellig,
hat eine, da gibt es eine Mutter,
gibt einen Vater und es gibt das Kind
Wenn wir jetzt eine kinderreiche Familie aufzählen wollen,
würden wir also Perrens schreiben, immer Mutter,
Vater und dann für jedes Kind einen eigenen Eintrag.
Und wir können jetzt schon mal loslegen und fragen,
wer sind die Eltern von den Kindern und so weiter?
Ich habe hier mal eine Antafel für meine Stammfamilie.
Also es gibt hier das Kind Peter und dessen Eltern sind
fairer und Viktor und die Eltern von Vera sind Maya und
Martin und Viktor hat die Eltern Mona und Mike und sie
haben ja wieder die Eltern von der Mona und so weiter.
Und das schreiben wir jetzt einmal hin, sichern das zu,
als Fakten in unserer Datenbank. Und ich habe hier mal als Kommentar reingeschrieben,
was das Muster dieser Relation ist, das Prädikatsche Maparents,
also Name ist Peerents und die drei Argumente sind Mutter, Vater und Kind.
So, also die Eltern von Vera, Quatsch,
die Eltern von Peter sind Vera und Victor,
die Großeltern von Peter, da schreiben wir Parents,
Maya, Martin und so weiter.
Und da haben wir die Tafel. Und jetzt können wir
fragen, wer sind denn die Eltern von Peter?
Jetzt benutzen wir unsere Relation, die relationale Datenbasis,
die wir deklariert haben,
beschreiben die Anfrage Parents
Bitte als Variable, Vater als Variable, also alles,
was mit einem Ausrufe, mit dem Fragezeichen beginnt,
ist ein Variablennamen, ja, und ein Festtier,
Peter als, ja, literales Fakt und da erfahren wir denn,
Mutter und Vater, wenn wir es wissen wollen,
ob noch mehr Eltern da sind für Peter,
erfahren wir nein, ja.
Wenn wir jetzt anfragen wollen, wer die Großeltern sind,
dann müssen wir, dann soll ich mir überlegen,
wie beschreibt man Großeltern überhaupt,
ja, Großeltern sind die Eltern der Eltern,
also Um nach den Großeltern zu fragen von Peter,
da würden wir erst mal die Eltern von Peter feststellen mit den Variablen,
Mutter und Vater.
Und wenn wir die haben, dann fragen wir hier nach den Eltern der Mutter.
Und die taufen wir grennt mal und grennt Paar.
Und hier wird eben, was immer für Mutter gefunden wurde, eingesetzt.
Und die Eltern vom Vater, die schaffen wir hier mal Oma und Opa.
Also wir sehen hier die Verwendung der Co-Referenz. Die Mutter,
die hier zurückkommt, muss dieselbe sein wie da.
Und der Vater, der hier gebunden wird,
muss derselbe sein
Wer hier in der Elternrelation, für die Eltern der Eltern eingetragen wird.
Und dann erfahren wir als einzige Lösung, Großmutter ist Maya,
ein Grenzpaar ist Martin und Oma ist Muna und Opa ist Mike.
Und wir erfahren auch noch, wer Mutter und Vater sind,
danach wollten wir eigentlich gar nicht fragen.
Und wir können eine ganze Relation aufzählen,
indem wir einfach alle Variablen unbelegt lassen,
indem wir nur Perlens schreiben und für sämtliche,
mögliche Argumente der Relation hier Variablen vorsehen,
kriegen wir
eine vollständige Aufzählung
So, jetzt wollen wir Variablen unterdrücken,
die wir nicht brauchen und in der Antwort nicht sehen wollen.
Und wir können sie anonymisieren, indem wir einfach ein Fragezeichen
da reinsetzen. Wenn wir wissen wollen,
wer ist der Vater von Walter oder die Mutter,
dann würden wir hier in dem Fall,
wo wir die Mutter wissen wollen, für Mutter eine Variable vorsehen.
Die Namen hat und für den Vater eine anonyme Variable.
Und dann erfahren wir Matsa Petra und über den Vater erfahren
wir nichts. Auch wenn wir die Datenbasis so aufbauen,
dass unsere Relation sehr viele Argumente haben.
Also eine hohe Stelligkeit
Kann es sein, dass wir uns wünschen, dass wir
verkürzte Relationen haben, die nur Teile in der Stelligkeit verwenden?
Und das können wir durch eine Projektion machen. Also wenn wir
eine Mutterrelation haben wollen, wo ist der Vater gleichgültig ist,
dann würden wir diese Projektion so machen,
dass wir eine neue Relation Mother einführen mit der Variablen
Mutter und Kind und sagen,
die Person ist eine Mutter von einem Kind, wenn wir beweisen können,
dass die Mutter in der Elternrelation mit dem Kind steht und
den Vater haben wir hier wieder unwichtig gemacht durch eine anonyme Variable.
Oder für die Vaterrelation würden wir sagen,
Jemand ist Le Relation, Vater, zu einem Kind,
wenn die Elternrelation den Vater des Kindes bestätigt.
Und da ist die Mutter dann ungünstig, unwichtig. Also das,
was wir da jetzt schon gesehen haben, ist,
wir können Projektionen machen, wir können sie vereinfachen,
wir können in mehrere Richtungen fragen, die ganze Relation aufwählen.
Und jetzt wollen wir es noch als deduktive Datenbank benutzen und
zum Beispiel Alle Vorfahren ausrechnen oder testen wollen,
ob jemand Vorfahrer einer anderen Person ist. Und dann müssten wir modellieren,
dass Eltern entweder Vater oder Mutter sind, dass Großeltern,
die Eltern der Eltern sind und das Vorfahren Eltern von Vorfahren sind.
Das Ziel soll jetzt also sein, dass wir eine
neue Relation Pre-Decessor-Vorfahrer einführen. Und die sagt uns,
irgendeine Person ist ein Vorfahrer zu einer anderen Person,
wenn wir beweisen können, diese Pre-Decessor-Personen in der Elternrelation damit steht.
So, damit haben wir die direkten Vorfahren modelliert.
Und wenn wir jetzt die Vorfahren, der Vorfahren haben wollen,
dann werden wir rekursiv. Wenn jemand ist Pre-Decessor,
wenn dieser Pre-Decessor ein Elternteil ist,
von irgendeinem Parent und diesen Perian suchen wir,
indem wir rekursiv alle Vorfahren zur Person suchen mit diesem Parent.
Über diese Bindung hier wird rekursiv gesucht In einem Schritt wird
ein Elternteil gefunden, das Nächste, das Nächste, das Nächste.
Und so lange, bis wir eine Person gefunden haben.
Also, was man hier sieht, ist, dass wir
berechnete Relationen im Wesentlichen machen, indem wir die Regeln rekursiv schreiben.
Und wir sollten die Rekursion immer so schreiben,
dass man nicht in die Rekursion ansteigt, bevor man Wertebindungen hergestellt hat.
Deshalb ist es hier sehr wichtig, dass wir zuerst Parents sagen,
um hier eine Bindung herzustellen und dann in die Rekursion einsteigen.
Ja, ein Beispiel fragen wir hier nach der Großmutter.
Und jetzt suchen wir für Mike
die Vorfahren
Berufen, stellen also eine Anfrage,
wer ist ein Vorfahre von Mike und dann erfahren wir Nena
und dann erfahren wir noch iPhones und dann kommt es nur,
weitere Vorfahren wurden nicht gekommen.
Wir können jetzt aber diese Regulation, die die Vorfahren
gesucht hat, eben auch in die Gegenrichtung verwenden.
Das ist wieder die Stärke von ProLog,
dass wir jetzt nach den Nachkommen fragen können.
Wir fragen nach dem Nachkommen, indem wir hier einfach
die Variablenbewegung verdrehen. Für wen ist Maya eine Vorfahren?
Indem wir es einfach umdrehen. Und dann erfahren wir,
okay, Maya ist eine Vorfahren zu Vera und Peter
Okay. Also wir haben pro Lock jetzt benutzt,
einmal als relationelle Datenbank. Wir haben pro Lock benutzt,
um berechnete Relationen aufzustellen und damit rekursiv potenziell unendliche Relationen zu definieren.
Und jetzt wollen wir pro Lock noch ein Beispiel nehmen,
als Inferenzmaschine, um für uns komplizierte Logikrätsel zu lösen.
Ja. Mal als Deduktive Datenbank.
Und zum Warmwerden nehmen wir jetzt nochmal
den Nachkommenrelation
Also wir hatten hier unseren Peter, dessen Vorfahren kennen wir schon.
Jetzt heiratet er und jetzt fängt er an, Nachkommen zu erzeugen.
Und bei seinen Nachkommen können wir jetzt mal fragen,
wer sind eigentlich Geschwister? Zwillingsprädikat aufstellen. Das ist immer
die erste kleine Komplikation. Väter, Petra und Peter,
die haben hier also ihre reichliche Kinderschar und wir wollen jetzt mal wissen,
wer sind eigentlich die Geschwister von Vinnie zum Beispiel?
Und da zu definieren wir, jemand ist ein Zippling.
Also Geschwisterkind. Wenn X von Y,
wenn X und Y dieselben Eltern haben, ja,
also X hat hier die Eltern M&F und Y,
die Eltern M&F und dann sind sie Geschwister.
Kann es funktionieren? Versuchen wir es mal?
Wir fragen hier nach den Geschwistern von Walter.
Und so wie wir es definiert haben, ein kleines Problem.
Walter ist sein eigenes Geschwister. Pech gehabt. Da muss man
noch etwas uns einfallen lassen, wie wir dafür sorgen,
dass nicht man selbst als der sein eigenes Geschwisterkind aufgibt,
als der eigene Bruder angezeigt wird.
Also er musste noch ausdrücken können,
dass der Bruder einer Person ungleich der Person ist.
Und dazu benutzen wir ein Gleichheitsprädikat. Gleichheitsrelation müssen wir
im Vorlaub verreckend einführen. Die ist nicht fertig da.
Aber wir können das einfach so machen. Wir definieren hier
die Gleichheitsrelation, indem wir sagen, X muss mit Exunifizieren.
Also die beiden Therme müssen gleich sein.
Wir benutzen die Unifikation, um die Gleichheitsrelation auszudrücken. So,
und jetzt wollten wir aber nicht die Gleichheit, sondern wir wollen,
dass Walter nicht als sein eigener Bruder gemeldet wird und wir müssen auf Ungleichheit testen.
Und dazu müssen wir jetzt noch eine weitere Relation einführen.
Das ist in dem Fall der Nord-Operator. Und diese Ungleichheitsrelation,
die definieren wir einfach sagen, indem wir sagen,
sie soll wahr sein, wenn X und Y nicht gleich sind.
Und damit können wir jetzt unsere Geschwister so definieren,
X und Y sind Geschwister. Wenn die Eltern gleich sind,
ja, von X, die Mutter von X ist dieselbe,
ist dieselbe Person wie die Mutter von Y,
der Vater von X selbe Person,
wie der Vater von Y. Die müssen also unifizieren,
aber X darf nicht mit Y unifizieren.
Ja, und jetzt erfahren wir, okay,
will mal Werner Windy, Windfried sind Geschwister von Walter,
aber er selbst nicht.
Er ist nicht mehr sein eigener Bruder. Also die logischen Operatoren,
die wir haben, wenn wir das unten ausdrücken wollen,
dann machen wir es
In den Prämissen einer Regel, also alle Prämissen,
die dort aufgezählt werden, sind automatisch mit unverbunden.
Wenn wir die Dyssuktion brauchen, dann nehmen wir nicht
ein Uder, sondern schreiben einfach alternative Regeln.
Und wenn wir die Negation wollen, nehmen wir den Not-Operator.
Also jedes fehlt, wenn du Unifikation nicht stattfindet,
ist automatisch eine Negation hier. Die Variablen,
die wir gesehen haben, die eingeführt werden in unseren Klauseln,
die gelten nur innerhalb der Klauseln,
außerhalb können wir immer wieder X schreiben,
ist es ein anderes X als das,
was wir in der Klausel erwähnt haben
Es gibt keine globalen Variablen, keine geschachtelte Blockstruktur.
Also das Leben ist einfach. Und Variablen brauchen wir nicht definieren.
Die bekommen irgendwann ihre Werte durch Unifikation und dann behalten sie sie.
So, jetzt noch eine Hilfsrelation, die wir immer wieder brauchen.
Member in einer Liste. Wir brauchen natürlich auch Listen,
um lange Strukturen aufbauen zu können.
Eine Liste schreiben wir, indem wir den Kunstoperator verwenden.
Also als Syntax für Listen nehmen wir das,
was in Reckel
schon fertig ist
Klammer auf, ein erstes Element, ein Punkt,
der steht für einen Kunstapparater und dann kommt eine Restliste.
Und wir sagen also, hier,
ein Element Item ist Element der Liste, wenn es das
erste Element der Liste ist und dann kommt ein Rest.
Das haben wir hier geschrieben. Das sichern wir zu,
um unsere Member-Relation zu definieren. Und die Listen können ja
nur ein bisschen länger werden und haben nicht nur ein erstes Element für den Rest der Liste,
machen wir es rekursiv, wie wir es gewöhnt sind.
Ein Element Item, ist Element einer Liste,
die aus einem Element X besteht, irgendeinem Element X,
das uns nicht weiter interessiert und einem Rest.
Und das Item erfüllt die Memberrelation für den Rest der Liste.
Das haben wir hier definiert. Und jetzt können wir loslegen
und fragen, ob ein Element in einer Liste vorkommt.
Also fragen wir mal, ist das Element eins,
ein Element der Liste 213 und wir erfahren Yes.
Und wenn wir jetzt nach weiteren Möglichkeiten fragen, kriegen wir Not zurück.
Wiederholen wir die Frage und mit einer anderen Liste,
mit der Liste
Da kriegen wir einmal einen Jess zurück.
Das ist die erste Einzieher, die das erfüllt.
Und dann kriegen wir noch mal Jess zurück für die zweite Einzieher.
Und dann kommt das Low. Da haben wir gesehen,
das ist zweimal gefunden worden. Wir können die Member-Relation
jetzt benutzen, um Listen zu erzeugen als Konstrukte.
Ich hatte ja gesagt, wir verwenden die Unifikation, um Datenstrukturen aufzubauen.
Und am Beispiel der Listen können wir super sehen,
wie wir die Unifikation nehmen können, um eine Liste aufzubauen.
Wenn ich hier frage, ist das Element eins,
Element der Liste, die aus den drei Variablen A,
B und C besteht, Dann kriege ich zurück, ja.
Es ist das Element der Liste, wenn A,
ein Eins gebunden wird und B, irgendwas ist und C,
irgendwas ist, ja. Oder aber alternativ ist es die Liste,
wo B, ein Eins gebunden wird und A,
ist irgendeine anonyme Variable Exo und so gebunden und C,
ein C, ja.
Oder dritte Möglichkeit, C ist das Element 1 und A
und B sind irgendwelche Variable. Das heißt,
durch diese Anfrage, Nemba, bekomme ich drei mögliche Listen,
die alle dieses Element enthalten und kann so Listen konstruieren,
die dieses Element haben,
Ja, in dem funktionalen Programmierung begann der Spaß in
dem Moment, wo man Funktionen höherer Ordnung hatte.
Und im ProLog beginnt das, wenn wir Prädikate zweiter Ordnung haben.
Wenn wir Prädikate über Prädikate definieren können. Und Ergebnisse von Berechnungen
zusammenfassen und so weiter. Und ich werde Ihnen hier nur
ein einziges Prädikat zweiter Ordnung vorstellen, was wir immer brauchen.
Im ProLog, das war ein ziemlich mächtiges,
und das ist das Find Find All.
Find All ist ein Sprachelement, das Ein Prädikat,
dem wir ein anderes Prädikat als Argument übergeben können.
Und es findet alle möglichen Strukturen, die dieses Prädikat erfüllen.
So, Find All, schreiben wir so, Find All,
Klammer auf, Find All und dann kommt ein Term.
Der Term hier sagt uns, das ist ein Ausdruck,
der sagt, wie die gefundenen Resultate verbunden werden sollen.
Das Ziel ist ein Prädikat, was erfüllt sein muss und
die Liste ist etwas, worauf das Prädikat angewendet wird.
Und Term und Ziel sind über gemeinsam uninitianziierte Variable verbunden.
Gucken wir uns mal konkret an
Wir wollen alle Vorfahren einer Person haben und wollen die als Liste zurückkriegen.
So. Hier hinten wird eine Liste. Ein Enz ist das
aufgebaut, ja. Und hier ist die Struktur vorgegeben,
wie es gebaut werden soll, Predicessor, ja, Predicessor und die Person.
So, der Ausdruck muss erfüllt werden und dann kriegen wir alle Predicessors,
die dies hier erfüllen, gesammelt in der Liste Enz ist das, ja.
Also alle Vorfahren von Von Walter? Ja. Gibt uns dieser Aufruf,
gebt uns dann die Liste Alphon, Stefan,
Nick Norbert und so weiter und ein No, mehr sind nicht gefunden worden.
Oder alle Geschwister von Walter gibt uns das, was wir schon mal gesucht hätten.
Also bei Find All können wir übergeben, womit gesucht werden soll,
sagen, wie die Struktur dessen sein soll,
was gefunden werden soll und das kriegen wir als Liste zurück.
Okay, ich möchte Ihnen jetzt anhand von zwei Logikrätseln zeigen,
wie wir das Ganze verwenden können, um mit Prolock Aufgaben zu lösen,
wo wir sehr unstrukturierte Vorgaben haben, an Wissenseinheiten,
die wir alle in die Datenbasis reinschmeißen und dann soll pro Log sehen,
bis damit zurechtkommt.
Das eine ist ein sehr bekanntes Rätsel aus Scientific American
und dann habe ich einfach nur so zum Spaß,
weil ich das mal überprüfen wollte,
noch ein Rätsel aus den Harry Potter Büchern genommen.
Um zu sehen, ob die Bücher da konsistent sind,
ein bisschen Literaturkritik. Gut, das Rätsel aus dem
Scientific American, das ist schon etwas älter.
Sie werden sehen, die Vorgaben sind politisch unkorrekt
Aber die Leute saufen und rauchen, dass es eine Art
hat und haben Haustiere, die dem Artenschutz unterliegen.
Also das Rätsel müsste mal abgedatet werden.
Aber trotzdem. Also die Vorgabe ist die folgende.
Wir haben fünf Häuser, diese fünf Personen sind also Nachbarn.
Und in jedem Haus gibt es Bewohner,
die sich in ihrer Nationalität unterscheiden, die unterschiedliche Haustiere haben,
die unterschiedliche Sachen rauchen, die unterschiedliche Sachen trinken und auch unterschiedliche Haustiere haben.
Und die Frage ist, wo wohnt der Wassertrinken und
wer hat ein Zebra als Haustier
Ja, also was wissen wir über die Leute?
Wie gesagt, es ist total unstrukturiert.
Also wir wissen zumindest, dass die Nationalitäten alle unterschiedlich sind.
Dass die Getränke alle unterschiedlich sind, dass die Haustiere alle unterschiedlich sind.
Und dass Zigaretten geraucht werden und die Zigarettenmarken sind alle unterschiedlich.
Und die Häuser haben auch eine unterschiedliche Farbe. So,
und dann wird uns gesagt, der Engländer lebt im Roten Haus.
Und wir wissen, der Spanier hat einen Hund und im
Grünen Haus wird Kaffee getrunken. Der Ukrainer trinkt Tee,
das grüne Haus ist direkt rechts neben dem Elfenbeinfarbenenhaus.
Also indirekt wird uns gesagt, welche Farben die Häuser haben.
Aber nicht an welcher Stelle
die stehen
Und der Winston Raucher hat Schlecken als Haustür.
Und im gelben Haus trägt, raucht man cools.
Und im mittleren Haus wird Milch getrunken. Ja,
Norweger lebt im ersten Haus von links. Und chester viel
Traucher, den gibt es auch. Und der ist der
direkte Nachbar von Mann mit einem roten Fuchs als Haustier.
Kulsraucher ist direkt der Nachbar vom Pferdebesitzer. Lackis Schreckraucher trinkt Orangensaft.
Japaner raucht Paliamins und der Norweger wohnt neben dem blauen Haus.
So, jetzt kann man anfangen, sich die Häusland aufzumalen
und den versuchsweise Farben zu geben und die Getränke zuzuordnen.
In dieser Liste sieht man,
es sind fünf Getränke
Genannt worden. Es sind viele Farben genannt worden, ja.
Es sind verschiedene Haustiere genannt worden, aber man kann bei
keiner Regel das direkt fassen und sagen, wo ist es?
Ein klarer Fall, da lässt man Pro-Log probieren.
Und wir benutzen Pro-Log jetzt, um eine Datenstruktur aufzubauen,
die diese Regeln alle erfüllt.
Wir führen eine Relation Haus ein und die bringt die Dinge zusammen,
Nationalität, Haustier, Zigarette, Getränk und Hausfarbe.
Wir müssen für die Regeln, wo wohnt neben,
ist direkt benachbart und rechts von,
müssen wir noch
Prädikate definieren
Also das Next Tool in der Liste von Häusern, sagen wir,
ein Haus X ist in dieser Liste direkt neben dem Haus Y,
wenn die Liste so aussieht, XY und dann kommt die Liste.
Oder aber, wenn es umgedreht ist,
wenn Y die Relation Allreit erfüllt, innerhalb dieser Liste.
Und das müssen wir Allreit noch definieren. Also die beiden
Fälle gibt es so rum oder so rum. Allreit,
das linke und das Rechte, ist erfüllt,
wenn die Liste damit beginnt, zuerst das Linke,
dann das Rechte und dann kommt der Rest
Oder, wenn es rekursiv im Rest erfüllt ist, ja
, das sagt diese Regel. So, die Gleichheitsredaktion verwenden
wir auch wieder. Und jetzt können wir eigentlich schon,
uns anfangen, unsere einschränkenden Relationen zu schreiben, ja.
Das Zebra-Rätsel. Definiere ich hier jetzt als eine Anfrage?
Zebra bekommt eine Liste von Häusern, ja.
Und dann haben wir hier die Frage, gucken wir uns
nochmal die Frage an. Was war die? Wo wohnt
der Wassertrinker? Das soll die Variable B beantworten.
Und wer hat ein Zebra als Haustür?
Das ist die Variable Z
, ja
So. Ja, Wassertrinker und Zebra. Also das ist
unsere Anfrage. Und jetzt schmeißen wir da alles hinein,
was wir aus dem Rätsel sagen können.
Also sehr unterschiedliche Wissensarten, die wir wissen. Viele unterschiedliche Instanzisierungsvarianten.
Zunächst wollen wir, dass es genau fünf Häuser gibt.
Ja, also machen wir hier eine Liste aus fünf Häusern.
Bei der ersten Regel, da war gesagt worden,
der Norweger wohnt ganz links, also Haus, Norweger.
Und dann haben wir vier Einheiten, von denen wir nichts wissen.
In der Mitte wird Milch getrunken, das ist die Regel
Die haben, also schreiben wir hier in die Mitte,
einmal Haus hin mit der Instanzierungsvariante,
als Getränk ist Milch festgelegt und über die anderen drei Häuser wissen wir gar nichts,
ja, aber damit ist klar,
die Variable Haus muss fünf Stellen lang sein und schon mal diese Stellen sind besetzt.
Und dann sagen wir, die Variable Haus muss enthalten,
ein Haus, in dem ein Englishman wohnt und das Haus muss rot sein.
Die Member-Relation, es muss ein Spanier mit einem Hund vorkommen.
Es muss ein Haus vorkommen, das Grün ist und
da wird Kaffee getrunken. Es muss ein Haus vorkommen,
wo ein Ukrainer wohnt und der trinkt Tee
Wir haben ein Elfenbeinfarbenes Haus und das ist rechts vom Grünen Haus.
So, dann haben wir ein Haus,
in dem hier jemand Winston raucht und der hält Schnecken.
Wir haben ein Haus, das gelb ist und der wird kurz geraucht.
Wir haben ein Haus, wo Chesterfield geraucht wird.
Und das ist direkt neben dem Haus, wo Kultske rausgebraucht wird.
Und wo das Pferd gehalten wird, dann muss es noch
in Lackis drei Graucher geben, der Orangensaft trinkt.
Ja, und das müssen Japaner geben, der Pauliamens raucht.
Und der Norweger wohnt neben einem blauen Haus
Der Wassertrinke, muss vorkommen und es muss ein Zebra vorkommen.
Und das bindet hier die Variablen W und Z.
Und jetzt suchen wir mal noch eine Lösung.
Gibt es eine. Also was man dem Beispiel sehen kann,
ist, wir können Pro-Log verwenden,
wenn wir die Fakten alle in unterschiedlicher Art vorgegeben haben.
Wir haben ein kleines Fragment vom Wissen,
einen Indiz für das, für mein anderes Indiz für das.
Und wir schreiben die Regeln einfach hin und das Suchen,
suchen mit Unifikation, führt dazu, dass so lange unifiziert wird,
bis genau fünf Häuser übrig bleiben,
die alles erfüllen.
Ja, was kommt raus, wenn ich jetzt einfach frage,
Zebrahauses, bohrt der Drinker Zibra-Ona?
Dann erfahren wir, das sind diese Häuser.
Ganz links der Norweger mit seinem Fuchs und Kuls
und der trinkt Wasser und das Haus ist gelb.
Dann der Ukraine mit Chester Field und C und die Lösung
passt in allen Punkten, prüfen sie es nach.
Also pro Lok als Inferenzmaschine einfach das Wissen mit vielen Fragezeichen vorgeben,
nur die Facetten beleuchten, die wir wissen und hoffen,
dass es für das Ganze
eine Lösung gibt
Und ich habe das eben auch immer ausprobiert für das Rätsel,
was ein Harry Potter und der Stein der Weisen gestellt wurde von Several Snape.
Ich weiß nicht, ob sie sich daran noch erinnern,
wenn sie mal, was war das, die Kammer der Schrecken?
Nee, das war doch Stein der Weisen.
Da stehen die vor einer Passade mit vielen kleinen Fläschchen
und von den Fläschchen weiß man jetzt bestimmte Dinge.
Ja, aber nicht viel. Aber eigentlich genau wie beim Zebra-Rätsel.
Es sind ein paar Facetten bekannt und die Frage ist jetzt,
aus welcher Flasche darf man trinken,
wo ist der Zaubertrank drin,
der uns vorwärts bringt,
sodass wir
das Feuer laufen können
Zweiter Zaubertrank, der einen sicher nach Hause bringt,
wenn es einem alles zu viel wird.
Und wo sind die Giftflaschen, die man besser nicht trinkt?
Ja, das habe ich auch mal versucht,
dieses kleine Rätsel jetzt im Poloc zu schreiben.
Der Text, die Harmonie,
die der findet oder Hermine in den deutschen Büchern,
Denjalized before you wild safety liced behind.
Two of aswell, how pubice ever you would find?
Wann embong are seven, the let your move a head?
In nother will transport the drinker back instead.
Two in mong or numbers sold only nettle vine,
was immer Nesselwein ist,
three of as a crylas waiting hitten in line
Tschüss und less you wish to stay here for ever more.
To help you and your choice, we give you these clues for.
First. How ever slayly the poison tries to high,
you will always find some on nettle vince left side.
Second, different are those sustand at eater end,
but if you would move on world nizer is your friend.
Third as you see clearly, all our different size,
nice that devove, on a Giant, holds best in their insights,
forth, the second left in the second on the right,
are twins, once you tave them,
the different at first side.
Ja, also das Rätsel ist insofern literarisch gesehen vollkommen unfair.
Den J.K. Rowling wäre nie in den Detection-Clip aufgenommen worden,
weil da Hinweise drinstehen, die dem Leser nicht mitgeteilt werden.
Es wird nie gesagt, in dem ganzen Text,
wie groß sind die Flaschen. Die Helden sehen sie
und wissen, welche sind groß und welche sind klein.
Das Wissen über Große und kleine Flaschen ist essentiell, um das Rätsel zu lösen.
Also sowas macht man nicht in einem anständigen Krimi. Man sagt den Lesern,
das, was die Helden wissen,
alles andere ist unfair und genau wie Miss Marple abgelehnt wurde vom Detection Club,
weil die alles für sich verhalten hat oder auch Scharlack Humms,
immer dem armen Dr. Wotson, die erzählt hat,
was Sache ist, dann, naja,
okay.
Also das ist unfair. Wir müssen mal sehen,
ob es trotzdem lösbar ist, wenn wir die Größen der Flaschen nicht wissen.
Ganz wie bei dem Zepar-Rätsel, wo es die bekannte Prolog-Lösung gibt,
habe ich das jetzt mal einfach übertragen.
Da hat eine Struktur eine Flasche, ist eine Liste in
der Form, Name und Inhalt.
Damit kann ich erzwingen, dass die Flaschen alle unterschiedlich sind.
Die Reihe hat sieben Flaschen,
also konstruieren wir eine Liste mit sieben Elementen,
mittels Member,
wie wir es schon hatten
So, und dann gibt es da drei Giftflaschen. Wir wissen,
dass es genau drei gibt. Also wir können sagen,
in der Liste der Flaschen, wie sie da stehen,
muss es eine Flasche mit Gift 1,
mit Gift 2 und Gift 3 geben.
Die sind also unterschiedlich und diese drei Giftflaschen sind da.
Es gibt zwei Flaschen mit Nesselwein,
also nehmen wir auch wieder die Member-Relation,
die Wein 1 und Wein 2, sind in der Flaschenrelation,
damit sind fünf schon besetzt.
Und dann muss es noch einen Schrank vorausgeben,
der uns vorausbringt und einer, der uns zurückbringt.
Also die Konstruktoren verwendet um die Liste der Flaschen zu erzeugen,
genau sieben und mit sieben verschiedenen Inhalten
Dann haben wir Beziehungen zwischen den Flaschen,
also die Flasche, die ein Riese ist.
Die hat keinen Gift und der Zwerg hat auch kein Gift.
Wir können leider nicht sehen, an welcher Stelle Riese und
Zwerg stehen. Wir können verschiedenen Inhalt definieren als Relation,
Inhalt 1, umgleich innerhalb 2. Und Zwillinge,
dass der Inhalt gleich ist und kein Freund voraus,
über diese Frage Ungleichinhaltsschrank voraus.
Jetzt Rätsel. Also wir bauen erstmal die Liste der Flaschen
auf, die heißen dir einfach so Lobflasche 1 bis Flasche
Wir haben Giftflaschen, Lesselweinflaschen, Tränkeflaschen, diese Prädikate müssen
erfüllt sein, alle. Links von Nesselwein steht Gift,
also sagen wir, Io-Wright, Gift, Wein,
Einsbein, Allrad, Gift Wein, Zwei.
Verschiedene an den Enden, die verschieden Prädikat soll erfüllt sein
für Flasche 1 und Flasche 7. Kein Freund, wenn man weiter will,
ist für Flasche 1 und Flasche 7, auch bekannt.
Und dann war noch zweite,
links und zweite rechts sind Zwilling,
also sagen
wir Zwillingsprädikat muss erfüllt sein für F3 und
Und jetzt fragen wir mal, was können wir denn sicher trinken?
Gut, ich rufe es auf und kriege zurück.
Anstelle Flasche sechs und Flasche sieben kriegen wir den Trank voraus und den Frank
zurück und Gift und Nesselwein, die stehen an diesen Stellen.
Also immer jetzt so ein Schluckchen nimmt, sollte man
Simekullern eingeben und sehen, ob es alternative Lösungen gibt.
Und da kriege ich dann raus, oh,
der Trank voraussteht an Flasche vier, könnte er auch stehen und trank zurück.
Aber da, wo vorher uns angeboten wurde,
dass uns das beschützt, wenn wir vorausgehen.
Da steht jetzt nur der Nesselwein. Oder die Tränke können
auch an der Stelle sieben, die wir trinken sollten,
steht das Gift. Also so gut ist das noch nicht.
Irgendwo müssen wir noch im Zusatzinformationen reinbringen für eine eindeutige Lösung.
Und jetzt tun wir mal so, als wären die Größen der Flaschen bekannt.
Na gut, jetzt habe ich einfach geschummelt und weitergelesen und man sieht,
welche Flaschen Herr Millionen als Zwerg erkannt hatte.
Da erfährt man dann, welches tatsächlich eine kleine Flasche
war und habe das noch fest verdrahtet.
Nämlich, dass der Riese F7 ist und der Zwerg F3.
Und wenn wir das machen, dann gibt es nur noch eine einzige Lösung.
Also das Wissen, wie groß die Flaschen sind, rettet uns.
Und dann ist dies hier wirklich die einzige Lösung.
Okay, Flasche 6 und Flasche 7 sind die,
die uns daheil aus dieser Kammer rausbringen.
Gut, wie weit sind wir jetzt?
Ah ja. Arythmetik in Prolock. Das ist also noch
ein weiterer Fall, wo wir ein bisschen länger und ausführlicher
drüber reden müssten
Wir können das heute nur ganz kurz antasten und dann schauen wir
den Rest uns am nächsten Freitag an. Aber ich hoffe,
Sie haben an diesen beiden Rätseln jetzt gesehen,
wie Pullock einfach benutzt wird, wenn man verteiltes Wissen hat,
sind die Datenbasisschreiben, beschreiben keine Suchkontrolle,
wir schreiben nicht selbst das Backtracking,
wir fragen einfach mit Variablen,
ProLock bildet unsere Anfrage auf die Datenbasis ab und versucht rekursiv durch Anwenden der zugesicherten Regeln,
für uns die Variablen Bindungen zu finden,
die konsistent sind
Mit der Datenbasis und unserer Anfrage wahrmachen. Und das heißt,
wir brauchen nie wieder ein Backtracking selbst zu programmieren.
Ja, wir brauchen auch das Vergleichen nicht selbst.
Von Strukturen selbst zu schreiben. Wir brauchen keine Konstrukturen zu schreiben.
Wir geben einfach die Muster vor und fertig. So,
aber jetzt, wenn wir tatsächlich mal rechnen wollen,
in Brotlock, dann müssen wir Relationen über Zahlen und Zahlobjekte schreiben.
Na gut, wir haben schon gesehen, als wir Funktionen eingeführt haben,
dass wir die natürlichen Zahlen nicht wirklich brauchen.
Wir können jede natürliche Zahl als eine Liste von Funktionen schreiben
Und wenn wir Rationalzahlen brauchen, machen wir einen Patienten von
diesen Listen, von Funktionen und so weiter.
Wir können das ganze Gebäude der Zahlen so aufbauen.
Wir können die Athmetik so aufbauen. Hat einen kleinen Seiteneffekt,
der ist dann der Arathemetik-Prozessor unseres Rechners nicht verwendet wird.
Und es könnte ein bisschen dauern. Und vor allem,
die Benutzern doch gerne den Komfort geben will,
dass sie den Arathemetik-Prozessor benutzen können,
ist man in Frohlock so weit gegangen,
dass man nicht alle Zahlen rekursiv über die Piano-Aktion erzeugen lässt,
sondern so eine funktionale Auswerteumgebung eingebaut hat
Und diese funktionale Auswerteumgebung kriegen wir über den IS-Opperater.
Und den habe ich dann auch im Prüllock in Reckett mit reingenommen.
Das ist der Schlüpfloch in die funktionale Programmierung. Wir schreiben da
also diesen IS-Opperator, mit dem wir sagen können, Variable X ist.
Und dann kommt ein Aufruf irgendeiner beliebigen Recket-Funktionen, die ausrechnet,
was der Wert ist und die Variable an diesen Wert bindet.
Und wir können mit einem Testapparat der Bulsche Ausdrücke funktional auswerten.
Und wenn die Unifikation erfolgreich ist, kommt der Austritt wahr
zurück.
Kleine Einschränkung
Wenn wir diese funktionale Auswerteumgebung verwenden, dann ist die richtungsunabhängigkeit weg.
Weil wir dürfen diese Tests erst schreiben, wenn alle Variablen gebunden sind.
Es werden Funktionen aufgerufen und Funktionen bestehen nun mal darauf,
dass die Variablen definiert sind. Und wenn die Werte nach
undefiniert sind, sagt die Funktion, nein, danke.
Und deshalb haben wir in dem Moment,
wo wir den Ist-Apparator in Prolog-Verwänden, auch in Prolog von Bracket,
sind wir gezwungen, alles so zu schreiben,
dass alle Variablen gebunden sind,
bevor damit gerechnet wird in einer Funktion
Und wir können unsere Prädikate, wenn irgendwo, in der Rekursion,
das ist gebraucht wird und die funktionale Auswerteumgebung gebraucht wird,
können wir die Prädikate nicht mehr richtungsunabhängig verwenden.
Dann haben wir mit einmal aus unserer schönen relationalen Sprache,
aus den Relationen, Funktionen gemacht. Ein Beispiel wird es
verwenden können. Wir wollen die Variable X binden an einen Wert,
dann sagen wir in unserem Prolog and Reckett einfach ist,
Variable X, 2 plus 4.
Hier schlägt Reckett los, rechnet los und rechnet uns das
aus und wir kriegen raus
X gleich 6 und weitere Lösungen
gibt es nicht
Wir wollen wissen, ob X-Größer Y ist, da könnten
wir eben so schreiben. Wir binden X an 3,
wir binden Y an 5 und jetzt rufen wir Test auf
und geben hier den Aufruf eines Prädikates an.
Das wird ausgewertet und wir bekommen dann zurück. Ja,
es ist erfüllt für X gleich 5, Y gleich 5
und X gleich 3 und weitere Lösungen gibt es nicht.
Wir können hier unsere Map-Funktionen verwenden,
Map über eine Liste schicken und so weiter.
Ja, also all hier, das ist unser Schlupfloch,
wo wir hingehen können, Variablen an Werte bindenden Funktionen aufrufen.
Und wir sind im bekannten Fahrwasser mit Recket und funktionaler Programmierung
Okay, hier gibt es mehrere Lösungen in dem Fall,
weil die Variable für X gleich 2,
würden wir einen anderen Wert bekommen als hier.
Wir können die Länge einer Liste berechnen als ein Beispiel mal,
wo wir hier wieder die Arrathmetik brauchen und zählen wollen.
Wir können zusichern die Länge der leeren Liste ist null.
Die Länge irgendeiner Liste, die mit einem X beginnt und
einen Rest hat, steht in der Längenredaktion hier.
Die Länge ist hier keine Funktion, sondern eine Relation.
Leere Liste steht in der Relation null bezüglich der Länge.
Die etwas längere Liste hier steht auch in der Relation,
Länge, längst zu dieser Lenksvariable und diese Lenksvariable wird gerechnet.
So, und wir müssen, um rechnen zu können,
zuerst einen Wert für die Variable Länge finden.
Die wird hier gebunden, Lenn ist eins plus längs,
und längs, er ist gebunden worden,
indem wir hier die Länge des Restes bestimmt haben.
Also in dieser Rekursion bestimmen wir zuerst die Länge der Restliste,
hier wird Lennys R gebunden,
rekursiv und dann am Ende,
wenn wir das haben,
können wir eins drauf addieren für das erste Element,
was wir
hier gesehen haben
Und, naja, längst der Liste ABC, nennen,
gibt uns zurück, länge ist drei. Aber wichtig ist eben,
wir können immer erst hier losrechnen, wenn diese Variable in einen,
in der Unifikation davor einen Wert erfahren hat.
Okay, ja, damit danke für die Aufmerksamkeit und das
nächste Mal gehen wir denn dahin zurück,
wo wir jetzt auch mit dem Makros arbeiten und selbst definieren
werden.