uns mit relationalen Datenbanken befasst, relationale Datenbanken als Faktensammlung im Prologsystem.
Fakten als Tupel von Werten, die über einen Funktor miteinander verbunden sind.
Und wir haben Anfragen an diese Datenbank gestellt und insbesondere damit wir
interessante Anfragen stellen können, den Begriff der Variablen eingeführt.
Und die Verwendung von Variablen in verschiedenen Instanziierungsvarianten,
also entweder instanziert oder nicht instanziert,
erlaubt es uns jetzt ganz unterschiedliches Verhalten des Systems hervorzurufen.
Und das ist zum einen, wir rufen vollständig instanziert auf,
dann wird getestet, ob dieses Tupel in der Relation enthalten ist.
Wir rufen partiell uninstanziert auf, dann wird Die freie Variable,
die wir verwenden durch Informationen angereichert oder instanziiert.
Und zwar soweit es irgendwie geht. Also für diese Variante,
was lebt so? Sie kriegen wir eben genau diese Instanzierung.
Buch heraus. Für diese Variante, wer liebt Bücher,
kriegen wir eben zwei Instanzierungsalternativen heraus. Die, die
möglichen Variablenbelegungen angeben. Wenn wir eine Überbeschränkte Anfrage stellen,
die sich nicht erfüllen lässt, kriegen wir die Lehre Ergebnismenge raus.
Das ist also immer das Ergebnis Folds,
wenn wir nichts anderes bekommen haben.
Und wenn wir das vollständig unterstifiziert aufrufen,
dann kriegen wir eine vollständige Aufzählung der Relation
Immer über die Alternativen Anfrage. Und das erlaubt es uns
jetzt Programmierung auch oder Programmabarbeitung als Prozess der Informationsanreicherung zu verstehen.
Wir geben einen unterschiedlich stark mit Informationen versehenes Anfragemuster in
die Berechnung hinein und kriegen alle Varianten aufgezählt,
wie wir gegeben das Programm, diese Anfrage mit Informationen anreichern können.
Wir hatten dann komplexe Anfragen eingeführt, die man also durch Konjunktion oder Disjunktion
elementare Anfragen bilden kann. Das Wesentliche ist dabei,
bei der Konjunktion natürlich, dass das immer eine Klausel ist,
die ich hier angebe und deshalb die Variablen,
die ich hier verwende, innerhalb des gleichen Skopus-Bereiches liegen.
Und deshalb Chorreferenz zwischen diesem Variablen Geld. Also die Variablenbindung,
die mit dem ersten Teilziel erzeugt wird,
muss auch im zweiten Teilziel verwendet werden. Und erst,
wenn beide Zeilziele unter der gleichen Variablenbindung erfolgreich abgeleitet werden können,
dann kriege ich ein Gesamtergebnis heraus.
Die Alternative dazu war die Desjunktion, die Desjunktion hat eben
genau den Nachteil, dass sie diese Skopus-Regel undeutlich macht.
Also dieses X hier ist jetzt in einem anderen Skopus
als jenes X und deshalb war die der Rat,
das insbesondere in komplexen Programmen nicht zu verwenden,
weil man dann schnell den Überblick verliert,
was eigentlich noch mit wem disjunktiv verknüpft ist
Die Suche, die jetzt durchgeführt wird, ist eine Tiefensuche.
Ich fange mit dem ersten Teilziel an. Und wenn ich
eine Lösung für dieses Teilziel habe, überprüfe ich die an
dem verbleibenden Teilzielen. Erst wenn ich damit fertig bin,
mache ich das Backtracking zu dem Entscheidungspunkt zurück und prüfe dann,
ob es noch weitere Alternativen, also weitere variablen Belegungen gibt,
die möglicherweise auch zu einem erfolgreichen Berechnungsabschluss führen.
Also der Prozess ist dann immer,
ich habe ein erstes Teilziel,
ich bearbeite das erste Teilziel bis zur ersten erfolgreichen Teilinstanzierung mit dieser variablen Belegung,
die ich hier gefunden habe,
gehe ich in das zweite Teilziel
und starte dort die Suche neu
Ja, und erst, wenn ich hier unten das bestätigt habe,
dann mache ich, nein,
wenn ich hier unten keine weiteren Alternativen mehr habe, ich habe das vollständig abgearbeitet,
dann mache ich das Backtracking zum letzten Entscheidungspunkt und kümmere mich um die restlichen Berechnungsalternativen.
Gut, das können wir uns im Baum angucken,
wir können es uns im Drehs angucken,
im Drehs also immer, hier ist das Backtracking charakterisiert,
gehe zurück zum Entscheidungspunkt Nummer eins und das ist derjenige hier oben aufgemacht wurde und schaue,
ob es dort noch Klauseln gibt,
die noch nicht abgearbeitet worden sind und das sind in diesem
Falle diese beiden hier,
die aber keine Ergebnisse mehr produziert haben
Werden wir den vajablen Begriff einführen, müssen wir das noch,
können wir das noch ein kleines bisschen erweitern?
Es gibt auch das Konzept der Anonymen Vajable,
die anonyme Vajable hat also keinen Namen und dient im Wesentlichen dann dazu,
Berechnungsresultate zu unterdrücken, wenn sie denn für einen gegebenen Zweck irrelevant sind.
Das entspricht, wer damit schon vertraut ist,
der Projektion in der Welt der relationalen Datenbanken.
Ich habe also viele Spalten in meiner Tabelle und
ich interessiere mich nur für eine Teilmenge davon,
dann kann ich diejenigen, die mich nicht interessieren,
damit einfach unterdrücken
Und das kann ich eben anwenden in unserer Immobiliendatenbank,
wenn mich bei der Anfrage nach Mehrfamilienhäusern nur Straße und Hausnummer interessiert,
dann kann ich etwa den Identifier hier oder das Baujahr als irrelevant
unterdrücken und kriege dann in der Ergebnisausgabe nur das heraus,
was ich tatsächlich gefragt habe.
Also das ist eins und allein ein Effekt am Prompt des Systems.
Weiter unten im System spielt das eigentlich keine Rolle.
Bestenfalls noch um Warnungen zu vermeiden,
wenn man also in komplexen Programmen Variablen im Kopf einer Klausel verwendet hat,
dann im Körper aber nicht wieder warnt einen das System,
dass man eine solche Variablen Verwendung macht keinen Sinn und man kann das dann die Warnung unterdrücken,
indem man an der Stelle eine anonyme Variable verwendet.
Also eine anonyme Variable ist eine,
deren Wert man nie wieder für die Co-Referenz beispielsweise benutzen möchte.
Und es ist dann egal, was dort dran gebunden wird.
So, wir haben ja bis jetzt uns nur über
Anfragen an eine Tabelle, also eine Prädikatsdefinition interessiert.
Das ist natürlich nicht das, wo man hin will.
Relationale Datenbanken bestehen üblicherweise aus mehreren Relationen,
die miteinander verknüpft werden
Über einen Join etwa. Und deshalb erweitere ich jetzt die Datenbank,
das sind unsere alten Immobilien, die wir hier haben.
Und das erweitere ich jetzt dadurch, dass ich hier noch eine Besitzwechseltabelle einführe. Die besitzt Wechseltabelle,
hat hier vorne wieder für den Vorgang des Verkaufens,
einer Immobilie, einen Identifier, bezieht sich dann hierüber auf den,
das ist dann ein sogenannter Fremdschlüssel,
bezieht sich über diesen Fremdschlüssel auf die Immobilienobjekte,
die hier oben angeführt sind.
Also hier ist Immobilie Nummer eins verkauft worden, dieses Einfamilienhausgärtnerstraße 15.
Und zwar von Herrn Müller, Annan Meyer,
zu einem bestimmten Preis an einem bestimmten Tag
Ja, und da sind eben jetzt verschiedene Verkaufsvorgänge aufgelistet.
Und jetzt kann ich natürlich über diesen Tabellen,
weil sie eben durch einen Fremdschlüssel miteinander verbunden sind,
komplexe Anfragen stellen, die sich auf beide Tabellen beziehen.
Bisher hat man eben immer nur Anfragen an die gleiche Tabelle,
indem ich eben sage, ja, wer hat welches Haus gekauft?
Dann stelle ich eben hier über diese Variable X die Co-Referenz
zwischen dem Objekt Identifier hier und dem Objekt Identifier hierher.
Also das Objekt, worüber ich hier rede,
muss auch das Objekt sein
Worüber ich hier rede. Ich möchte mit Straße und Hausnummer
wissen und ich möchte wissen, wer ist der Käufer dieser Immobilie.
Das ist das, was man in der relationalen Datenbankwelt dann auch als Join bezeichnet.
Für einige zwei Tabellen zu einer neuen Relation, die sich
dadurch ergibt. Und die neue Relation sehen wir hier,
das ist jetzt die Relation zwischen dem Objekt Identifier der Straßenangabe,
der Hausnummer und dem Käufer.
Eine vierstellige Relation sozusagen, die uns dort aufgezählt wird.
Und der Join, die Korreferenz,
also zwischen den Tabellen, die Verbindung zwischen den Tabellen
wird durch die Korreferenz hergestellt
Okay, so, jetzt sind wir also in der Lage,
beliebig, komplexe, relationale Datenbanken zu verarbeiten und fragen,
beliebigen Schwierigkeitsgrad zu stellen, indem wir eben die Einzeltabellen miteinander verbinden.
Der große Vorteil der relationalen Datenbanken,
dass sowas wird ja alles in GDB behandelt,
das muss ich hier nicht machen, ist also,
dass ich in der Lage bin,
Redundanz möglichst weit zurück zu drängen,
dass ich also nicht die gleiche Information an unterstellt in schiedlichen Stellen in der Datenbank habe,
das will man üblicherweise nicht,
weiß jemand,
warum man das nicht will
Ja? Ja, dass man halt dann nur an einer Spielejahre ändert,
muss man sich dafür deuten, dass man jeder stellt.
Das ist der Hauptgrund, jede Redundanz da drin führt dazu,
dass sich an mehreren Stellen Änderungen vornehmen muss. Und man kann sicher sein,
dass man irgendwann mal bei irgendeinem Pflegeprozess auf der Datenbank,
dass man das vergisst, entweder beim Eintragen der Daten oder
auch wenn man irgendwelche Prozeduren über der Datenbank schreibt,
dass man nicht mehr alle diese Stellen im Auge hat.
Denn solche Tätigkeiten an der Datenbank werden ja auch weiter vererbt,
an Nachfolgepersonen, die auch in 20 Jahren möglicherweise mit dieser Datenbank noch zurechtkommen müssen.
Und dann ist es irgendwann mal verloren gegangen,
dass die Information dort ja auch noch steht und schon versackt,
die Datenbank in fehlerhaften Eintragungen und inkonsistenten,
vor allen Dingen hier steht es so, dort steht es so.
Anderes Argument ist natürlich immer noch der Speicherbedarf,
wenn ich den gedoppelt abspeichere, muss ich den Preis dafür bezahlen,
aber das ist typischerweise in modernen Rechenumgebungen nicht mehr das entscheidende Kriterium.
Aber das Update-Anomalie-Kriterium ist ein sehr ernstes
Ich mache jetzt einen Vorgriff, den ich schon angekündigt hatte,
in einen Bereich, den wir eigentlich erst irgendwann um,
kurz nach dem Jahreswechsel behandeln werden, nämlich Prädikate zweiter Ordnung.
Prädikate zweiter Ordnung sind Prädikate, die wir definieren können,
die andere Prädikate oder in diesem Falle Prädikatsaufrufe als Argumente verwenden.
Und zwar als Eingabeargumente. Und das Wichtigste,
das mache ich deshalb hier,
weil jede gute relationale Datenbank über eine ähnliche Funktionalität verfügt,
nämlich immer dann, wenn ich aggregieren will.
Wenn ich über der Datenbank aggregieren will, dass ich
beispielsweise die Summe aller Verkäufe in einem Monat ermitteln will.
Dann muss ich erstmal alle Verkäufe in dem Monat ermitteln und muss dann
darüber summieren. Und die Operation liefern mir alle Einzelverkaufsbeträge,
das kann man mit dem Prädikat Find All machen.
Und Find All ist ein solches Prädikat zweiter Ordnung,
welche anderen Prädikate zweiter Ordnung es gibt, das guckt man später an.
Also die Sonntags ist ganz einfach, es ist ein dreistelliges, ein dreistelliger Prädikat.
Ich gebe dem Ganzen einen Term vor, der kann beliebig kompliziert sein.
Bis jetzt kennen wir nur standen und vaiablen.
Es macht keinen Sinn, hier eine konstant anzugeben,
aber deshalb wird hier für unsere jetzigen Zwecke immer eine Bayable stehen.
Wir geben ein Ziel vor, ein Ziel ist eine ganz
normale Anfrage. Eine Anfrage kann einfach oder komplex sein,
wenn sie einfach ist, wird sie einfach hier reingeschrieben,
wenn es eine komplexe Anfrage ist.
Da muss sie um die Dreistelligkeit hier sicherzustellen, nochmal geklammert sein.
Ansonsten kriege ich zusätzliche Kommata da rein und dann
würde das nicht mehr der Signatur dieses Prikats entsprechen.
Und hier hinten kriege ich als Ausgabe Informationen eine Liste.
Eine Liste ist ein Datentyp, den wir auch erst in einigen Wochen behandeln,
aber wir können hier schon mal sehen, wie man Listen erzeugen kann.
Wie man, oder wie Listen aussehen eigentlich nur,
wie man sie tatsächlich erzeugen kann, das gucken wir uns später an.
So, und die Semantik des Ganzen ist,
sammle die Resultate, die der Aufruf dieses Ziels hier produziert,
als instanziierte Variablen dieses Terms auf.
Wenn das nur eine Variable ist, wird eben einfach der Wert,
der durch den Aufruf von Ziel an diese Variable gebunden worden ist,
hier an die Variable übergeben und alle diese Variablen
Instanziierungen werden in Liste zusammengetragen
Und das Ganze macht eben nur Sinn, deshalb diese pragmatische Einschränkung,
wenn es hier und hier gemeinsame Variablen gibt.
Denn nur die Bindungen, die hier erzeugt werden,
werden an diese Variablen übergeben. Ansonsten macht das keinen,
ja, kann man das nicht sinnvoll verwenden. Die einzige Instanzierungsvariante,
die es gibt, ist also, der Therm muss gegeben sein,
das Ziel muss gegeben sein und die Liste ist beliebig.
So, und das gucken wir uns jetzt mal an.
Also hier sage ich, suche mir alle, alle Bs,
das sind die Baujahre hier in diesem Ziel,
ja,
alle Baujahre für Einfamilienhäuser
Also geh in die Objektdatenbank mit diesem Ziel,
gib mir ein Einfamilienhaus und das dazu gehört ihr Baui,
nein, suche alle Einfamilienhaustupel raus und gib mir das Zugehörige Baujahr
als Wert von B. Dir ist das Wert von B wird hierher übertragen und dann in der Liste zusammengesammelt.
Und dann kriege ich eben eine Liste und Listen sind syntaktisch immer Ecke,
Klammer auf, die Elemente durch Kommas getrennt,
kriege ich eine Liste solcher Baujahre als Ergebnis ausgegeben.
Hier nur schon mal ein Eindruck davon, dass das auch komplizierter geht.
Hier unten gebe ich eben als Therm nicht nur eine Variable an,
sondern wieder einen komplexen Therm,
der aber bei Ajablen enthält uninstanzierte Variablen enthält und wo es
eine Chor-Referenz zwischen diesen Variablen hier und diesem Variablen in dem Ziel gibt,
das hier zu erfüllen ist.
Nämlich die Straße und die Hausnummer, die sind hier in der Chor-Fans.
Und jetzt wird dieses Ziel hier und wir sehen,
das ist ein komplexes Ziel, das ist geklammert,
das besteht aus zwei Teilzielen.
Das erste ist dieses hier, der Zugriff auf meine Immobiliendatenbank
und das zweite ist eine Bedingung für das Baujahr, die ich formuliert habe.
Also heißt, gib mir alle einen Familienhäuser,
deren Baujahr größer als 1960 ist und die Werte,
die du für Straße und Hausnummer bekommst,
Instanziere in diesen Thermen und sammle dann diesen Therm in der Ergebnisliste L.
Und dann kriege ich eben als Ergebnisliste die Adressen aller Häuser,
die nach 1960 gebaut worden sind, alle Einfamilienhäuser,
die nach 1960 gebaut worden sind, in einen komplexen Thermen zusammengefasst.
Also auch hier sehen wir das Prinzip der Informationsanreicherung.
Ich gebe diesen Thermen vor und er wird durch diesen Aufruf
maximal mit Informationen angereichert und die angereicherten Therme werden dann
in die Ergebnisliste übernommen
Das hier nur schon mal eingeführt, weil das relativ oft
gebraucht wird. Wenn ich also jetzt beispielsweise wissen will,
wie viele, wie viele Einfamilienhäuser wurden nach 1960 gebaut,
dann kann ich dieses Muster hier verwenden.
Das wäre sogar ein Punkt, wo ich nicht mal
eine Variable hier vorne an der Thermstelle brauche.
Hier könnte ich einfach ein X in die Liste reinschreiben.
Und anschließend zähle ich die Anzahl der Xe,
die in der Liste drinstehen und weiß,
wie viele Einfamilienhäuser es gibt
Ich könnte auch eine Eins in die Liste reinschreiben und anschließend
die Summe über alle Listenelemente bilden und ich hatte die Anzahl der Einfamilienhäuser auch.
Also es gibt tatsächlich Einzelverwendungen,
wo nicht unbedingt eine Vayable hier drin sein muss, wo es reicht,
konstanten in die Liste zu schreiben und hinterher zu zählen,
wie viele davon habe ich gefunden.
Aber im typischen Fall, wenn man also einen Mittelwert bilden will,
wenn man eine Summe bilden will oder sowas,
dann muss man die Elemente aufsammeln und hinterher dann entsprechend weiterverarbeiten.
Und um sie aufsammeln zu können, brauche ich eben hier in,
oder sagen wir so wie hier,
eine Vayable in dem Aufruf drin
Also, Botschaft ist, wann immer ich Aggregation betreiben will,
von Ergebnissen, dann bitte mit einem Prädikat zweiter Ordnung,
eine andere Möglichkeit gibt es nicht.
Es gibt auch noch andere Prädikate zweiter Ordnung, die gucken
wir uns dann später an. Zum Abschluss dieses Abschnittes,
also ein kleiner Vergleich, was unterscheidet jetzt das,
was wir hier gemacht haben, eigentlich von der relationalen Datenbankwelt,
wie sie in Grundlagen von Datenbanken etwa behandelt wird.
Dort dient natürlich nicht Polok als Absprachesprache, sondern SQL als Abfragesprache,
das weiß man, das Verarbeitungsmodell ist allerdings exakt dasselbe,
also es ist die Relation Algebra, zumindest,
was die Recherchefunktionalität betrifft, ist es keinerlei Unterschied da.
Das, was dort als Selektion betrachtet wurde,
das sind bei uns hier die Anfragen, das,
was in der relationalen Datenbankwelt als Projektion betrachtet wird,
das sind Anfragen mit anonymen Variablen, das hatte ich schon gesagt.
Und den Join haben wir gerade gesehen,
das sind mehr Tabellenanfragen, die über eine gemeinsame Variable,
also durch Co-Referenz über eine Variable miteinander verknüpft sind.
Und immer wenn wir aggregieren wollen, dann also sowas,
was in SQL count oder Max oder dergleichen ist,
dann läuft das auf Prädikate zweiter Ordnung hinaus
Und wir haben gesehen bisher, dass diese Abfragesprachen,
die wir hier haben, referenziell transparent sind und auch richtungsunabhängig sind.
Und das ist eine Eigenschaft der relationalen Datenbanken, die eigentlich
sehr angenehm ist, weil wir dann Dinge hochgradig parallelisieren können.
Die referenzielle Transparenz heißt immer, sehr,
sehr gut für Parallelisierung geeignet. Und das ist das,
was in der Datenbankwelt natürlich massiv ausgenutzt wird,
sonst könnte man keine effizienten Datenhaltungssysteme aufbauen.
Also der relationale Kern,
der Datenbankkern von Prolog ist Äquivalent zur Recherchefunktionalität von SQL
Aber eben nur zur Recherchefunktionalität. Es ist in jeder Datenbank-Abfragesprache,
habe ich zusätzliche Elemente drin, die hier nicht ohne weiteres unterstützt werden.
Also so etwas wie Konsistenzbedingungen und dergleichen mehr.
Da wird es dann interessant. Das Ganze ist eine echte Teilmenge
von Prolog. Wir werden ja unser Prolog noch erweitern.
Wir sind ja erst bei Stufe 1,
Stufe 2 und Stufe 3 kommt ja noch.
Und Datenbank, relationale Datenbanksysteme sind natürlich nicht berechnungsuniversell.
Ich kann nicht damit beliebige Berechnungen machen,
sondern nur strukturierte Abfragen an Relationen
Und was Datenbank-Systeme üblicherweise darüber hinaus liefern, das,
was wir jetzt hier in dem Prolog-System überhaupt nicht haben,
ist persistente Haltung großer Datenbestände.
Bei uns ist es Feierabend in dem Augenblick,
wo wir das Prolog-System verlassen. Das ist nicht persistent.
Mehr Benutzerbetrieb ist auch nicht möglich. Die Konsole ist oder
die Kommandozeile des Prolog-Systems ist in der Hand eines Nutzers.
Wartung und Aktualisierung der Daten wird nicht unterstützt,
also so etwas wie Backup ist
Ja, wieder Anlauffähigkeit und was Datenbankssysteme da alles ermöglichen,
das ist hier nicht vorgesehen. Und alle Mittel zum Schutz
der Integrität der Daten gibt es hier auch nicht.
Also sowas wie Zugangsrechte, konsistent Checks, Logging-Angelegenheiten,
all das ist hier nicht vorgesehen. Also die Gemeinsamkeit bezieht
sich eben tatsächlich auf die Relation in Algebra als Verarbeitungsmodell.
Die Licht beiden in gleicher Weise zugrunde.
Oder dieses Modell liegt beiden im gleicherweise zugrunde. So,
das war Schritt Nummer eins, der Einstieg in die Prolog-Programmierung
oder die relationale Programmierung als Arbeit mit relationalen Datenbanken.
Oh, das sieht aber etwas komisch aus. Eidedaus,
das, haha. Ja, die Verlässlichkeit all dieser Latich-Styles
ist immer nicht sehr groß. Man macht das Ganze
ein Jahr später und plötzlich kriegt man solche netten Effekte her.
Das muss ich nochmal angucken, woran das liegt.
Der nächste Schritt ist jetzt nicht so wesentlich. Der nächste Schritt,
den wir hier machen wollen, ist, sind die deduktiven Datenbanken.
Wir werden jetzt also hinzunehmen, was Deduktion bedeutet. Wir werden uns angucken,
wie man mithilfe von Deduktion die Recherchefunktionalität in einer Datenbank verbessern kann,
indem man jetzt nicht nur Relationen durch Aufzählen von Fakten eingeben kann,
sondern dass man Relationen auch berechnen kann.
Das wird das Ziel sein. Und wir werden uns dann
zum Schluss verschiedene Spezialtypen von Relationen angucken,
also Relationen mit bestimmten Eigenschaften und werden uns anschauen,
wie sich das Prolog-System in solchen Fällen verhält.
Gut, als erstes der Deduktionsbegriff Deduktion heißt immer,
und daher kommt ja der Begriff deduktive Datenbanken,
Heißt immer, ich leite aus einer gegebenen Aktionmenmenge.
Das ist das, was ich per Consult in die Datenbank eintrage.
Aus dieser Aktionmenge leite ich jetzt Folgerungen ab.
Und da gibt es eben verschiedene Logikkalküle in den verschiedenen Logikkalkülen,
sieht es anders aus. Sieht es ein bisschen unterschiedlich aus
. Der einfachste Fall ist die Aussagenlogik. Da habe ich
eben als Aktionen atomare Formeln und Implikationen.
Und ich kann die Schlussregel dem Modus Pones darauf anwenden,
kann also sagen, wenn A gilt und ich weiß,
dass aus AB folgt,
dann kann ich die Schlussfolgerung ziehen
Und das wird dieses, dieser Teil hier wird unser Prolog-Programm sein und
dieser Teil ist das Berechnungsergebnis, was das Prolog-Programm produziert hat.
Das ist die Brücke zur Logik, die wir,
auf der das Ganze basiert. In der Prädikatenlogik haben wir
jetzt zusätzlich noch Variablen eingeführt und die Variablen erlauben uns jetzt generelle Aussagen.
Und diese generellen Aussagen braucht man eben genau um Relationen brechen nennen zu können.
Also so etwas wie ein Fakt erstmal, Hans ist ein Mann,
das kann ich eben in Prolog-Notationen oder in Logik-Notationen ungefähr so schreiben.
Hans hat die Eigenschaft, Mann zu sein, ein einstelliges Prädikat
Und ich habe eine Regel, die jetzt sagt,
die ist allquantifiziert, alle Männer sind Verbrecher,
also für alle X gilt, wenn X Mann ist,
dann gilt auch X Verbrecher.
Und durch Anwendung dieser Schlussregel des Modus Ponens kann ich jetzt eben
diese Variablen hier instanzieren und kann daraus den Schluss ziehen,
dass Hans ein Verbrecher ist.
Genau diese Form von Berechnung von Relationen wollen wir jetzt in das Produktsystem einführen.
Also das, was wir bisher gemacht haben, ist,
wir haben eine Relation durch Aufzählung der Elemente spezifiziert.
Das wird auch als extensional bezeichnet. Ich schreibe alles hin,
was ich weiß, das ist eben der Stand der
relationalen Datenbank
Ich muss per Insert erst mal alle Dopel eingeben,
über die ich dann in der Folge reden will oder
über den ich suche, Operationen durchführen will.
Und jetzt stellten wir an die Stelle, dass wir nicht
die Elemente aufzählen, sondern dass wir sie berechnen wollen.
Wir sagen jetzt also, die Relation besteht nicht aus ein,
zwei, drei, vier Endtubeln, sondern wir sagen,
die Relation besteht aus allen Tubeln, die bestimmte Bedingungen erfüllen.
Und die Frage ist, wie geben wir diese Bedingungen an?
Und das wird genau über Implikationen passieren oder eben Regeln.
Also die Regeln, die wir dafür brauchen,
um solche Bedingungen zu spezifizieren.
Und das wird dann auch als intentionale Spezifikation bezeichnet,
die entsprechenden Implikationen in der Prädikatenlogik
Und auf diese Art und Weise können wir also regeln,
Implikationen auf bekannte Fakten anwenden und,
genau das haben wir hier gemacht, wir haben eine Regel genommen,
eine Implikation und haben sie auf bekannte Fakten angewendet und haben daraus neue Fakten abgeleitet.
Und damit haben wir sozusagen eine, es ermöglicht,
dass wir die Verbrecherrelation dadurch berechnen können, dass wir wissen,
wer ist ein Mann und dass wir diese allgemeine Regel kennen,
jeder Mann ist ein Verbrecher
Gut, das ist dann, läuft auf den Begriff der relationalen Abstraktion hinaus.
Das Ziel ist, Relation nicht mehr aufzuzählen, sondern berechnen zu können,
intentionale Spezifikationen aufzuschreiben. Und sie erinnern sich ganz,
in der ersten Vorlesung hatte ich gegenübergestellt, funktionale Abstraktionen,
ich habe eine Funktionstabelle,
wo ich die Argumentbelegungen und die zugehörigen Funktionswerte aufgelistet habe,
dann durch funktionale Abstraktionen in eine Berechnungsvorschrift überführt,
beispielsweise Quadratzahl berechnen
Das Gleiche machen wir jetzt eben auf der relationalen Ebene.
Wir haben bisher eben eine Tabelle gehabt. Wie hat dieses Argument,
entspricht mir diesem Argument oder diese Zuordnungen von Werten
zu unterschiedlichen Argumenten, sind Bestandteil der Relation.
Und ab sofort möchte ich das eben berechnen können.
Und dass die in zwei Zwecken, das erste ist,
ich möchte Redundanz beseitigen. Wir werden also Beispiele sehen,
wie ich auf die Art und Weise durch Angabe einer Berechnungsvorschrift
erst mir erspare, die Dinge immer und immer wieder aufzuzählen.
Und das zweite ist, und Beseitung von Redundanz hatten wir eben,
ist immer ein heeres Ziel in der Welt der Datenbanken,
weil ich eben auf die Art und Weise
die Sachen updaterobust machen kann
Und das Zweite ist,
ich bin dadurch, dass ich nicht mehr aufzählen muss,
nicht mehr an die Endlichkeit der Relation gebunden.
Also ich bin dann in der Lage, Relation über unendlichen Domänen zu spezifizieren.
Und das ist der Bereich, der uns insbesondere interessieren wird.
Relationale Datenbanken sind immer endlich. Gut, wir brauchen eine
kleine, syntaktische Erweiterung. Wir hatten bisher Klauseln als Faktenziele
oder regeln alle jeweils mit dem Punkt, äh Quatsch,
als Fakten oder Ziele,
alle mal mit dem Punkt abgeschlossen und neu hinzu kommt jetzt der Begriff der Regel.
Und ein Regel ist eben immer eine Operatorstruktur,
die hat einen Kopf, die hat einen Körper
Und das Ganze ist durch ein Separator voneinander getrennt.
Das ist das Doppelpunkt minus. Und dieses Doppelpunkt minus
soll im Prinzip ein nach links gerichteten Pfeil symbolisieren.
Das ist nämlich die Implikation, die wir vorhin gerade gesehen haben,
aus Mann von X folgt Verbrecher von X.
Das ist die Regel, die wir hierhin geschrieben haben.
Der Kopf ist immer eine Struktur, also so etwas mit Funkter und Argumenten dazu.
Und der Körper ist ein Ziel. Und Ziele hatten wir
ja schon, das können elementare oder komplexe Ziele sein.
Also im Körper der Klausel können nicht nur solche elementaren Ziele
stehen, sondern auch komplexe Ziele dieser Art
Das heißt also jetzt, wenn irgendetwas ein Mensch ist und
es ist männlich und es ist erwachsen,
damit zeichnen wir das auch als Mann.
Das wäre eine Definition für diesen Begriff Mann. Und wenn
ich denn Informationen über diese einstelligen Prädikate hier zur Verfügung habe,
kann ich aus den gegebenen Fakten mithilfe dieser Regel
neue Fakten in der Relation
Mann berechnen
Insbesondere ist es jetzt wesentlich, dass sich innerhalb einer Prikatsdefinition
sowohl Regeln als auch Fakten verwenden werde.
Das ist sogar der typische Fall, dass die beide gemeinsam
in einer Prädikatsdefinition auftauchen. Also es ist nicht so,
dass sich entweder eine Sammlung von Regeln oder eine Sammlung von Fakten
habe, sondern sie werden in gemischter Form auftauchen.
Also ich werde Teile über Fakten spezifizieren und ich werde
andere Teile aus diesen Fakten ableiten und berechnen.
Das wird das typische Vorgehen sein
Ja, wir müssen, wenn wir die Syntax haben,
uns natürlich auch über die Semantik unterhalten.
Und da müssen wir jetzt zwei Semantikbegriffe wieder benutzen.
Einerseits die Denotationelle Semantik. Wir gehen also auf
den Berechnungsformalismus aus der Mathematik zurück und sagen ganz einfach,
wir wollen logische Ableitbarkeit von Fakten mithilfe dieser Schlussregel des Modes Ponens haben.
Und Modes Ponens heißt immer, nimm einen Fakt,
nimm eine Implikation und schließe von der Prämisse auf die Conclusio.
Das ist die Anwendung des Modes Ponens
Operational müssen wir das wieder auf den Rechner bringen.
Und das heißt, wir müssen die Regeln irgendwie abarbeiten.
Wir müssen einen Algorithmus vorgeben, wie diese Regeln abzuarbeiten sind.
Und wir hatten das ja schon. Der Algorithmus muss so allgemein sein,
dass sich in diesem Rahmen beliebige Anwendungsalgorithmen laufen lassen kann.
Denn das Ganze soll berechnungsuniversell sein.
Und wir wollen ja den Algorithmus im Verarbeitungsmodell spezifizieren.
Und das ist genau an dieser Stelle jetzt wieder.
Wir müssen die Verarbeitung so machen, dass auf der Basis
beliebige Anwendungen realisieren kann. Die operationale Semantik heißt hier immer,
ich unifiziere den Regelkopf mit der Anfrage. Also bisher,
wenn wir nur Fakten hatten,
haben wir ja auch die Anfrage mit dem Fakt unifiziert
Jetzt unifizieren wir die Anfrage mit dem Kopf der Regel.
Der Kopf der Regel ist das, was vor dem Separator steht.
Also dieser Teil hier. Ja, und wenn das erfolgreich war,
dann benennen wir die Variablen erst mal um,
wegen des Gültigkeitsbereichs der Variablen für die Klausel.
Wir machen eine neue Verarbeitung der Klausel,
möglicherweise der gleichen Klausel, aber auf einer unterschiedlichen Rekursionsebene.
Aber auf jeden Fall wird eine neue Klauselabarbeitung gestartet und
das startet immer mit Umbenennen der Variablen.
Dann ersetzen wir die Anfrage durch den Regelkörper.
Und Benutzen dabei alle, die vajablen Bindungen,
die bei der Unifikation des Regelkopfes hergestellt worden sind.
Also bei der Uniplikation des Regelkopfes hier oben, da,
kriegen wir ja vajablen Belegungen. Diese vajablen Belegungen benutzen wir
im Körper des Regel, im Körper der Regel.
Und diesen Körper der Regel schicken wir als neues Teilziel
in die Verarbeitung des Systems hinein.
Ja, also wir machen jetzt ein rekursiven Aufruf der gesamten Prologverarbeitung.
Die Suche wird mit einer neuen Anfrage neu gestartet und geprüft,
ob sie zu positiven Ergebnissen führt oder nicht
Kleine Einschränkung wieder, die Syntax sagt ja nur,
dass das Ganze hier vorne eine Struktur sein soll.
Das ist auch richtig. Ich kann ja beliebige Strukturen hineinschreiben,
aber es machen nur ganz bestimmte Strukturen tatsächlich Sinn.
Insbesondere sollten es keine Grundstrukturen sein. Also dort sollten wieder
Variablen drinstehen, denn diese Variablen brauche ich,
als die formalen Parameter der Prozedur.
Irgendwo muss ich mit meinem Berechnungsergebnis hin,
wenn ich dort keine Variablen habe,
dann ist die Verwendung dieses Prädikats nur noch für
ganz merkwürdige,
exotische Fälle
überhaupt geeignet
So, wir machen das jetzt einfach. Wir nehmen unsere Liebt-Datenbank,
die ja bisher aus diesen vier Fakten bestanden hat und erweitern
sie um eine Regel. Und die Regel sagt jetzt,
Paul liebt alle diejenigen, die auch Bücher lieben.
Merkwürdig, aber warum denn nicht? Die menschliche Natur ist unerforschlich.
Wenn wir jetzt unsere Datenbank erweitern, müssen wir uns gleichzeitig
über all die Konsequenzen für den Softwareentwicklungsprozess klar werden.
Und das heißt hier, wir müssen überlegen,
wie wir so etwas jetzt testen. Bisher haben wir ja
immer gehabt, wir müssen positive und negative Beispiele testen.
Wir müssen alle Instanzierungsvarianten testen. Jetzt haben wir in
unserer Prädikatsdefinition sowohl Fakten als auch Regeln drin
Wir müssen beides dem Test unterziehen. Das heißt,
die Komplexität des Testens wird nochmal um eine Dimension größer.
Wir müssen also dafür sorgen, dass wir sowohl bei Ableitbarkeit
über diese Fakten als auch Ableitbarkeit über diese Regel überprüfen.
Das ist für einen vollständigen Test unverzichtbar.
Also, wir nehmen jetzt einen vollständig instanziertes Ziel.
Wir fragen, liebt Paul K., wird diese Anfrage,
gehen wir in die Datenbank hinein?
Die Fakten liefern kein Ergebnis dazu. Über Paul an der
ersten Argumentstelle sagt die Datenbank überhaupt nichts aus.
Aber wir haben diese Regel hier zur Verfügung.
Die ist ja nur hinzugekommen
Also als fünfte Alternative wird jetzt diese Regel genommen.
Es wird die Anfrage, die hier oben steht,
mit dem Kopf der Klausel unifiziert. Dabei wird,
und es werden Vayablen umbenannt, deshalb haben wir jetzt hier
eine Vayable X1 und die Vayable X1 kriegt als Wert K.
Und mit dieser Vayablen Belegung für X1 geht es jetzt in
den Körper der Klausel und der Körper der Klausel wird mit
dieser Vayablen Substitution als neues Teilziel in die Suche geschickt.
Also X1 ist K, wir haben jetzt die neue Anfrage,
liebt Karl ein Buch, was Sinn macht.
Paul ist dann oder Paul mag dann Karl
Ja, von hier kommt der Karl her. Ich habe
gerade die Quelle von Karl gesucht. Der steht in der Anfrage drin.
Paul mag dann Karl, wenn Karl ein Buch mag.
Das war genau die Regel, die wir aufgeschrieben haben.
Paul mag alle die Leute, die Bücher mögen. Ja,
und mit dieser Anfrage also müssen wir prüfen,
ob Karl Bücher mag,
gehen wir wieder in die Datenbank hinein und wir haben dem ersten Fakt eben,
das scheitert den zweiten Fakt,
haben wir den dritten Fakt,
haben wir,
der vierte Fakt liefert schließlich
einen Erfolg
Wir haben diese Anfrage also positiv beantwortet. Jawohl,
Paul liebt Karl, ist jetzt in der Relation enthalten.
Wir haben wieder vollständig installierte Anfragetests auf enthalten Seiten
in der Relation durchgeführt. Ich habe hier das gemacht,
was Sie niemals machen sollten,
fällt Sie jemanden auf
Das hängt damit zusammen, dass das noch die alte Abarbeitungsweise des Systems
auch ist, dass die Tracers etwas anders aussehen jetzt.
Ich hatte hier danach noch mal nach Alternativen fragen müssen.
Das geht jetzt, das ging früher nicht.
Früher konnte ich ja gar nicht nach Alternativen fragen.
Jetzt könnte ich es und dann müsste auch noch wieder überprüft werden,
ob ich denn die Regel hier unten noch mal anwenden kann.
Also das hängt, ist ein bisschen mit dieser Übergangssituation verbunden.
Früher hat das Prolog-System hier immer nur einen Success ausgegeben oder
ein True, wenn wenigstens ein Ergebnis ableitbar war.
Jetzt zählt es die verschiedenen Möglichkeiten der Ableitung tatsächlich auf.
Ich habe dann möglicherweise mehrfach ein True auf der Konsole,
wo ich immer wieder nach Alternativen fragen kann.
Und an der Stelle könnte ich jetzt nach alternativen Fragen,
die ich früher nicht, was ich früher nicht konnte,
habe ich hier aber nicht gemacht.
Die Botschaft ist nur, man sollte es immer tun,
wann immer einem das System die Möglichkeit bietet,
nach einer Alternative zu fragen, sollte man diese nutzen.
Denn die Probleme könnten in der nächsten Alternative auftauchen.
Hier das Gleiche nochmal auch in der gleichen unvollständigen Art und Weise im Trace.
Ich habe also die vier Fakten auf Unifikation überprüft, keiner davon unifiziert,
weil die Uniplikation immer schon auf der ersten Argumentstelle scheitert.
Die Regel ist einschlägig, dadurch wird X1
an Karl gebunden
Das ist diese vajablen Bindung hier hinten. Und mit dieser
vajablen Bindung gehe ich in den Körper der Klausel.
Also liebt Karl Bücher. Die Anfrage geht jetzt als
neuer Entscheidungspunkt als neuer Entscheidungspunkt in die Suche hinein.
Und tatsächlich kriege ich mit dem vierten Fakt das positiv abgeleitet.
Das heißt, das ist erfolgreich abgearbeitet worden. Ich kriege
einen Truh. Ich könnte jetzt also nach Alternativen fragen,
ob es noch weitere Möglichkeiten gibt, das abzuleiten.
Dann würde ich wieder auf diese Regel stoßen
Die würde aber nicht unifizieren, das sehen wir schon,
weil eben hier die Anfrage ist, mit Karl auf der
ersten Argumentstelle und die Regel ist nur für Paul einschlägig.
Das heißt, ein weiteres Ergebnis kriegen wir dadurch nicht.
Da scheitert also die Unifikation der Anfrage mit dem Regelkopf
bereits auf der ersten Argumentstelle. Das heißt also,
selbst wenn wir hier jetzt sagen würden, Backtracking,
bitte weitere Ergebnisse,
würde die nächste Unifikation scheitern und die Verarbeitung bricht entsprechend ab
Das sehen wir jetzt deutlicher,
wenn wir ein partiell instanziertes Ziel eingeben, also Venen liebt Paul,
jetzt haben wir hier nur Stadt des Karls, einfach eine uninstanzierte Variable.
Wir haben das gleiche Verhalten, die Fakten sagen über Paul
überhaupt nichts aus, die Regel sehr wohl.
Also gehen wir in die gleiche Regelabarbeitung.
Hier nur das X1 jetzt eben nicht an Karl gebunden ist,
weil ja hier eine Variable steht, sondern es wird die Co-Referenz hergestellt,
was immer der Wert von X1 ist.
Es wird auch der Wert von X sein. Also hier
wird jetzt eine Co-Referenzkette aufgebaut. Dach X habe ich gefragt,
nach X1, und das wird weitergereicht an die Frage nach
Regelkopf unifiziert, ja, dass wir gucken zusammen genau an,
der unifiziert, weil auf der Funktor der gleiche ist,
die Stelligkeit die gleiche ist,
auf der ersten Argumentstelle zwei Atome sind,
die unifizieren und auf der zweiten Argumentstelle zwei Variablen aufeinanderstoßen.
Ja, und zwar unterschiedliche Variablen, denn ich habe als erstes umgenannt.
Und wenn zwei Variablen aufeinanderstoßen, da können sie nochmal in
die Tabelle gehen, die wir letzte Woche hatten,
dann wird Co-Referenz zwischen diesen beiden Variablen hergestellt.
Und genau das steht hier. Ja,
und mit dieser vaiablen Belegung oder dem Wissen,
dass das Co-Referent ist, geht es jetzt eben in die Anfrage.
Das Ding ist nach wie vor uninstanziert.
Ich kenne den Wert von X1 nicht
Die Frage ist, kann ich Ihnen jetzt möglicherweise ermitteln?
Und damit gehe ich wieder hier rein.
Und tatsächlich kriege ich jetzt mit dem dritten Fakt eine Vajablenbindung,
denn jetzt kann ich X1 an Susi binden.
Und diese Vajablenbindung X1, gleich Susi, taucht als Ergebnis auf über diese Choreferenzkette.
X1 gleich Susi, also auch X gleich Susi,
dann heißt Paul liebt Susi. Wenn ich nach Alternativen frage,
dann geht er zunächst zum nächsten Fakt und auch dort
findet er jetzt den Karl,
der Bücher mag und deshalb ist das eine vajablen Bindung für
X1 und weil es wegen der Choreferenz ist es auch eine
vajablen Bindung für
Ich wieder nach Alternativen frage, dann komme ich jetzt wieder zu der Regel.
Die Regel, der Regelkopf unifiziert jetzt auch, ja,
und zwar mit der vajablen Bindung X1,
wird an Paul gebunden und X2 wird an Buch gebunden.
Das sind die vajablen Bindungen, die ich hier hinschreibe, kann.
Und ich rufe das Ganze jetzt mit dieser vajablen Bindung auf,
also X2 ist jetzt gleich Buch.
Ich kriege die neue Anfrage, wieder ein neuer Entscheidungspunkt.
Der dritte jetzt 1, 2, 3. Der dritte Entscheidungspunkt
fragt, mag ein Buch
ein Buch
Und damit gehe ich die Datenbank durch.
Die Fakten sagen nichts darüber aus, was Bücher mögen.
Und eine Regel habe ich darüber auch nicht.
Die sagt nur etwas über Paul aus. Das heißt,
keine dieser Fakten und auch die Regel, der Regel Kopf
unifizieren mit dieser Anfrage und damit kriege ich keine weiteren Ergebnisse.
Die beiden Ergebnisse, die ich hier hatte, sind die einzigen,
wenn ich wieder nach Alternativen frage, kriege ich dann ein False raus.
Das Gleiche nochmal im Trace, damit man das nochmal gegenüberstellen kann.
Also diese partiell instanzierte Anfrage, die Fakten liefern keine Ergebnisse,
die Regel unifiziert sehr wohl, weil Paul hier, Paul dort,
mit der Choreferenz X gleich X1, das steht hier hinten.
Und dann wird für diese Regelabarbeitung ein neuer Entscheidungspunkt aufgemacht,
mit dem Körper der Klausel als neuem zu befriedigenden Ziel,
unter der Variablenbindung X gleich X1. Also X1 ist uninstanziert,
damit geht es in die Suche hinein.
Der erste Fakt, der unifiziert, ist der hier mit Susi.
Susi wird ein X1 gebunden, auf der zweiten Argumentstelle stimmen
diese Dinge überein. Und diese Variablenbindung X1 gleich Susi und
damit auch X gleich Susi wird nach außen ausgegeben.
Wenn ich jetzt nach Alternativen frage,
geht er im gleichen Entscheidungspunkt Nummer zwei an den nächsten Fakt heran,
denn er hat sich ja gemerkt,
bis wohin
die Verarbeitung fortgeschritten war
Nimm diesen Fakt, stellt eine neue Variablenbindung her,
nämlich X1 gleich Karl und damit auch X gleich Karl und das
wird nach außen ausgegeben. Wenn ich wieder nach Alternativen frage,
komme ich zu der Regel,
die Regel unifiziert mit dieser Anfrage hier,
in dem X1 Paul wird und X2 kriegt den Wert Buch und damit
ist die neue Anfrage Liebbuch-Buch und Liebbuch-Buch führt mit keinem Eintracht der Datenbank zu einem Ergebnis.
Ich bin am Ende dieses Entscheidungspunktes 3 angekommen,
ich mache ein Backtracking zum Entscheidungspunkt 2
Im Entscheidungspunkt 2 ist mein Pointer auch an der letzten Stelle angekommen,
denn ich hatte bereits die Regel abgearbeitet.
Das heißt, hier gibt es keine weitere Alternative.
Ich mache ein Backtracking zum Entscheidungspunkt 1. Auch den hatte ich
bereits bis zu der Regel abgearbeitet.
Das heißt, weitere Ergebnisse kriege ich nicht.
Das heißt, das nächste Ergebnis heißt Falls. So,
wir können das Ganze umdrehen und fragen, ja,
wer liebt denn Bücher? Das haben wir im Prinzip
schon gesehen. Das ist die eingebettete Anfrage hier gewesen.
Wer liebt Bücher? Hier haben wir das.
Das ist jetzt sozusagen unsere Top-Level-Anfrage, die wir stellen.
Und deshalb wird das Verhalten
nicht anders sein
Wir gehen die Fakten durch mit dem dritten Fakt,
kriegen wir die Informationen. Susi mag Bücher, wenn wir
nach Alternativen fragen, kriegen wir mit dem vierten Fakt Karlmarktbücher.
Und dann kommt wieder diese etwas obskure Anfrage, ob denn
Bücher Bücher mögen. Und die liefert kein weiteres Ergebnis.
Das heißt, mit diesen beiden Ergebnissen haben wir alles erreicht.
Das ist also sozusagen ein Teil des Suchraumes,
den wir bei dieser Anfrage ohnehin abarbeiten mussten.
Gut, jetzt das Gleiche vollständig und uninstanziert.
Also wer liebt was
Jetzt habe ich hier natürlich zwei Variablen.
Die stoßen hier auf zwei Atome. Das heißt,
die Atome werden als Wert an die Variablen gebunden.
X kriegt den Wert H und Y kriegt den Wert G.
Und das wird als erstes Ergebnis ausgegeben. Wenn ich nach Alternativen frage,
kriege ich eben Hans als Wert von X,
Susi als Wert von Y und ich kriege ein zweites Ergebnis.
Das dritte ist dann Susi, lieb Bücher, das vierte SK-Liebbücher.
Damit habe ich die vier Ergebnisse aufgrund der Fakten,
die meine Prädikatsdefinition enthält, bereits Ermittelt?
Ich komme zu der Regel hier hinten. Jetzt wird X
an Paul gebunden und eine Co-Referenz zwischen Y und X1 hergestellt.
Das ist dieses hier. Und mit dieser vajablen Bindung
gehe ich jetzt in den Körper der Klausel.
Das heißt, das wird wieder uninstanziert aufgerufen.
Wer mag Bücher? Und das kommt uns irgendwie bekannt vor.
Das ist genau die Anfrage, die wir hier schon mal gestellt haben.
Also wir können hier übernehmen, was wird passieren?
Wir werden zwei weitere Ergebnisse bekommen und kein drittes.
Das heißt also, was ist insgesamt die Bilanz,
heißt, wir kriegen vier Ergebnisse über die Fakten,
wir kriegen zwei Ergebnisse über diese Regel.
Insgesamt enthält die Relation inklusive unserer Regel
sechs Tupel
Und die kriegen wir alle aufgezählt. Das heißt,
wir haben wieder dieses Verhalten,
was wir schon bei den relationalen Datenbanken festgestellt hatten,
wenn wir vollständig instanziert aufrufen, sagt uns das System neu,
ja oder nein, ist enthalten in der Relation oder nicht.
Wenn wir es partiell unterspezifiziert aufrufen, kriegen wir wieder diesen Informationsanreicherungsaspekt,
also alle Vayablenbindungen, die das Ding bar machen.
Und das heißt im Falle der vollständigen Unterspezifikation,
die Relation wird vollständig aufgezählt.
Denn ich habe keinerlei Einschränkungen vorgegeben. Ich sage hier,
zähle mir alles auf oder gib mir alle Vayablenbindungen,
die unter dieser Relation
gültig sind
Und das sind in diesem Falle sechs Paare, die ich
dann ausgegeben bekomme. Das Ganze wieder nochmal im Trace,
aber das machen wir jetzt ganz kurz.
Wir haben also hier die vier Fakten,
die uns die vier Tupel liefern über die Fakten.
Dann kommt die Regelanwendung und diese Regelanwendung haben wir jetzt schon dreimal gesehen.
Und die Backtrackings hier hinten, die ziehen immer nicht,
weil ich bereits bei der letzten Klausel jeweils angekommen war und
deshalb keine weiteren Ergebnisse produzieren kann
So, jetzt gucken wir uns mal an,
was wir für spezielle Relationstypen haben. Aus der Mathematik her
kennen wir ja da eine ganze Reihe von Sachen,
die wir als Eigenschaften von Relation bezeichnen können.
Die sind natürlich, weil die Mathematik Relationen immer als zweistellig
betrachtet auf zweistellige, auf den zweistelligen Fall beschränkt.
Gegebenenfalls müssen wir die also erweitern auf höherstellige Relation.
Aber die Grundidee können wir natürlich beibehalten. Also die Mathematik
kennt beispielsweise symmetrische Relationen, transitiver Relation, reflexiver Relation.
Es gibt noch eine Reihe mehr, aber das sind die drei,
die für unsere Zwecke hier wesentlich sind.
Und wir werden noch etwas betrachten, was unter unterschiedlichen Namen geht,
als linkseindeutige Relation oder rechtseindeutige Relation in der relationalen Datenbankwelt,
werden die dann auch als 1 zu M oder M zu 1-Relation bezeichnen.
Ich werde für den allgemeinen Fall sagen, sie sind funktional in einem Argument.
Das sind die vier Klassen, die wir jetzt hier betrachten.
Da schauen wir uns an, wenn wir jetzt hingehen,
solche Relationen zu spezifizieren, auf was müssen wir achten?
Oder umgekehrt, wir wollen eine Relation spezifizieren und das Prolog-System
macht standardmäßig eine Relation mit einer dieser Eigenschaften draus.
Wie können wir
das möglicherweise verhindern
Ja, also die Idee ist dabei,
dass wir dahin kommen,
nicht prozedural zu denken und das ist der Durchgang durch die ganze Veranstaltung hier,
dass wir wegkommen von den prozeduralen Details einer Programmiersprache,
dass wir irgendwann mal auch nicht mehr so weit sind,
dass wir Schritt für Schritt den Drehs durchgehen und sagen,
ah, jetzt macht die Suche dieses,
dann inifiziert sie das mit dem und deshalb geht es jetzt hier so weiter,
sondern dass wir irgendwann dahin kommen,
dass wir wirklich den deklarativen Aspekt des Programms sehen,
dass wir das als Spezifikation einer Relation sehen und ein guter Einstieg dahin ist,
als allererstes nachzudenken, was sind die Eigenschaften,
die die Relation hat?
Möchte ich, dass sie symmetrisch ist oder möchte ich
es nicht
Und je nachdem, wie ich die Frage beantworte,
kann ich ein Programm pattern oder ein anderes Programm pattern anwenden.
Da möchte ich hinkommen. Oder eben transitive Relationen. Wie schreibe ich
eine transitive Relation auf, damit ich nicht in bestimmte Fallen laufe?
Jetzt kommt nämlich der Punkt, wo die ersten Fallstricke auftauchen dabei.
Was kein Wunder ist, denn wir reden über Programmierung.
Programmierung ist voller Fallstricke. Also erste Eigenschaft ist Symmetrie.
Die Mathematik sagt uns, eine zweistellige Relation ist symmetrisch,
wenn, denn OSP von XY folgt in beiden Richtungen,
P von Y X.
Wenn dies allgemeingültig ist, das heißt für alle Tupel
der Relation gilt,
dann ist die Relation symmetrisch
Also aus der Tatsache, dass A.B. liebt,
kann ich immer folgen, und zwar immer.
Allgemeingültigheit heißt immer, immer folgendes auch B.A. liebt.
Wenn das der Fall ist, dann ist eine Relation symmetrisch.
Also nehmen wir eine symmetrische Relation, beispielsweise die Zwillingsrelation.
Zwillinge sind immer, es ist eine symmetrische Angelegenheit. Schöner,
ja, netterweise auch eine, eine binäre Angelegenheit, sonst sind es ja Drillinge.
Oder Einlinge, weiß ich nicht, wie nennt man das?
Okay. Ich möchte also die Symmetrieeigenschaft in der Relation haben,
dass wenn ich weiß, dass Paul und Anne Zwillingsgeschwister sind,
dann ist eben Maul, Zwillingsgeschwister von Anne und Anne, Zwillingsgeschwister von Paul.
Wenn wir also einfach nur eingeben, Paul und Anne sind Zwillinge,
das ist jetzt unsere Prädikatsdefinition und Hans und Nieder sind Zwillinge,
dann können wir zwar fragen, sind Paul und Anne Zwillinge,
aber wenn wir umgekehrt fragen, sind Anne und Paul Zwillinge,
dann kriegen wir ein Falls.
Das ist natürlich nicht das, was wir haben wollen.
Also die Botschaft daraus ist, wann immer wir exzensional
eine Relation definieren und das haben wir hier getan.
Wir haben zwei Fakten aufgeschrieben, dann ist das standardmäßig unsymmetrisch.
Und wenn wir es symmetrisch haben wollen, müssen wir Aufwand treiben.
Das Prolog-System sagt standardmäßig, das Prolog-System, auch jedes relationale Datenbank-System, sagt das.
Woher weiß ich denn, wenn das gilt, dass auch das gelten soll?
Aber wenn ich es machen will, muss ich es irgendwie kodieren.
Und das habe ich drei Möglichkeiten, das zu tun.
Erste Variante ist, ich baue Redundanz in die Datenbank ein.
Wir hatten gerade, dass das nicht ganz unproblematisch ist.
Also ich sage immer, was ist die Inverse,
das Inverse Tupel? Also in dem Augenblick,
wo ich Paul und Anne als Zwillingsgeschwister eintrage,
muss ich auch immer eintragen an und Paul sind ebenfalls Zwillingsgeschwister.
Und wenn Hans und Nina Zwillingsgeschwister sind,
dann auch Nina und Hans
Will man nicht unbedingt haben. Hat Redundanz,
Redundanz ist speicheraufwendig, schön, können wir vielleicht tolerieren,
aber ist insbesondere Update anfällig. Wenn ich also feststelle,
na das stimmt ja gar nicht, hier die Eintragung,
nehme das eine raus, vergesse aber das andere mit rauszunehmen,
sitze ich schon in der Patsche,
dann ist meine Datenbank nicht mehr konsistent.
Also gibt es alternative Möglichkeiten, das zu erledigen?
Ja, die gibt es. Wir können,
wir haben jetzt Regeln zur Verfügung und wir werden die Regeln benutzen,
um die Symmetrieeigenschaft zu erzwingen. Also wir sagen jetzt,
wir tragen erstmal die Fakten nur noch ein, das heißt jetzt ZV,
ich könnte irgendeinen beliebigen Namen an der Stelle nehmen und sagen,
Paul und Anne sind in dieser Beziehung, dass sie Zwillinge sind.
Und Hans und Nina sind es auch.
Wir sehen, die Fakten enthalten keine Redundanz mehr.
Und jetzt schreiben wir außen drumherum einen Rapper,
der sagt, X ist Zwilling von Y,
wenn sie denn hier in der Datenbank eingetragen sind.
Also das wird direkt auf ZV XY abgebildet.
Heißt nichts anderes, X und Y sind Zwillinge,
wenn denn die Eintragung hier in der Relation ZV stehen.
Und zweite Regel, ich drehe die Argumente einfach um.
Also X und Y stehen in der Relation Zwilling von,
wenn sie denn in der Faktendatenbasis,
in der umgekehrten Reihenfolge YX stehen
Ja, also ich nehme einmal die Original-Reihenfolge der Argumente.
Ich drehe andererseits plötzlich die Argumente um und habe damit
die umgekehrte Richtung auch in der Datenbank drin.
Es gibt noch eine dritte Variante, die wir hier aber nicht genauer betrachten werden.
Das ist, ich versuche das Ganze wissensbasiert zu modellieren. Ich überlege mir,
was definiert eigentlich ein Zwilling und ein Zwilling kann ich etwa definieren,
er hat eine Mutter, also der eine davon,
X hat eine Mutter und es ist die gleiche Mutter,
die auch Y hat.
Und X hat Geburtstag an einem bestimmten Tag und Y
hat auch Geburtstag an einem bestimmten Tag
Wenn das gegeben ist, ist damit der Zwilling definiert.
Dann kommen Sie mir nicht mit den ganzen Sonderfällen
Gebot um Mitternacht und der eine noch an diesem Tag,
der andere am nächsten Tag,
möglicherweise ist der Jahreswechsel noch dazwischen gewesen und so weiter und so fort.
Da gibt es auch wieder zahlreiche, wir abstrahieren.
Wir machen das, was wir die ganze Zeit machen,
wir beschränken uns auf die regulären Fälle und abstrahieren von allen Absünderlichkeiten.
Ich glaube, in diesem Falle trägt das Standesamt
auch das gemeinsame Gebautsdatum ein und nicht unterschiedliche Geburtsdaten.
Aber ich kenne mich nicht aus, ich habe es nie geschafft,
Zwillinge hervorzubringen. Gut, das sind die drei Möglichkeiten,
die wir haben. Wir gucken uns jetzt insbesondere die zweite
Möglichkeit an und modifizieren die noch
ein bisschen
Ah gut, ich habe die natürlich überhaupt nicht getestet,
das ist nicht sehr schön,
aber das kann man mir glauben, dass das funktionieren sollte.
Wenn ich jetzt also aufrufe, Zwilling von Paul Anna,
Anna, dann ruft er dieses ZV-Prädikat auf mit der Variablenbindung X,
Paul, Y, Anne und da findet er einen Fakt und damit kann er das positiv ableiten.
Wenn ich es in der umgekehrten Richtung aufrufe,
Zwilling von Anne Paul, dann wird eben X an Anne gebunden,
Y an Paul,
dann geht es mit dieser Variablenbindung in die Datenbank,
da findet er nichts dazu
Also Backtracking gibt es noch Alternativen. Ja,
es gibt noch eine zweite Regel, die ich probieren kann.
Dabei drehe ich die Argumente um und damit kriege ich das Ganze erfolgreich abgeleitet.
So, nun kann man sich fragen, ist diese Konstruktion,
die man hier hat, tatsächlich sinnvoll oder nicht?
Oder sieht ja ein bisschen kompliziert aus. Ich muss so
ein Hilfsprädikat einbauen, das will ich eigentlich gar nicht haben.
Und der Mathematiker würde es so auch nicht hinschreiben.
Der Mathematiker würde es so hinschreiben, dass er sagt,
ja, wenn XY umgekehrt, Ben Y und X Zwillinge sind,
dann sind auch X und Y Zwillinge.
Das ist die Implikation, die hier steht,
die Regel
Und er trägt natürlich die Zwillingsfakten mit der gleichen Relationssignatur ein wie die Regel ebenfalls.
Wir haben hier Regel und Fakten sozusagen in zwei separate Prädikatsdefinitionen
ausgelagert, die Regeln greifen immer auf die Fakten zu.
Also wir haben sauber voneinander getrennt, hier die Fakten,
hier die Regeln. Und der Mathematiker würde eben einfach alles
als Axiome in ein Aktionssystem werfen und sagen,
so nun wollen wir doch mal ableiten.
Wenn wir das machen und das als Datenbank eingeben,
dann kriegen wir
folgendes Verhalten
Wir rufen das Ganze auf mit Zwilling von Paul Anne und natürlich kriegen wir einen True raus,
weil das gleich der erste Fakt ist, den er findet.
Das ist das erste True. Wenn wir jetzt noch Alternativen fragen,
dann geht er in die Regel und in der Regel
wird jetzt Paul X an Paul gebunden,
Y an Anne gebunden und das Ganze mit den umgekehrten,
einer umgekehrten Argumentreihenfolge wieder aufgerufen.
Jetzt fragt er nach Zwilling von Anne Paul,
da findet er natürlich nichts in der Faktensammlung,
aber er hat ja noch eine Regel.
Und die Regel sagt, wenn die Reihenfolge der Argumente
nicht funktioniert, versuch es doch mal mit der anderen
Also wieder zurück, Zwilling von Paul Anne. Natürlich steht das in der Datenbank
drin. Und wenn ich nach Alternativen frage, ja,
dann tauschen wir die Argumente doch, mach mal aus,
nutzt nichts, aber wir können sie auch ein zweites Mal austauschen.
Ah, wir kriegen ein drittes Ergebnis, ja. Also,
wir kriegen hier plötzlich eine Aufzählung von Möglichkeiten,
die in diesem installierten Fall noch nicht mal das Schlimmste ist,
aber wir kriegen natürlich genauso viele vaiablen Bindungen, wenn wir das uninstanziert aufrufen.
Ja. Das ist ein Zyklus-Tipp, also die Aufrufe sind einfach Ja,
das gucken wir uns zum, das gucken wir zum Schluss noch an.
Man könnte in diesem Falle eine einfache Überwachung einbauen,
ob dieser Aufruf schon mal passiert ist. Das Problem ist nur,
und darüber können Sie nachdenken, wenn ich,
wenn das allgemeingültig funktionieren würde, hätte ich das Halteproblem der Turing-Maschine gelöst.
Ja, dann könnte ich nämlich beliebige Programme zum Halten zwingen.
Also irgendwo muss der Wurm drin sein und Sie können jetzt
schon mal überlegen, wo der Wurm ist.
Also natürlich für einfache Fälle, ja, definitiv, das geht.
Wir werden uns auch bei der Metaprogrammierung ein Zyklen vermeidenden Interpreter angucken.
Das wird eine der ersten Amtshandlungen sein,
wenn wir die Semantik des Prolog-Systems verändern, dass wir sagen,
können wir nicht ein Produkt-System bauen
Was immer genau das überprüft, was eine History mitführt und guckt,
ob der gleiche Aufruf vorher schon mal gemacht worden ist.
Und auf die Art und Weise können wir das genau verhindern.
Das nackte Prolog-System tut es nicht. Den Service stellt es
uns und es tut es aus gutem Grund nicht.
Das müssen wir uns noch angucken. Gut,
das Gleiche also in der umgekehrten Richtung,
wenn wir das schon von vornherein umgekehrt eingeben,
ist natürlich das gleiche Verhalten.
Er tauscht immer zyklisch
die Argumente aus und produziert immer
neue Ergebnisse
Wir können das Ganze auch unterspezifiziert aufrufen und dann sehen wir deutlicher, was passiert.
Also wir kriegen Elmar erst die Variante Paul und Anna
sind Zwillingsgeschwister, aber Anna und Paul sind auch Zwillingsgeschützer.
Bis dahin ist ja alles okay, das wollten wir ja auch.
Aber wenn wir weiter fragen, dann geht es eben wieder von vorn los.
Paul und Anna sind Zwillingsgeschwister an und Paul sind Zwillingsgeschwister.
Und je nachdem, wie lange wir das treiben,
umso mehr Ergebnisse bekommen wir.
An dieser Stelle sehen wir erstmalig zwei Dinge.
Das erste ist, wir kriegen potenziell unendlich viele Ergebnisse.
Also der Versuch, hierüber ein Find All zu machen und
alle diese Ergebnisse aufzusammeln, führt dazu,
dass sich das System in eine Schleife treibe
Dann endlos lange, natürlich nicht endlos lange,
sondern genau so lange, bis die Systemressourcen erschöpft sind und das erste,
was erschöpft ist, ist der Kellerspeicher des Systems.
Wenn der überläuft, dann gibt es einen Fehler.
Also wir zwingen das System zum Absturz sozusagen.
Deshalb ist das Fine-All-Verwenden relativ risikoreich. Ich habe es
nicht ohne Grund bei den relationalen Datenbanken eingebaut.
Relationale Datenbanken haben immer endliche Relationen. Deshalb ist garantiert,
dass das Fine-All auch endlich viele Elemente produziert.
Jetzt ändert sich die Welt. Hier ist es noch
keine unendliche Relation, sondern ein Mangel unserer Ableitungsmaschine.
Nämlich genau, dass sie nicht feststellen kann, ja,
Herrgott, das habe ich ja schon fünfmal gemacht.
Warum soll ich das dein sechstes Mal machen? Dass dies
nicht festgestellt wird. Also, denortationell heißt die Antwort,
diese Relation hier enthält zwei Tupe.
Nämlich dieses Tupel und dieses Tupel. Operational heißt es,
die Suche geht an Infinitum und erzeugt beliebig viele Variablen Bindungen.
Hier fällt erstmalig die demotationelle Semantik, die ich haben will,
und die operationale Semantik, die ich auf der Maschine habe,
das ist nicht mehr das Gleiche.
Hier kriege ich jetzt plötzlich einen Unterschied zwischen diesen beiden Semantikbegriffen.
Und wir müssen uns fragen, woran liegt es eigentlich,
dass diese dieser Unterschied auftaucht. Es ist noch schlimmer,
es kommt noch viel, viel schlimmer,
wenn ich das Ganze mit einem unbekannten Objekt aufrufe,
also Zwilling, Paul K., sie erinnern sich in der Datenbank,
war immer von Paul und Anne und sonst so was die Rede.
Also nur, dass ich jetzt hier eine Person Karl einführe und wissen will,
ob Paul und Karl Zwillingsschwester, Zwillingsgeschwister sind, führt dazu,
dass das System abschmiert und zwar richtig abschmiert.
Auch hier wieder, bis es irgendwann die Systemressourcen erschöpft sind.
Denn was passiert hier
Ich rufe das damit auf und finde keinen Fakt in
der Datenbank, der das ableiten lässt.
Also tausche ich die Argumente aus und versuche es nochmal.
Auch dann werde ich nichts finden über Karl,
steht in der Datenbank nichts drin. Das Rezept sagt,
na dann tausche ich die Argumente aus und probiere es nochmal.
Und das hat Infinitum. Ohne ein einziges Ergebnis produziert zu haben.
Allein der Aufruf reicht aus, das Ganze in eine unendliche
Schleife zu bringen. Also es ist keine Schleife,
es ist ein rekursiver Abstieg, der also Gott sei
Dank kann man an der Stelle nur sagen,
immer neue Einträge auf den Kellerspeicher schreibt und der läuft irgendwann
mal über
Also hier auch wieder eine Differenz. Wir hatten eigentlich denotationell erwartet,
dass das Ziel nicht in der Relation enthalten ist und das Resultat sollte also false sein.
Was wir kriegen, ist eine Suche, die nicht terminiert.
Das ist natürlich nicht schön. Wir haben, wir können
einen Verdacht ableiten, wenn wir uns die Unterschiede angucken.
Hier, diese Definition, die hat das Problem nicht hervor,
oder das müssen wir jetzt glauben,
ich habe die Tests hier nicht dargestellt.
Das fällt mir gerade auf, dass das eigentlich Mangel ist.
Hier kriegen wir plötzlich das Problem, dass wir unendlich viele Ergebnisse bekommen,
dass wir hier einen nicht terminierenden Aufruf haben und so weiter und so fort.
Was unterscheidet diese, diese Definition eigentlich von dieser,
die wir hier haben? Hat jemand einen Verdacht?
Also das ist die eine, die ist einigermaßen sicher.
Das ist die andere, die Probleme hervorruft, ja.
Ja, gut. Ja, okay, hier wird es eben zweimal angefasst.
Aber im Prinzip steht da so eine ähnliche Regel da auch.
Also so substanziell unterscheiden die sich nicht da.
Doch, sie unterscheiden sich substanziell, aber was ist
das Substanzielle
Die wäre es etwas. In der Tat, die ruft sich selber wieder auf.
Hier ruft das Prädikat Zwilling von wieder das Prädikat Zwilling von auf.
Und dann sind wir im Zyklus drin. Das passiert hier nicht.
Hier ruft Zwilling von ZV auf und ZV ruft kein anderes auf.
Hier gibt es keinen zyklischen Aufruf drin. Hier gibt es
einen zyklischen Aufruf drin. Das ist der fundamentale Unterschied.
Und deshalb also der Verdacht, dass diese rekursive Spezifikation
Grund für die Terminierungsprobleme ist. Und der Ausweg,
und das ist tatsächlich, das können Sie sich merken,
die terminierungssichere Variante ist, ich muss Eine solche unbeschränkte Rekursion,
wie sie hier verwendet worden ist.
Ja, also das Zwilling von unbeschränkt,
ohne dass irgendeine Information hinzukommt, sondern einfach nur tauscht die Argumente aus.
Einen solchen unbeschränkten rekursiven Abstieg muss ich vermeiden.
Der ruft offensichtlich Probleme hervor. Und eine Möglichkeit,
wie ich es vermeiden kann, ist dieses hier.
Ja, das ist eine vollständige Definition. Der einzige Trick,
den man macht, ist dieses Hilfsprädikat, was hier eingefügt wird.
Aber dadurch bricht man den Zyklus auf.
Zwilling von ruft jetzt nicht mehr Zwilling von auf.
Und deshalb kriege ich hier
das Terminierungsproblem nicht
So, also was heißt Rekussion?
Rekussion im allgemeinen Fall heißt Selbstbezüglichkeit einer formalen Struktur.
Die tritt in unseren Fall im Wesentlichen in zwei Formen auf.
Einerseits eine Prädikatsdefinition wird auf sich selbst zurückgeführt.
Das ist eben genau diese Klausel hier, Zwilling von XY
, wird auf Zwilling von Y X zurückgeführt.
Die zweite Form ist, ein Datentyp wird durch sich selbst definiert.
Das werden wir dann im Falle der Strukturen und der Listen uns angucken,
wo die Elemente einer Liste wieder Listen sein können und die Elemente einer Liste von Listen,
wieder Listen sein können und so weiter.
Auch das geht bis in unendliche Tiefe fortzuführen
Das werden wir als drittes Element brauchen, wenn wir Thuring-Berechnungsäquivalenz erreichen wollen.
Wenn wir also eine berechnungsuniverselle Programmiersprache haben wollen,
brauchen wir diese rekursiven Datenstrukturen auch noch.
Das ist der dritte Schritt, den wir als nächstes
dann machen. Jetzt machen wir erstmal den zweiten Schritt.
Wir bauen rekursive Prädikatsdefinitionen, erlauben rekursive Prädikatsdefinitionen.
Und wir müssen gucken, was ist daran eigentlich gefährlich.
Und das ist genau das, was hier passiert.
Eine Hand malt die andere Hand genauso in diesem Zyklus,
stecken wir hier auch drin.
Und da können wir eben beliebig oft in dem Zyklus herumgehen,
ohne dass wir jemals ein Terminierungskriterium erfüllen
Gut, das gucken wir uns dann später an,
wie wir das in den Griff bekommen. Wir schauen uns
den nächsten Typ von Relation an. Das sind die transitiven Relationen,
transitive Relationen spielen in vielen Bereichen des täglichen Lebens eine wichtige Rolle.
Ein paar davon habe ich rausgegriffen. Also hier ist es
ein Teil von Hierarchie. Ich sage also,
ein Auto besteht aus Rädern,
aus Motor und so weiter und so fort und Räder bestehen
wieder aus Felge und Reifen und Narbe, was weiß ich was.
Der Motor enthält Kolben, Ventile, Zündkerzen und dergleichen mehr
. Auf die Art und Weise kann ich also jetzt
terminologische Wissen uns Repräsentationen beschreiben und Relationen ermitteln der Art,
wie ist die Felgebestandteil des Autos.
Und das kann ich eben aufgrund der Elementarinformationen nicht ermitteln.
Ich muss die sogenannte transitive Hülle bilden und sagen,
ja, die Felge ist kein unmittelbares Teil des Autos,
aber über die Vermittlung des Rades ist sie es schon.
Ja, und genauso gilt das eben für die Zündkerze.
Zündkerze ist Teil des Motors, Motor ist Teil des Autos,
also auch Zündkerze, ein Teil des Autos.
Und die Regel, die dem Ganzen zugrunde liegt, sagt also,
wenn XY in einer Relation stehen und YZ in einer Relation stehen,
dann folgt daraus auch das XZ in der Relation stehen.
Und das muss wieder
allgemeingültig sein
Das muss für beliebige Paare XY und YZ gelten.
Und das hat man eben an vielen Punkten,
das findet man auch in so etwas wie der Werbung.
Ich habe sie leider mir nicht, ich kann es nicht mehr finden,
aber die Lufthansa hatte mal eine Zeit lang eine Werbung,
die da lautete,
wir bringen sie nicht nur von A nach B und von B nach C,
sondern wir bringen sie auch von A nach C. Nichts anderes als die transitive Hülle über eine Erreichbarkeitsrelation.
Also in In Werbeslogans wird es sogar alles genutzt.
Vielleicht haben sie gerade Mathematiker angeheuert und ihn gefragt,
wie man am besten Reklame für Flugverbindung macht. So,
jetzt modellieren wir das, als Eintragung in der Datenbank,
als deduktive Datenbank. Es gibt wieder Alternativen dazu,
das Prolog-System hat den Reiz,
dass es uns ganz verschiedene Werkzeuge anbietet und wir immer vor der Designentscheidung stehen,
wie wollen wir das eigentlich implementieren?
Und hier lautet die Implementierungsvariante, wir bauen das in die Datenbank ein,
in eine Form einer Faktensammlung, mit Regeln ergänzt und die Alternative wäre,
wir benutzen eine rekursive Datenstruktur dafür.
Das gucken wir uns später an
Also hier haben wir unmittelbare Teile von Beziehung UTV.
Die Felge ist Teil des Rades und das Rad des Teil des Autos und des Motors
ist Teil des Autos und so weiter und so fort.
Und wir sagen jetzt, ein Teil ist Teil eines anderen.
Wenn es denn ein unmittelbares Teil ist, ja,
wir sehen das gleiche Pattern hier,
was wir auch schon bei der Symmetrie benutzt haben.
Wir erreichen einfach die Argumentbelegungen für X und Y an das Hilfsprädikat
weiter. Und das Hilfsprädikat sagt uns dann den Fakt,
ja, Felge ist Teil des Rades.
Also ist auch, es ist unmittelbares Teil des,
oh Gott, ist unmittelbares Teil des Teil von.
Und deshalb ist auch die Felge Teil von einem Rad
Wenn das nicht erfolgreich ist, dann müssen wir eben die transitive Hülle bilden.
Da müssen wir gucken, ob wir irgendwie über Zwischenstationen diese Beziehung herstellen können.
Und das heißt, wir gucken als erstes,
ist denn dieses Teil X unmittelbares Teil von irgendetwas anderem Y?
Und dieses Y muss dann Teil von Z sein.
Und dann nehmen wir eben hier unmittelbares Teil von,
das macht einen Schritt. Und der zweite Schritt sagt dann,
ja, jetzt musst du sehen, wie du von dort aus weiterkommst.
Denn diese Hierarchie ist ja, die wir hier haben,
ist ja nicht auf zwei Etagen beschränkt,
die kann ja beliebig viele Etagen haben.
Und den nächsten Schritt, den wir machen, ist,
wir wenden das Ganze auf Suchproblemen an. Eben genau,
wie komme ich von A nach B, also das Navigationsproblem.
Und da können wir ja auch nicht sagen,
dass es mit drei Schritten erledigt, sondern es können potenziell unendlich viele sein.
Auf jeden Fall kann ich keine Oberschranke angeben dafür.
Das heißt also, ich mache erstmal den ersten Schritt und
fahre in den Nachbarort oder benutze hier die nächste höhere Ebene.
Und dann muss ich gucken, ob ich von dieser nächst
höheren Ebene Y bis zu dem gewünschten Ziel Z komme.
Das ist also die Definition. Ich kann gleich vorwegnehmen,
das ist die Definition, die man verwenden sollte.
Denn die ist wieder terminierungssicher. Wir gucken uns dann das Problem
gleich wieder an. Also wir rufen das Ganze auf.
Wir fragen, ist die Felge Teil des Autos.
Wir rufen die Das ist die Anfrage, die wir stellen.
Wir kriegen als erstes die Regel,
die die beiden Argumente eins zu eins auf diese UTV-Hilfsrelation abbildet.
Also X1 wird dorthin gegeben, Y1 kommt dorthin.
Und mit dieser vaiablen Belegung ruft man das Ganze wieder auf.
Also ist die Felge ein unmittelbares Teil des Autos.
Und dann geht man die Fakten durch und keiner der Fakten
liefert etwas dazu. Also über die Tatsache,
dass eine Felge irgendwas mit dem Auto zu tun hat,
steht in unserer Datenbank
nichts drin
Also muss ich ins Backtracking gehen. Ich muss zu diesem Entscheidungspunkt
zurück. Und hier habe ich ja noch eine Alternative.
Und die Alternative sagt, ja,
wenn es denn nicht das unmittelbare Teil von ist,
dann gucke, ob es eine Vermittlung über eine Zwischenstation gibt.
Also suche erst ein bliebiges, unmittelbares Teil von und gucke
von dort aus weiter, ob du bis zu dem Z1 kommst.
Also es wird wieder der Kopf der Klausel unifiziert,
X1 wird an Felge gebunden,
Z1 wird an Auto gebunden und mit dieser baiablen Bedingung geht es jetzt in das komplexe Teilziel im Körper der Klausel.
Und das komplexe Teilziel der Körper, fragt jetzt als erstes Teilziel ab,
elementares Teilziel ab, ist die Felge Teil von irgendetwas anderem
Ja, und natürlich kriege ich damit eine valiablen Bindung,
die sagt, ja, na klar, die Felge ist Teil des Autos.
Nein, Quatsch, ist die Felge. Z1 ist ja gebunden,
ist, ich kriege X1 ist die Felge,
Y1 ist X1 wird an Felge gebunden,
Z1 wird an das Auto gebunden und mit dieser vaiablen Bindung
geht es in dieses erste Teilziel hinein mit der Frage,
wovon ist die Felge eigentlich ein unmittelbares Teil
Und jetzt geht es mit der Frage, mit dem ersten Teil,
zu denen die Datenbank und die Datenbank ruft natürlich zurück,
jawohl, die Felge ist Teil des Rades.
Damit habe ich jetzt auch eine vajable Mindung für Y1 und ich kann jetzt
mit dieser Vagablung für Y1 in das zweite Teilziel gehen.
Und das ist das, was hier ist.
Also die Bindung Rat ist, das Radheld des Autos.
Also die Felge ist Teil des Autos,
wenn ich nachweisen kann, dass das Radteil des Autos ist.
Und da komme ich eben wieder in die erste Regel und
die erste Regel ist hier bereits erfolgreich,
denn der Fakt, Rad ist unmittelbares Teil des Autos.
Das steht in der Datenbank drin. Damit ist das Ganze
positiv bestätigt und ich kann hier insgesamt die Antwort zurückgeben.
Jawohl, die Felge ist Teil des Autos,
weil eben die Felge unmittelbares Teil des Rades ist,
das Rad unmittelbares Teil des Autos ist,
ist auch über die transitive Hülle die Felge Teil des Autos.
So, Sie sehen also schon, das wird zunehmend haariger,
sich da noch durchzuforsten und deshalb brauchen wir Abstraktionsebenen,
auf denen wir uns auf bestimmte Berechnungseigenschaften verlassen können.
Das ist das Ziel jetzt zu zeigen, wie Ich habe eine transitive Relation.
Welches Programmpattern kann ich verwenden, wenn ich diese Relation spezifizieren will?
Da steht das. Also bildet das einerseits direkt ab
und andererseits mache ich den transitiven Schritt. Und dann
hast du noch das Hilfsprädikat, der unmittelbaren Teil von Beziehungen,
die eben hier und hier benutzt werden. Wir gucken uns an,
welche Eigenschaften das hat, sodass man dann also,
wann immer man eine solche transitive Relation zu programmieren hat,
weiß, wie muss man da rangehen.
Und was muss man vermeiden, um zu verhindern,
dass es Berechnungsprobleme gibt
Also hier gibt es jetzt noch Reihe, zusätzliche Anfragen.
Ich kann fragen, ist die Bremse Teil des Autos?
Ich kriege Falls, nanu, nanu, warum keine Bremse?
Da kann man gucken. Tatsächlich sagt unsere Datenbank nichts überbremsen aus.
Traurig, aber so ist es. Ich kann fragen,
wovon ist die Felge ein Teil? Und wir kriegen wieder
teilweise die Abarbeitung, die wir schon gemacht haben.
Wir kriegen erst über die unmittelbare Teil von Beziehung,
die Felge ist Teil des Rades. Und wenn wir nach Alternativen fragen,
wird die zweite Klausel benutzt und die macht den transitiven Schritt.
Und die sagt, ja, das Rad ist auch Teil des Autos,
damit ist auch die Felge Teil des Autos.
Und auch hier sehen wir wieder, diese Anfrage wird maximal angereichert mit Informationen.
Entweder wir setzen hier Rad ein oder wir setzen hier Auto
ein.
Beide Tubel sind bar
Umgekehrt kann ich eben fragen, was ist Teil des Rades?
Und weil wir hier eine Hierarchie haben, ist,
dass gleichzeitig auch ein Beispiel für eine linkseindeutige, für eine rechtseindeutige Relation.
Wir gucken uns die Hierarchie lieber an, für eine rechtseindeutige Relation.
Also das obere Teil ist immer eindeutig gegeben.
Wir haben hier an keiner Stelle so, dass wir sagen,
ja, das Rad ist Teil von dem und von dem.
Das ist eine Hierarchie. In der Hierarchie ist der Mutterknoten
immer eindeutig zu ermitteln. Und deshalb ist es kein Wunder,
dass wir hier an dieser Stelle genau ein Ergebnis bekommen
Da müssen wir uns arg wundern, wenn wir an
der Stelle, nee, Quatsch, was rede ich?
Das können wir, nein, unsere Datenbank ist unvollständig.
Sorry, ich bin im falschen Film,
dass wir hier nur ein Ergebnis bekommen, liegt daran,
dass wir in der Datenbank nur eine,
die Felge drin zu stehen haben.
Wenn wir schließlich das Ganze uninstanziert aufrufen,
kriegen wir wieder die gesamte Relation aufgezählt.
Hier ist ein bedauerlicher Fehler. Achso, nee, ist
kein Fehler passiert. Das sind also als erstes die Fakten,
die wir aufgezählt bekommen,
Felge ist unmittelbares Bestandteil des Rades
Und wenn wir die Fakten aufgezählt haben, dann kriegen wir
auch noch die Ableitungen. Und die sagt eben beispielsweise,
dass die Zündkerze Teil des Motors ist, aber der Motorteil des Autos ist,
weshalb also auch die Zündkerze Teil des Autos ist.
Wir kriegen also jetzt aufgezählt, sowohl die Tupel,
die direkt als unmittelbare Teil von Beziehung in der Datenbank als Fakten drinstehen,
als auch Tupel, die durch Anwendung der transitiven Schlussregel produziert werden.
Gut, auch hier gilt wieder,
dass man in die gleichen Probleme läuft, wie bei der Bei der Symmetrie,
wenn man dieses Pattern für die Transitivität so nicht verwendet,
sondern es so verwendet, wie der Mathematiker es machen würde,
dann kriegt man wieder die gleichen Terminierungsprobleme.
Und das, vielleicht gucken wir es uns in der Übung
nochmal an, das müssen wir noch überlegen. Ja,
funktionale Abhängigkeiten, funktionale Abhängigkeiten heißt eben immer in einer Argumentstelle,
ist der Wert eindeutig bestimmt, wenn ich den anderen kenne.
Also ich habe zwei Tupel, die haben das gleiche zweite,
die zweite Argumentposition, also Y, hier, Y dort.
Wenn das der Fall ist, dann weiß ich,
und das ist vom Typ 1 zu M oder heißt die erste Argumentstelle ist,
eindeutig gegeben, dann muss X gleich Y sein.
Wenn ich Y kenne, weiß ich, die erste Argumentstelle eindeutig,
das heißt das hier. Und das ist symmetrisch dazu,
wenn ich die erste Argumentstelle kenne, dann kenne ich eindeutig die zweite Argumentstelle.
Das ist die Definition, dass ihr es sagt.
Und das können wir jetzt erweitern auf beliebigstelle Relation.
Wir können also sagen, diese Relation ist eindeutig im fünften Argument.
Und der typische Fall von funktionalen Relationen,
die Sind die Schlüsselbeziehungen in relationalen Datenbanken,
wo eben funktionale Abhängigkeit vom Schlüssel gegeben sein muss.
Also der Schlüssel muss eindeutig das Wertetupe bestimmen. So,
also in diesem Falle haben wir es tatsächlich mit einem Spezialfall der Relation
zu tun, nämlich der eindeutigen Abbildung von Y nach X.
Und solche Relationen sind eben Spezialfälle einer M-zu-N-Relation,
standardmäßig macht das Prolog-System aber immer M-zu-N-Relation.
Wenn wir also eindeutige Relationen haben wollen, müssen wir wieder etwas tun.
Was wir tun, kommt noch nicht heute, sondern später.
Also Beispiele für eins Relationen ist etwa die Mutter von Beziehung.
Die Mutter sollte eindeutig gegeben sein. Ich habe es schon
in der Übungsgruppe diskutiert. Gerade letztens kam also die Meldung herum,
dass es jetzt erstmalig gelungen ist, Embryonen von drei Personen herzustellen.
Und zwar mit zwei Müttern. Und das ist in dem Fall
interessant. Von der einen Mutter stammt der genetische Bestand der Kommosom,
von der anderen Mutter stammen die Mitochondrien,
die Gebrauchtbären um den Zellstoffwechsel aufrecht zu erhalten und dann steht schon die Frage,
ist das eigentlich noch eine linkseindeutige Relation oder ist es nicht mehr?
Das Alter von einer Person in Jahren sollte eindeutig gegeben sein,
ja, auch da kann man sich wieder Ausnahmefälle und Sonderfälle überlegen.
M21-Relationen haben wir schon gesehen, also das unmittelbare Teil von ist so etwas,
solange wir also erzwingen, dass das eine Hierarchie ist,
ein Baum, dann ist das eindeutig der Mutterknoten gegeben,
eine Stadt liegt typischerweise eindeutig in einem Land.
Auch da gibt es Ausnahmefälle von geteilten Städten an verschiedenen Stellen in der Welt,
die durch historische Entwicklungen dann irgendwann mal zwischen zwei Ländern aufgeteilt worden sind.
Da kann man sich darüber streiten, sind das dann zwei
Städte, die in zwei Ländern liegen?
Oder ist es das eine Stadt, die in zwei Ländern liegt?
Also da gibt es verschiedenste Sonderfälle. Aber der typische Fall
ist eben, dass es da eine eindeutige Zuordnung gibt.
Ja, Prädikatsdefinition sind im Allgemeinen immer vom Typ M zu N.
Also ich kann beliebig viele Werte an der einen Position mit
beliebig vielen Werten an der anderen Position kombinieren.
Wenn ich jetzt eine eindeutige Relation habe,
dann könnte ich eigentlich die Eindeutigkeit der Relation ausnutzen für die Suche
Wenn ich ein Ergebnis gefunden habe, als wenn ich eine Mutter gefunden habe,
dann brauche ich nicht nach einer zweiten Mutter zu suchen.
Das Prolog-System ist blind dafür, es sucht nach der zweiten Mutter.
Und erst, wenn die Datenbank vollständig durchsucht ist,
ist es zufrieden und sagt, eine weitere Mutter gibt es nicht.
Diesen Suchaufwand könnte man sich möglicherweise sparen,
wenn man die Relation von vornherein als eindeutig kennzeichnen kann,
dass es gibt ein Hack, mit dem man das partiell erledigen kann,
aber das gucken wir uns wesentlich später an in dem Augenblick,
wo wir die Suche selber manipulieren,
wo wir dem System sagen,
was,
wann es mit der Suche aufhören soll
Das ist ein Operator, der heißt der Cut.
Damit kann ich sagen, so,
suche nicht nach weiteren Ergebnissen, es wird keine geben.
Allerdings hat das Ding auch wieder unangenehme Nebenwirkungen,
die wir uns im Einzelnen dann angucken müssen.
Das ist nur schon erstmal eine Andeutung.
Letzte Eigenschaft ist die Reflexivität, zweistellige Relation ist reflexiv,
wenn gilt jedes Element der Domäne steht mit sich selbst in der Relation.
Das kriegen wir immer dann ins Haus,
wenn wir auf so etwas wie die Unifizierbarkeit zurückgreifen.
In dem Augenblick, wo wir also x mit Y unifizieren,
folgt daraus sofort, dass y mit x auch unifiziert.
Also Unifikation ist immer reflexiv
Ein Ding unifiziert immer mit sich selbst. Und immer,
wenn ich mich auf die Unifiziergabbarkeit beziehe, kriege ich dann standardmäßig reflexiver Definitionen heraus.
Also hier beispielsweise, ich möchte die Schwester von Relationen definieren.
Ich sage X und Y sind,
oder in ein X ist Schwester von Y,
wenn X ein Kind einer Person Z ist und Y ist
auch ein Kind einer Person Z und das X muss weiblich sein,
sonst ist es keine Schwester, sondern ein Bruder.
Der Witz steckt hier in diesem Teil drin,
ich beziehe mich also auf das gemeinsame Elternteil und auf die Art und Weise wird die Relation reflexiv.
Wenn ich jetzt also irgendwelche Beispieldaten habe über Mutterkind oder Eltern-Kind-Beziehungen und die entsprechenden Geschlechtsangaben dazu und dann frage,
ist es eine Schwester von Susi oder welche S muss ich substituieren,
damit die Schwester von Susi sind,
dann kann ich eben in die Definition gehen.
Ich gucke als erstes, gibt es eine Person,
die das gleiche Elternteil oder die auch in der Kind von Relation zu,
wie Susi steht, Susi steht zu Anne in der Kind Elternrelation,
also kommt Jane.
Achso, nein
, halt
Ich kriege als erstes diesen Fakt hier,
Susi steht zu Anne in der Kind-Elternbeziehung,
Z wird Anne und mit der vaiablen Belegung gehe ich jetzt
in das zweite Teilziel und gehe wieder in die Datenbank rein.
Ich fange von vorn an, das heißt,
als nächsten Fakt kriege ich wieder, ja,
Susi ist Kind von Anne. Und Susi ist auch weiblich,
das heißt, das erste Ergebnis, was ich bekomme,
ist mit x gleich Susi, y, gleich Susi.
Mit anderen Worten, Susi ist Schwester von sich selbst.
Das will man üblicherweise nicht. Jane ist ja okay,
das zweite Ergebnis ist dann, dass sie tatsächlich eine andere Variablenbindung bekomme,
aber die erste Variablenbindung ist dummerweise die reflexive.
Ich kriege also die Schwesterbeziehung zu sich selbst,
was könnte man tun,
um es zu verhindern
Ja, definieren das. Bitte? Genau,
ich muss das, die Gleichheit hat es hervorgerufen,
also muss ich die Ungleichheit fordern. Ich muss genau sicherstellen,
dass das, was ich hier an X binde,
ein anderer Wert ist als das, was ich an Y binde.
Also da das in diesem Falle Atome sind,
ist es relativ unkritisch. Also wir bauen an der Stelle
einfach ein Darf-nicht-Unifizieren ein
Und damit haben wir das sichergestellt, dass dieses Ergebnis hier nicht produziert wird,
sondern nur das Zweite, Jana ist die Schwester von Susi. Gut,
das sind Beispiele dafür gewesen,
wie man jetzt die Eigenschaften von Relationen ausnutzen kann,
oder nein, wie, aus den Eigenschaften von Relationen,
ein bestimmtes Verhalten des Systems folgt,
da im Prolog fast alles auf die Unifizierbarkeit zurückgeführt wird,
sind also solche Prädikatsdefinitionen eben standardmäßig reflexiv und ich muss wieder aktiv etwas tun,
um es zu verhindern.
Relationen sind standardmäßig nicht funktional, wenn nicht die Funktionalität sicherschweren will, muss ich es erzwingen. Transitive,
für Transitive und Symmetrie, symmetrische Relation muss ich aufpassen,
dass sich die Definitionen so mache, dass sie nicht eine unbeschränkte Repussion enthält,
unbeschränkte Rekursion heißt immer, dass die Gefahr des Terminierens,
des Nicht-Terminierens besteht und es gibt bestimmte Patter,
mit denen man das vermeiden kann, das muss man einfach wissen.
Und deshalb ist es also immer die erste Frage,
dass man sich hinstellt und schaut, welche Eigenschaften der Relation möchte ich
eigentlich realisiert haben und was sind die Mittel, um diese Eigenschaften zu realisieren.
Und das werden wir uns dann nächste Woche an,
Beispiel zu einer Wegplanungsanwendung angucken,
wo ich also verschiedene Orte habe und Erreichbarkeitsbeziehungen zwischen den Orten,
das wäre also die Datenbank meines Navigationssystems und ich möchte dann Anfragen stellen,
wie komme ich von Düsseldorf nach Hamburg beispielsweise.
Und dann ist eben diese Erreichbarkeitsrelation, die man hier hat,
welche Eigenschaften hat, sollte sie haben, Sollte sie symmetrisch sein, ja oder nein?
Wenn ja, warum? Wenn nein, warum? Jetzt sollte es im Methos sein,
wenn wir von Hamburg nach Bremen fahren,
dann sollte man doch typischerweise ja, mit einer Ausnahme,
also das ist immer eine Frage der Granularität,
solange ich hier über solche Städte rede, ja,
Autobahnen sind immer in beiden Richtungen befahrbar, davon geht man typischerweise aus,
aber in städtischem Umfeld können es Einbahnstraßen sein und Einbahnstraßensysteme können so unglücklich gemacht sein, dass es möglicherweise keine Verbindung gibt.
Also ich vermute mal, die Polizei achtet ein bisschen drauf,
dass das nicht passiert, dann würden sich nämlich die Anwohner härter beschweren,
aber theoretisch ist es möglich.
Also man könnte leichte Datenbank bauen, wo das passiert.
Aber für diesen Fall wollen wir ganz klar Symmetrie haben,
was brauchen wir noch für Eigenschaften? Ja,
Transitivität auf jeden Fall, wenn wir von Düsseldorf nach Hannover kommen,
dann wollen wir und von Hannover nach Hamburg,
dann wollen wir daraus genau Schlussfolgern,
dass wir von Düsseldorf auch nach Hamburg kommen.
Was brauchen wir noch
Das ist emittiert, weil wir schon nach Hause kommen.
Ja, zumindest stört sie uns nicht sehr, ja.
Das ist die Frage jetzt vom Nutzerinterface. Wollen wir
das haben oder nicht? Aber wir wissen auch,
wir können es leicht unterdrücken, wenn es denn stört.
Das ist kein Problem. Ja, Eindeutigkeit wollen wir
die haben
Also denken Sie an Ihr Navigationssystem. Das wird ja immer
vollständig instanziiert aufgerufen. Das ist ja die Randbedingung dabei.
Dann ist Nicht-Eindeutigkeit nur durch unterschiedliche Zwischenstationen gekennzeichnet.
Also gibt es alternative Wege von A nach B?
Das wäre die Frage der Eindeutigkeit an dieser Stelle.
Will man alternative Wege haben?
Ja oder nein
Genau, das ist die typische Funktionalität. Ich kopple das
Ganze mit dem Traffic-Message-System. Ich kriege die Baustellen-Nachrichten oder Unfallmeldungen
und dann Alternativvorschläge, wo ich auch noch langfahren kann.
Also solche Funktionalität will man durchaus haben. Klassischerweise kann man
auch wählen zwischen unterschiedlichen Optimierungsvarianten, möchte ich eine schöne Strecke,
eine schnelle Strecke, eine kurze Strecke haben.
Das kriege ich nicht, wenn ich das Ganze eindeutig mache.
Dann hört er nach der ersten Verbindung auf und wenn
die erste Verbindung von Hamburg nach Bremen über München geht,
dann habe ich ein Problem gehabt
Ich möchte die Alternativen sehen und ich möchte eine
nach einem bestimmten Kriterium optimale Variante davon auswählen.
Und dafür ist es Voraussetzung, dass das Ganze nicht funktional gemacht wird.
Wenn ich nach dem ersten Ergebnis aufhöre und sage,
haha, ich habe es, dann kann ich sicher sein,
dass das nicht das ist, was ich haben.
Also das gewünschte Ergebnis ist. Okay, soviel für heute
mit der Anwendung machen wir dann nächsten Freitag weiter.
Ich wünsche Ihnen ein schönes Wochenende