Wir hatten letzte Woche ja erstmalig mit der Frage der Rekursion
uns auseinandergesetzt und weil Rekursion ab jetzt das zentrale Thema sein wird,
mit dem ich sie terrorisieren werde,
leite ich ab sofort die Vorlesung immer mit einem Cartoon zum Thema ein.
Sie können sich das als kleine Übungsaufgabe vorstellen. Machen Sie sich mal Gedanken,
wo ist da die Rekursion, welcher Typ von Rekursion ist es,
wir werden auch in der Programmierung verschiedene Rekursionstypen kennenlernen.
Und das ist dann, da können Sie mal gucken,
ob Sie das in diesen Cartoons wiederfinden.
Ich habe den hier ausdrücklich für den Start ausgewählt,
weil er eine gewisse Ähnlichkeit mit unserem Symmetrieproblem hat,
was wir ja jetzt kommt hier wieder dieses nette Spiel.
Oh, das war einfach diesmal.
Mit unserem Symmetrieproblem
relativ große Ähnlichkeit hat
Wollen ja eine Relation redundanzfrei abspeichern, trotzdem aber sicherstellen, dass sie symmetrisch ist. Also wir haben,
als ein Beispiel der Zwillingsrelation uns angeguckt,
wir wollen in der Datenbank tatsächlich nur Fakten in der Einrichtung abspeichern,
wollen aber über ein Interface nach draußen tatsächlich in der Lage sein,
das Ganze symmetrisch zu betrachten und das Pattern,
was wir da anwenden können,
das haben wir uns das letzte Mal angeguckt,
ist also,
dass wir außen drumherum einen Rapper um die Datenbank schreiben und dieser Rapper sorgt dafür,
dass die Datenbankfakten sowohl in der Einrichtung als auch in der
anderen Richtung aktiviert werden und auf die Art und Weise
können wir
die Symmetrie sicherstellen
Wir hatten uns auch angeguckt, was würde passieren würde,
wenn wir das nicht machen mit diesem Rapper-Predicard,
sondern wenn wir direkt eine Datenbank haben,
wo wir auf der Datenbankebene die Symmetrie herstellen und wir hatten dann erstmalig gesehen,
dass unsere schöne, heile Welt der referenziellen Transparenz,
beziehungsweise der Deklarativität auseinanderbricht,
weil die Denotationelle und die operationale Samantik plötzlich nicht mehr identisch sind,
wenn ich also im schlimmsten Fall einen Aufruf mit einem unbekannten,
mit einer unbekannten Person mache, dann terminiert das Ganze nicht mehr,
weil das System eben jede obere Schranke versucht,
immer durch Austausch der Argumente eine Lösung für das Problem zu finden.
Aber es gibt keine Lösung für das Problem, weil es
gar keine Fakten über Paul und Karl in der Datenbank gibt.
Und deshalb sucht er unendlich lange nach einer Möglichkeit,
dieses Prädikat wahrzumachen und kommt nicht zum Ende.
Und der Verdacht war eben, es funktionierte mit dem Rapper
Predict Card ja ganz gut hier.
Und hier haben wir keine direkte Regulsion.
Wir führen niemals das Prädikat auf sich selbst zurück.
In dem anderen Fall tun wir das, was ein sehr
mächtiges Programmierkonstrukt ist.
Moment,
wo war die Definition
Ist die Definition, hier führen wir Zwilling von direkt auf
das Prädikat Zwilling von zurück, nur mit umgedrehten,
mit ausgetauschten Argumenten und offensichtlich ist diese direkte Rekursion dieser direkte Rekursionsaufruf hier das Problem für die Terminierung.
Wir brauchen tatsächlich eine,
die Möglichkeit, ein Prädikat durch sich selbst zu definieren,
in dem Augenblick, wo wir transitive Variationen betrachten,
da kommt nämlich der Trick mit dem Rapperprälikat nicht mehr so ganz zum Ziel.
Und transitive Relation haben wir eben immer in solchen Hierarchien,
das kann ein Dateisystem sein, das kann die biologische Systematik sein,
das können Wir strukturieren unsere Welt sehr,
sehr oft mit solchen Hierarchien.
Und wenn wir jetzt nicht nur lokale Teil von Beziehungen betrachten wollen,
sondern globale Teil von Beziehungen über mehrere Zwischenetagen,
müssen wir die transitive Hülle dieser Relation herstellen.
Und das heißt, wir müssen das Prädikat durch sich selbst definieren.
Es geht nicht mehr anders. Wir werden hier auch den
Trick mit dem Rapper-Prädikat an, nur dass wir jetzt das
nur partiell machen.
Also wir bilden einerseits die Datenbank
direkt auf unser Zielprädikat ab
Und wir machen andererseits den rekursiven Schritt, über den die transitive Hülle hergestellt wird,
indem wir erst mal mit einem direkten Zugriff auf die Datenbank wenigstens eine der beiden Variablen instanziieren.
Und dann können wir mit dieser instanziierten Variablen in die Rekursion einsteigen.
Und dadurch stellen wir sicher, dass das Ganze nicht unbeschränkt erfolgt,
sondern dass wir tatsächlich erst mal Werte,
zumindest der einen Variablen, nämlich genau dem Y hier haben.
Und durch diese Wertebindung für das Y die Suche in der Datenbank eingeschränkt wird.
Und wir können tatsächlich nachweisen, dass durch dieses Pattern,
dass wir nun nicht direkt die Rekursion verhindern können,
die passiert hier trotzdem noch, aber dass wir,
bevor wir in die Rekursion einsteigen,
erst mal so viel Informationen wie irgend möglich aus der Datenbank hineinunifizieren.
Dadurch sind wir in der Lage,
in so einer Hierarchie tatsächlich auch die Terminierung zu garantieren.
Wir können das durch die Tests nachweisen und das hatten wir
uns das letzte Mal schon angeguckt. Also wenn wir vollständig uninstanziert aufrufen,
dann kriegen wir tatsächlich eine vollständige Auflistung aller Paare von Beziehungen,
auch über die transitive Hülle.
Also wir sehen dann nicht nur, dass die Fellgeteilt des Rades ist,
sondern auch, dass die Felge Teil des Autos ist.
Oder eben die Zündkerze ist Teil des Autos, weil sie
Teil des Motors ist und der Motorteil des Autos ist.
Und wir sehen insbesondere auch,
das war ja vorher bei der Symmetrie der kritische Fall,
wenn wir hier ein unbekanntes Teil abfragen in der Datenbank ist ja überbremsen nichts gesagt,
auch dann terminiert das Ganze.
Er versucht also nicht unendlich lange nach dem Scheitern des Aufrufes
zu suchen, sondern er ist tatsächlich in der Lage,
uns ein negatives Ergebnis zu produzieren.
Das klappte ja ohne dieses Rapper-Palikat bei der Symmetrie eben
gerade nicht
Gut, nächster Typ von Relation,
den wir gerne hätten, ist eine funktionale Abhängigkeit,
was in der Datenbank wählt gern als 1 zu M oder M zu 1-Relation dargestellt wird,
dass eine solche Relation ist immer gegeben,
wenn wir denn eines der Argumente kennen,
dann können wir eindeutig auf das andere Argument schließen.
Das ist typischerweise das, was man für die Schlüsselbeziehung in
den relationalen Datenbanken nimmt.
Man möchte also vom Schlüssel eindeutig auf die Werte der
anderen Attribute
schließen können
Das sind funktionale Abhängigkeiten. Das kann ich jetzt für eine
zweistellige Relation in beiden Richtungen machen, vom ersten Argument zum
zweiten oder vom zweiten Argument zum ersten Argument haben.
Das Ganze ist also jetzt eine funktionale Spezifikation auf einer Argumentposition.
Ich könnte also auch sagen, das eine Argumentbelegung,
eine Funktion der anderen Argumentbelegung ist.
Das sind also Spezialfälle einer M-zu-N-Relation. Und die Frage ist,
kann ich sicherstellen, dass eine solche Relation im unserem relationalen
Modell tatsächlich eindeutig ist, in einem der Argumente.
Und wir beispielen Spiele für so etwas haben wir massenweise,
also die Mutter von Beziehung ist natürlich eine funktionale Relation.
Die Mutter ist immer eindeutig gegeben, die Tochter, in einer heilen Welt,
ist all die Ausnahmen, von denen wir erstmal absehen müssen,
von Leihmüttern bis zu ausgetauschten Müttern. Die Altersangabe,
auch hier wieder in einer heilen und idealen Welt,
ist eindeutig gegeben für eine gegebene Person,
kann ich sagen, wie alt sie ist,
oder umgekehrten Richtung hatten wir eben diese Teil von Beziehung,
Das ist natürlich immer die Frage,
warum man das definiert oder eben auch so eine Beziehung zwischen Stadt und Land,
dass eine Stadt immer eindeutig einem Land zugewiesen werden kann.
Auch da gibt es zahlreiche Ausnahmefälle, insbesondere wenn geografische Grenzen sich verschieben.
Die kennen wir alle von Jerusalem bis zu dem ehemaligen Berlin,
wo plötzlich Städte auch geteilt sind. Ja, im Prinzip
auch hier in einer heilen Welt ist das eine eindeutige Zuordnung.
Was heißt das jetzt auf der Programmierebene im Allgemeinen,
ist eine Prädikatsdefinition immer vom Typ M zu N.
Das heißt, wir können Beliebig Paarungen aus den beiden Domänen vornehmen.
Und das hat zwei Sachen zur Folge. Also hat insbesondere zur Folge,
auch wenn ich weiß, dass die Relation eindeutig ist,
sucht das System stur immer nach Alternativen Funktionswerten.
Und die kann es nicht geben,
wenn ich einmal eine Mutter gefunden habe,
brauche ich nicht nach der zweiten Mutter zu suchen.
Ich werde sie nicht finden, da ist keine.
Wenn die Datenbank natürlich konsistent ist. Das kann unnötigen Suchaufwand
hervorrufen. Gut, das könnte man noch verschmerzen.
Das ist nicht so schlimm. Aber das Schlimmere ist,
dass es teilweise auch Terminierungsprobleme bei rekursiven Definitionen hervorrufen kann.
Beispiele dafür werden wir
noch genügend sehen
Und der mögliche Ausweg wäre, dass wir die Suche einfach nach dem ersten Resultat
auch abbrechen könnten. Und dafür gibt es eine Möglichkeit,
das ist im Prinzip ein Mechanismus zur Steuerung des Ablaufes der Suche,
den wir uns erst später angucken werden.
Das ist der sogenannte Cut, der irgendwann mal dazu dient,
dass wir sagen, okay, wir brechen die Suche jetzt ab.
Was der genau macht, das müssen wir uns später angucken,
das Once ist im Prinzip nur ein Mittel,
um diesen Cut zu verstecken,
weil der Cut eine relativ Nun ja, sagen wir,
fehleranfällige Konstruktion ist, wenn man nicht genau weiß,
was man dort tut, dann sollte man ihn lieber nicht verwenden.
Und das Ones sorgt dafür, dass ein Aufruf nur Erfolg,
das Erstergebnis liefert und kein Backtracking mehr für diesen Aufruf passieren kann.
Also wir machen mal ganz einfach eine Datenbank,
die A zu B zuordnet und diese Paare zulässt.
Das heißt also, in dieser Richtung ist die Zuordnung hier
mehrdeutig und in dieser Richtung ist die Zuordnung hier mehrdeutig.
Wir können also irgend
so etwas machen
Die Mutter von A ist, achso, ne, die Mutter ist natürlich schlecht,
dann hätte ich mal ein bisschen andere Buchstaben hier nehmen sollen,
sonst ruft das vielleicht Verwirrung hervor.
Das ist dann vielleicht eine beste Variante,
also die Mutter von ASC, die, achso,
naja, das mit der Mutter natürlich auch schlecht,
weil das natürlich nur in einer Richtung,
ist ja nicht hierarchische Beziehung, hier haben wir keine hierarchische Beziehung,
gut, dann Na, wir lassen es.
Es ist egal. Wir nehmen uns eine abstrakte Mutter her.
Das ist, gut, für B ist das auch zäh,
für B kann es aber auch D sein.
Ups. Das sind die drei Beziehungen, die wir haben.
Also wir haben Mehrdeutigkeiten in beiden Richtungen.
Und wenn wir jetzt eben normalerweise abfragen würden,
von B, dann würden wir,
wenn alles glatt geht,
eben zwei Lösungen bekommen C und
Und wenn wir das Ganze jetzt einkleiden in dieses Ones,
ein einstelliges Prädikat, sagt das, rufe mir dieses,
und das ist ein Prädikat höhere Ordnung,
genauso wie das Find Old, was wir schon behandelt haben,
dann ruft das dieses Ziel auf,
bricht aber die Verarbeitung nach dem ersten Ergebnis ab.
Dann kommt kein weiteres mehr. Und das können wir in der anderen Richtung genauso machen,
wenn wir also so etwas hätten wie x,
was habe ich jetzt, das erste Argument, C,
dann sollte ich hier wieder zwei Ergebnisse bekommen.
Und wenn ich das einkleide in das Ones,
Dann bleibt es bei einem Ergebnis. Was wir hier sehen,
ist, dass das gewünschte Ergebnis gar nicht erzielt wird.
Wir machen das Prädikat nämlich nicht funktional, sondern wir machen es eindeutig.
Wir haben jetzt eine eindeutige Zuordnung zwischen A und C
und zwischen B und, also zwischen C und,
ne, zwischen A und C und zwischen B und C.
Und das sind einfach nur die Eintragungen in der Datenbank,
die er zuerst findet. Also wenn ich nach dem A
Nachdem B-Frage dann ist eben die Zuordnung zu dem C,
das erste, was in der Datenbank drinsteht.
Und wenn ich nach dem C frage, hieß es,
ah, das erste, was in der Datenbank drinsteht.
Und das kriege ich als Ergebnis zurückgeliefert.
Die anderen sind nicht mehr sichtbar. Das,
was ich eigentlich haben möchte, ist, ja,
und jetzt machen wir das vielleicht doch mal wieder in
dem Beispiel mit Mutter von Beziehung.
Wir müssen also retrakten.
Wir gucken ja vorsichtshalber nochmal nach, was war da in der Datenbank
drin stehen haben. Genau, das sind diese beiden Fakten,
also C ist die Mutter von A und B, so rum können wir es vielleicht interpretieren.
Wenn wir jetzt also fragen, wer ist die Mutter,
wir sehen es jetzt nach außen nicht,
wenn wir also fragen, wer ist die Mutter von A,
dann kriegen wir natürlich ein Ergebnis,
mehr steht in der Datenbank nicht drin und das System weiß auch sofort eine weitere Lösung,
gibt es nicht dafür, das ist über das Hash-Coding bereits ermittelt.
Wenn wir jetzt aber umgekehrt fragen,
wer ist ein Kind der Mutter C,
Dann kriegen wir eine Lösung und die zweite Lösung heraus.
Jetzt kommt natürlich der entscheidende Teil. Wir packen das Ganze in
das Ones rein. Dann ändert sich hier erstmal überhaupt nichts
an dem Verhalten. Da gibt es eben nur eine Mutter.
Das, was wir nicht sehen, ist der Aufwand,
der getrieben wird, um zu verhindern, nach Alternativen zu suchen.
Und im umgekehrten Fall, wenn wir das in das Ones hineinstecken,
dann sehen wir,
dass uns Lösungen verloren gehen
Denn er müsste jetzt eigentlich beide Kinder aufzählen,
aber er zählt nur eins davon auf. Das heißt,
das Ganze wird nicht eindeutig, dass die Mutter eindeutig ist,
sondern auch die Kinder werden eindeutig.
Mehr als das erste Kind kriegen wir jetzt nicht geliefert. Ja,
wir wollten eigentlich erreichen, dass das Ganze in einem Argument eindeutig wird,
was wir erreicht haben, durch das Once ist es,
dass es in zwei Argumenten eindeutig wird.
Dass in einem Argument eindeutig zu machen, ist tatsächlich mit größerem Aufwand verbunden.
Also, das ist ein bisschen ein frustrierendes Ergebnis.
Wir haben nicht genau das erreicht, was wir erreichen wollen,
sondern wir kriegen eine Verschärfung des Prädikats zu einer eindeutigen Relation.
Für jedes erste Argument ist das zweite eindeutig gegeben und für
jedes zweite Argument ist das erste
eindeutig gegeben
Letzte Eigenschaft, die wir uns hier angucken,
ist die Reflexivität, reflexibit, reflexive,
eine Relaf Reflex, ob es eine reflexive Relation heißt ja,
dass jedes Element zu sich selbst in dieser Relation steht.
Ein Beispiel dafür ist, die sind Äquivalenzrelation,
die kennen wir von der Gleichheit in der Mathematik und das
überträgt sich hier auf die Unifizierbarkeit in der Logikprogrammierung,
wenn X mit Y unifizierbar ist, dann ist auch Y mit X unifizierbar.
Und Y ist mit sich oder X ist mit sich selbst
unifizierbar. Das ist ja die Konsequenz daraus
Wann immer ich eine Relationsdefinition habe, die sich irgendwo auf eine solche Äquivalenzrelation bezieht,
also unsere Unifikation, die die Basisoperation des gesamten Logikprogrammierungssystems ist,
dann erbt es von der Unifikation die Eigenschaft der Reflexivität.
Also hier ist es anders als bei der Symmetrie und bei der Transitivität,
wo wir etwas tun mussten, um die Eigenschaft herzustellen,
ist es hier eben so, Reflexivität ist standardmäßig gegeben.
Ich muss sie eher beseitigen, wenn sie stört. Und ein Beispiel,
wo das der Fall ist, ist etwa, ich will definieren,
was eine Schwester von Beziehung ist und ich definiere es über die Kind von Relation,
also ich habe ein Kind X und ein Kind Y,
die beide Kind von Z sind und X muss noch weiblich sein,
damit es eben eine Schwester ist und kein Bruder.
Und das Ganze Die ganze Verarbeitung läuft jetzt auf die Unifikation hinaus,
weil ich eben hier dieser vajablen Chor-Referenz herstellen muss.
Und von dieser vajablen Chor-Referenz erbt das Ganze, dass es reflexiv ist,
was man mit einer einfachen Datenbasis sofort testen kann.
Ich habe hier also verschiedene Eltern-Kind-Pare. Ich habe ein paar Informationen über die Geschlechtszuordnung. Und wenn ich jetzt aufrufe,
wer ist die Schwester von Susi,
dann gehe ich eben mit der vajablen Bindung Susi für Y hier hinein und suche die Mutter von Susi,
äh Quatsch, Die Schwester von Susi, ich,
genau, das ist das Y,
ich suche die Mutter von Susi, das ist Anne.
Und ich brauche hier ein weiteres Kind von Anne.
Und das erste, was ich in der Datenbank finde,
ist Susi. Und die weibliche Redaktion stimmt auch,
sodass ich als erstes Ergebnis bekomme, Susi ist die Schwester von sich selbst.
Was man typischerweise auf diese Antwort nicht sehen will,
man möchte dort eine nicht reflexive Relation haben,
dieses Ergebnis soll nicht auftauchen.
Also wir gucken uns auch mal in der richtigen Reihenfolge an
Ich gehe hier in die Datenbank-Kind von hinein. Der erste Fakt,
den ich finde, ist Susi, mit Anne als Mutter.
Ich rufe mit der Vayablen genug an für Z,
die das Teilzählt nochmal auf und finde natürlich als allererstes wieder den gleichen Datenbank-Eintrag.
Und deshalb kommt die Reflektierung, die Reflexivität zustande.
Was kann ich tun, um es zu verhindern?
Ja, man kann sagen, dass X nichts ist.
Genau, ich muss, das sind Atome,
da ist es trivial, ich muss nur fordern,
dass X und Y nicht gleich sind,
also im allgemeinen Fall nicht unifizieren miteinander.
Also dieses Backslash gleich zwischen X und Y als zusätzliches Teilziel
würde es tun. Wo muss ich das reinsetzen,
an welcher Stelle,
in die Klausel
Mhm. Im Prinzip ist es, es gibt nur ein Erfüllenkommateil,
also man kann es natürlich grenzen, über die Kommaten zu arbeiten,
wenn man sich ganz vorne ansieht, so ist es auch da.
Das gehört ganz oben, meine Schwester. Ja,
ja, natürlich, hier ist die Frage, hierhin,
dahin, dorthin oder dahin?
Die vier Möglichkeiten habe ich. Also die letzte ist sicherlich
unstrittig, was ist schlecht daran,
wenn ich es da hinten hinsetze
Die Möglichkeit werden wir es ganz am Ende auswirken.
Ja, das ist ineffizient. Wenn wir die ausprobiert probitiziert sind,
stelle ich fest, das wollte ich ja gar nicht.
Ja, das ist also ineffizient, das ist nicht so schön.
Ich könnte es zum Beispiel hier davor setzen, wäre das
gut oder gibt es auch Gründe, die dagegen sprechen?
Oder machen wir es andersrum? Vorhin hieß es,
es ist egal, wir fangen hier vorne an,
was würde passieren,
wenn ich es hier vorn reinsetze
Das darf man vielleicht gar nicht verarbeiten, weil er ist nicht selber.
Was macht er denn, wenn er das nicht kennt?
Also wir, das Schöne an so einem System,
an so einem interaktiven System ist ja immer,
wir haben es sofort in der Hand,
wenn ich jetzt noch meinen Fenster,
ich empfinde da ist es.
Also was passiert, wenn ich sowas fordere,
wie X soll nicht mit Y unifizieren?
Weißt du,
wann sie meinen
Der hinunterkommt, manches war aller Ergebnisse, wo jetzt schon weiter zu machen.
Das wäre schön, wenn man das bekommt.
Das ist falsch, denn sie können gleich sein.
Es ist im allgemeinen Fall nicht sicherzustellen, dass sie ungleich sind.
Und das würde auch passieren,
wenn wir das in diesem größeren Prädikat an den Anfang stellen,
dann würde das Ganze den Gimmer scheitern.
Natürlich kann ich Werte von X und Y finden,
die das Ding gleich machen. Die Forderung ist ja,
ich muss verhindern, dass es Werte gibt,
die miteinander unifizieren
Das gleiche Zeichen alleine würde nur heißen,
X und Y muss identifizieren, ich muss Werte finden,
die das Ganze gleich machen. Und die Negation heißt,
ich brauche etwas, dass das nicht gleich macht.
Oder ich muss sicherstellen, dass für alle möglichen Wertebindungen,
die ich finde, X undgleich Y sein muss.
Und das gilt einfach nicht allgemein. Ja, also,
es ist nicht beliebig, wo ich das Ding hinsetze,
wo kann ich es denn nun hinsetzen?
Ach so, nee, hier muss ich hin.
An welcher Stelle gehört es denn nun? Ja,
jetzt muss für mich das eine der beiden Adrianen,
Das liegt mir nicht aus für das Auge.
Wenn es der Halt weiß oder entweder vor oder zwei
Licht oder nachts, weiß ich nicht.
Okay. Ja. Ja. Ja. Ja. Ja
. Das liegt mir auch. Das wird für die beiden
jetzt bauen. Was würde rauskommen, wenn wir,
das wäre ja der Fall, eins von beiden ist instanziert,
also wir können hier alle hinschreiben, damit es realitätsnäher aussieht.
Was passiert in dem Falle? Das ist nicht viel anders
als davor. Wir müssen sicherstellen, dass X
den Wert alle
nicht annehmen darf
Natürlich darf es das, es darf jeden beliebigen Wert annehmen,
es ist uninstanziert, ich kann alles hineinunifizieren dort.
Das heißt, ich kann nicht sicherstellen, dass X ungleich Anne ist.
Also das funktioniert auch nicht, ja. Natürlich X-Medien,
indem man sagt, zum Beispiel X ist technisch Y.
Damit würde man das sicherstellen. Nee, negieren kann ich
nur Prädikate. Ich kann nicht Atome prädet, negieren oder
auch nicht Variablen negieren. Mir bleibt nur die Möglichkeit,
so eine Vergleichsoperation zu nehmen, die zwei Bayablenbindungen miteinander vergleicht.
Also negiert von einer Variablen, das geht nicht,
das gibt es nicht
Gut, also das funktioniert auch nicht. Damit ist klar,
was wir zu tun haben. Wir müssen es dorthin setzen,
wo beide Vayablen instanziert sind und das ist hier der Fall.
Wir haben ein Kind gefunden, wir haben ein zweites Kind
gefunden, was möglicherweise den gleichen Namen trägt.
Und hier dahinter können wir sicherstellen, dass der Wert von X
nicht gleich den Wert von Y sein darf.
Und dann schließen wir aus, dass sowohl für X als
auch für Y Susi als Wert genommen wird.
Dann bricht die Verarbeitung hier ab und geht ins Backtracking.
Da muss ich eben hier ein alternatives Kind von Anne
suchen und das ist Jane
Da kriege ich dann das zweite Ergebnis und weitere Ergebnisse
werden dann eben nicht produziert. Gut,
dann gucken wir uns jetzt mal ein komplexeres Beispiel an,
wo wir die Dinge versuchen zusammenzubringen.
Und das macht sich immer ganz gut mit einem Wegsuchproblem,
also im Prinzip einem Navigationssystem, was wir schreiben wollen.
Wir haben also jetzt sehr vergröbert Orte auf der Landkarte.
Wie ich habe hier tatsächlich größere Ortschaften genommen im Navigationssystem,
werden dies tatsächlich Straßenkreuzung sein
Ich muss sagen, ob ich von einer Straßenkreuzung zur
nächsten Straßenkreuzung komme. Denn das ist die Information,
die ich brauche, um Navigationen zu betreiben.
Egal, ob für Fußgänger, für Fahrräder, für Autos.
Und ich möchte, das war ja eine der Bedingungen,
keine Redundanz in der Datenbank haben. Das heißt also,
die Verbindung sollen immer nur mit minimalem Aufwand spezifiziert werden.
Dafür habe ich hier dieses Con-Prädikat eingeführt für Connection.
Es gibt eine Verbindung von Hamburg nach Berlin.
Das ist hier eingezeichnet mit diesem Pfeil. Und es gibt
eine Verbindung von Hamburg nach Hannover, das ist dieser Pfeil hier.
Und eine Verbindung von Hamburg nach Bremen, das ist dieser Pfeil.
Und ich möchte jetzt also eine Ja, eine Prädikat haben,
was mir auf der Basis dieser Fakten berechnet,
ob ich von einem beliebigen Ort zu einem beliebigen Ort kommen kann.
Und natürlich haben wir da als allererstes die Frage,
wir haben eine gerichtete Beziehung hier, die von A nach B geht.
Typischerweise, wenn ich von A nach B fahren kann,
kann ich auch von B nach A fahren.
Es sei denn, es ist eine Einbahnstraße.
Aber Einbahnstraßen auf dieser Granularitätsebene gibt es zum Glück nicht.
Das wäre ein bisschen kurios,
wenn ich zwangsweise von Hannover über Berlin oder was weiß ich wie immer,
es gibt ja nicht mal,
das geht gar nicht
Ja, doch, wenn ich also, wenn mir die
Verbindung über Bremen nach Düsseldorf nur in einer Richtung befahrbar wäre,
das wäre nicht so gut. Also standardmäßig ist das Ganze unsymmetrisch,
wenn ich mit dieser Datenbank teste, komme ich von Hamburg nach Bremen,
dann ist okay, das geht, wenn ich teste,
komme ich von Bremen nach Hamburg, dann geht das nicht,
weil ein entsprechender Fakt nicht in der Datenbank steht.
Also nehmen wir unser Pattern,
wir bauen einen Rapper außen drumherum und der soll jetzt heißen,
Konnektivität symmetrisch
Im Gegensatz zu diesem Basiskonnektivitätsprädikat. Und das rufe ich jetzt eben des Datenbank,
die Datenbankfakten einmal mit der Argumentreihenfolge XY auf und einmal mit
der Reihenfolge YX auf und kriege dadurch tatsächlich diese Metrie ins Haus.
Ich komme jetzt also von Hamburg nach Hannover und ich komme
genauso gut von Hannover nach Hamburg, obwohl das umgekehrte,
der umgekehrte Fakt gar nicht in der Datenbank drinsteht.
Nächste Eigenschaft ist Transitivität,
wenn ich von A nach B komme und von B nach C komme,
dann will ich auch von A nach C kommen und wir
hatten unser Pattern für Transitivität und was eben dafür sorgt,
dass das umgebende Prädikat einfach auf die Datenmark abgebildet wird.
Also wenn ich in der Datenbank die Informationen habe,
dass ich von X nach Y komme,
dann übernehme ich das einfach auf die Ebene des übergeordneten Prädikats.
Ansonsten übernehme ich den Fakt aus der Datenbank und sehe zu,
ob ich von dem Ort, der dort angegeben ist,
tatsächlich der Zielort erreichen kann.
Und natürlich habe ich die ganzen Fakten wieder dazu,
die ich brauche, um die transitive Hölle bilden zu können.
Und ich kann jetzt also fragen, komme ich von Hamburg
nach Düsseldorf und ich kriege dreimal True und wenn ich noch weitere alternative Frage False.
Und wenn man sich das in Bezug auf die Datenbank anguckt,
dann ist es eben tatsächlich so,
ich kann von Hamburg nach Hannover über Was war das?
Hamburg Hannover? Nee, was hatte ich gefragt?
Jetzt habe ich es vergessen. Hamburg nach Düsseldorf,
von Hamburg nach Düsseldorf komme, dann kann ich eben einerseits über Bremen fahren.
Ich kann andererseits über Hannover fahren und ich kann zum dritten
über Berlin, Hannover nach Düsseldorf kommen.
Das sind die drei Möglichkeiten in dieser Datenbank von Hamburg
nach Hannover zu reisen. Sorry, jetzt wieder falsch,
von Hamburg nach Düsseldorf zu reisen, möglicherweise über Hannover.
Und ich kann genauso gut fragen, wo komme ich von Ham,
wo komme ich von Hamburg aus überall hin?
Und dann kriege ich eben tatsächlich mehr Ups, die mehrfach Ergebnisse dazu heraus.
Also erstmal die Fakten, die in der Datenbank stehen.
Von Hamburg komme ich nach Berlin, Hannover und Bremen.
Und wenn ich weiter nach Alternativen frage,
dann kriege ich auch weitere Antworten, die dann eben lauten,
ja, ich kann nochmal nach Hannover kommen und das ist jetzt nicht der direkte Weg,
sondern über den Umweg Berlin.
Und ich kann nach Düsseldorf reisen, entweder über Bremen,
entweder über Hannover oder über Berlin, Hannover nach Düsseldorf.
Weitere Ergebnisse gibt es nicht. Das scheint also ganz gut zu funktionieren.
Wir könnten jetzt auch mit völlig uninstanziierten Datentesten mit nicht existierenden Orten beispielsweise,
um das System zu zwingen, den gesamten Suchraum abzuarbeiten.
Wir gucken uns aber einfach mal nur an,
wir koppeln jetzt die Transitivitätseigenschaft mit der Symmetrieeigenschaft.
Also wir haben jetzt sowohl die Prädikate, um die Argumente zu vertauschen,
als auch die Prädikate, um die transitive Hülle zu bilden.
Ja, und wenn wir das jetzt eingeben,
kommen wir von Hannover nach Bremen,
dann kriegen wir eben dreimal True,
weil es drei Möglichkeiten gibt,
da hinzukommen und danach schmiert unser System ab
Und wenn wir fragen, kommen wir jetzt mit,
so mit Unterbetrachtung der Symmetrie und der Transitivität von Düsseldorf nach Berlin,
dann schmiert das System sofort ab.
Was läuft schief? Was haben wir falsch gemacht? Ja,
jetzt ist das wieder so, wenn wir kommen sollten.
Der Pfarrer jahres, da gibt es Z, da drückt mit
der Tatmaß. Komifizieren und der Test, das wird,
kann er, aber dann wieder die umgekehrte Politikation machen,
Weil, dass er nach Heiligen runtergefallen ist.
Und da wird wieder uns. Hat also die Phase,
du warst bei zu zwei Euro.
Das ist der untere Fall hier, ja.
Von Düsseldorf führt in der Datenbank selber keinen Weg weg.
Alle Wege führen nach Düsseldorf hin, es sind nur zwei Wege.
Das heißt, er hat keinen direkten Fakt.
Er muss eine Regel anwenden und dass sie die erste Regel,
die er findet, ist, ja, dann vertauscht die Argumente und probierst doch andersrum.
Kommst du von Düsseldorf nach Berlin? Nein, das geht genauso wenig,
weil ich entweder über Hamburg oder über Hannover fahren muss.
Anders komme ich da nicht weiter. Das heißt,
das scheitert auch wieder die nächste Möglichkeit, die er wiederfindet,
ist, ja, dann vertauscht doch einfach mal die Argumente.
Vielleicht klappt es dann
Und aus dem Grunde, ja, pendle ich zwischen den Orten
hin und her oder aber fahre irgendwo im Kreise herum.
Denn jetzt kann ich ja plötzlich in dieser Datenbank oder ja,
in dieser Verbindung, kann ich ja sowohl hin und her fahren,
aber eben auch im Kreise fahren.
Das ging vorher ja nicht. Also gegen die Richtung konnte ich ja schlecht fahren.
Ich konnte die transitive Hülle immer nur in der eigenen Richtung herstellen.
Also von Hamburg über Bremen nach Düsseldorf. Aber jetzt kann
ich eben auch gegen die Fahrtrichtung fahren. Das heißt,
ich kann genauso gut eben Zyklen bilden, die vorher nicht gingen.
Wenn sie das sehen, dieser Kreis hier erfordert immer an
irgendeiner Stelle, dass ich gegen die Pfeilrichtung fahre.
Und das Gleiche passiert hier oder auch in dem äußeren Kreis.
Ich muss immer irgendwo gegen die Pfeilrichtung fahren.
Die Pfeilrichtung ist aber aufgrund der Symmetrieprädikate aufgehoben.
Die gibt es nicht mehr. Das heißt,
jetzt kann ich hier beliebig im Kreise fahren,
was ja kein Mangel unseres Programms ist.
Das ist ja kein Bug im Programm,
sondern das ist der Bug in der Welt.
In der Welt kann ich im Kreise herumfahren.
Das ist eben so. Wenn ich nicht Sorge dafür trage,
dass ich das nicht tue und das muss ich tatsächlich mit Aufwand treiben,
was wäre das Mittel, was man verwenden kann,
um zu verhindern,
dass ich ständig im Kreise herumfahre
Im Endeffekt merkt man, das ist der Faden der Ariadne,
der sie ihrem Liebling mitgegeben hat, damit er aus dem
Labyrinth wieder herauskommt, wo er dem Minotaurus töten sollte.
Damit er den Rückweg wiederfindet, hat sie ihm das Wolleknäuel mitgegeben,
damit er sich an dem Wollknäuel wieder zurückorientieren kann. Und auch,
damit er auf dem Hinweg nicht bereits in Zyklen läuft,
denn wann immer er wieder an dem Wollefaden vorbeikommt, weiß er,
er ist im Kreise gelaufen und dann kann er am Wollefaden zurück,
sich aus dem Kreis wieder raus manövrieren
Das müssen wir hier auch treiben. Wir brauchen eine Technik
, die nennt sich Mimuisierung. Wir müssen uns merken,
wo wir schon gewesen sind und verhindern, dass wir dies nochmal tun.
Dann können wir verhindern, dass wir in Zyklen laufen.
Dafür brauchen wir aber Listen, das kriegen wir dann später,
da nehmen wir das gleiche Anwendungsbeispiel unter Weiterns nochmal unter der Vermeidung von Zyklen.
Also wenn wir tatsächlich ein praktikables Auskunftssystem bauen wollen,
ein Navigationssystem, dann ist eine solche deduktive Datenbank die Basis,
die wir brauchen, aber allein reicht sie nicht aus.
Das ist die Botschaft dabei
Gut, dann sind wir hiermit auch durch. Ja,
das Fazit der deduktiven Datenbanken ist, also es ermöglicht uns,
Berechnungen durchzuführen, statt der Aufzählung von Fakten.
Wir haben bis jetzt nur sehr einfache Beispiele gesehen.
Wir werden im weiteren kompliziertere sehen. Wir können dadurch vermeiden,
dass wir Redundanz in die Datenbank bekommen,
die eben wegen der Update Anomalien üblicherweise nicht gut gefunden wird.
Und wegen der Ressourcenverschwendung natürlich auch. Und wir werden in
der Lage sein, Relation über unendlichen Domänen zu definieren.
Wir haben bisher noch keine unendlichen Domänen
Unsere Anzahl der Orte war, ist begrenzt,
die Anzahl der Personen, über die wir reden,
ist begrenzt, weil wir immer alle vollständig auflisten müssen.
Wir brauchen also ein Mittel, um tatsächlich unendliche Domänen zu kreieren.
Das haben wir bisher noch nicht. Das kriegen wir aber
gleich als nächstes. Wir sind in der Lage,
die transitive Hülle einer Relation zu berechnen. Also,
wenn ich von A nach B komme und von B
nach C komme, dann auch von A nach C.
Wir kriegen aber Terminierungsprobleme bei rekursiven Definitionen, wenn wir nicht aufpassen. Wir haben Pattern gesehen,
wie wir Basisterminierungsprobleme bei Symmetrie und Transitivität vermeiden können,
aber wenn die Terminierungsproblem nicht im Programm liegt,
sondern in der Welt, dann sind wir nicht in der Lage,
auf der Basis tatsächlich Terminierung zu garantieren.
Wir kriegen bei persistenter Datenhaltung natürlich Effizienzprobleme, wenn wir mit solchen Terminierungsproblemen zu tun haben,
wissen wir ja nie, wann wir den Prozess abbrechen müssen.
Der läuft und läuft und läuft und kommt nicht zum Ende.
Ein eingeschränktes Modell ist Bestandteil der SQL-Definition des Standards,
aber es ist ein sehr eingeschränktes Modell,
da muss der Entwickler sehr viel Aufwand treiben,
um zu spezifizieren, wie viele, also das Hauptproblem ist,
das Halteproblem auch natürlich in so einer Datenbank und das wird voll in die Verantwortung,
dafür wird voll in die Hand des Entwicklers gelegt.
Er muss einen Mechanismus bereitstellen, der sicherstellt,
dass das Ganze tatsächlich terminiert,
aber es ist in der Hand des Entwicklers und der kann natürlich Fehler machen,
das heißt also,
das ist der Grund,
weshalb deduktive Datenbanken im SQL-Standard eher mit Spitzenfingern angefasst werden
So, jetzt kommen wir also dazu,
dass wir tatsächlich mal anfangen über Domänen zu reden,
die potenziell unendlich viele Werte zulassen.
Und das Mittel dafür ist die zweite Verwendung von Rekursion.
Wir hatten ja schon gesehen, Rekursion ein selbstbezüglicher Prozess,
wo ein Prädikat ein sich selbst noch mal aufrufen kann.
Wir werden jetzt Rekursivität auf der Ebene der Datenstrukturen einführen.
Und das ist der dritte, ja,
der dritte Schritt auf dem Weg von der relationalen Datenbank,
relationale Datenbank plus Implikation, macht deduktive Datenbank.
Und deduktive Datenbank plus reklusive Datenstrukturen
macht Berechnungsuniverselles Modell
Das ist jetzt der zweite Schritt, den wir gehen.
Und danach haben wir ein vollständig ausgebautes Turing-Äquivalentes Berechnungsmodell.
Das ist für praktische Zwecke noch nicht immer unbedingt geeignet,
aber im Prinzip können wir dann damit alles berechnen,
was eine Turing-Maschine berechnen kann.
Und der Weg wird ja über eingebettete Strukturen führen,
dass eine Struktur wieder eine Struktur enthalten kann.
Und das erlaubt es uns über diese Rekussion beliebig,
tief eingebettete Strukturen zu erzeugen und beliebig tief eingebettete Strukturen heißt
natürlich,
ich kann unendlich
viele davon generieren
Eine Anwendung sind zum Beispiel Baumstrukturen in der Programmierwelt,
in der Welt der natürlichen Sprache, die man gerne nimmt,
um die hierarchische Komposition von komplexen Beobachtungen zu strukturieren.
Beispielsweise einen Satz, in dem man dann sagt,
wie der intern zusammen, wie die Elemente intern zusammenhängen.
Das ist das, was wir bisher an Syntax haben.
Das ist ihnen alle schon mal über den Weg gelaufen.
Wir haben eine Datenbank, die besteht aus Klauseln innerhalb der,
wir haben, der Datenbank haben wir Prozeduren oder Prädikatsdefinitionen,
die bestehen ebenfalls aus Klauseln
Eine Klausel ist eben immer ein Fakt, ein Ziel,
eine Regel abgeschlossen mit dem Punkt.
Ein Ziel kann ein elementares und so weiter,
das kennen Sie alles. Jetzt kommen nur ganz,
ganz wenige Sachen noch hinzu, damit wir das vollständig ausbauen.
Und das erste sind eben diese eingebetteten Strukturen.
Eine Struktur besteht jetzt aus dem Funktor der öffnenden Klammer
und dann durch Kommas getrennt, beliebig viele Therme.
Dann kommt die schließende Klammer. Und die Therme,
die hier durch Kommas getrennt sind, die können jetzt,
das hat man bisher schon, konstanten oder Variablen sein,
sie können aber auch Strukturen wieder sein.
An der Stelle bauen wir die Rekursion ein,
eine Struktur darf Therm enthalten und Therme dürfen wieder Strukturen sein.
Das heißt, Strukturen enthalten sich selbst oder können
andere Strukturen als Bestandteil enthalten
Sie können insbesondere auch wieder Strukturen mit dem gleichen Funktor enthalten,
das ist nicht ausgeschlossen. Das kann sein, muss aber nicht sein.
Die zweite Erweiterung, die wir uns noch angucken werden,
ist rein so in taktischer Natur. Das dient nur zu dem Zweck,
Strukturen bequemer schreiben zu können, speziell ein- und zweistellige Strukturen.
Das ist nämlich die Einführung der Syntax, der Infix-Operatoren,
die wir aus der Mathematik für eine Addition von der Multiplikation
und so weiter kennen, auch in die Welt der Logikprogrammierung.
Und die dritte Erweiterung, die wir auseinbauen,
ist die Listen, sind Listen als spezielle Struktur oder spezielle Therme,
weil wir sehr, sehr oft Listen als Container brauchen,
um überhaupt etwas Praktikables programmieren zu können, aber gleich eine Warnung,
die Listen, die wir hier behandeln,
sind linear verkettete Listen.
Das sind nicht Javalisten, Vorsicht. Das sind rekursive Datenstrukturen.
Da gibt es einen Gewalt, den Terminologie-Clash. So,
also Rick, Strukturen können rekursiv eingebettet sein. Das heißt,
in der Welt der Datenbanker Prolog-Datenbanken müssen nicht in erster Normalform sein,
sondern können sogenannte Non-First-Normalformen, Databases sein.
Deshalb NF zum Quadrat
Also, ich kann so etwas machen, dass ich solche Strukturen aufbaue,
ein Angebot besteht eben aus einem Produkt mit einer bestimmten Produktbezeichnung,
einer Kategorie, dass dies ein Radio ist,
dass es einen bestimmten Status hat.
Es ist nämlich am Lager und einen bestimmten Preis in einer Währung,
die sind 100 Euro. Das ist eine Möglichkeit,
wie ich eingebettete Strukturen verwenden kann, um komplexe Objekte zu
beschreiben mit ihren verschiedenen Eigenschaften, die sie haben.
Ein wichtiger Spezialfall, ich hatte schon angedeutet,
ist aber, wenn der Funktor immer wieder der Funktor,
der gleiche Funktor ist und dann haben wir eine rekrusive Selbsteinbettung
einer Struktur
Wir werden das benutzen, um natürliche Zahlen zu modellieren.
Da kommen wir gleich drauf. Es wird aber zum Beispiel
in Systemen wie Scheme oder auch Prolog benutzt, um Listen darzustellen.
Dieses DP habe ich extra DP genannt, weil es für Dotted Pere steht.
Das ist ihnen vielleicht in der funktionalen Programmierung schon über den Weg gelaufen.
Listen werden immer als verkettete, zweistellige Datenstrukturen aufgebaut.
Ich habe einen großen Speicher,
der solche Zellen enthält und ich kann jetzt sagen,
Und das erste Element dieser Liste ist A und sie wird
fortgesetzt durch eine Restliste und das zweite Element sei beispielsweise B und die Liste dort fortgesetzt durch ein weiteres Element,
das kann dann C sein und ich muss irgendwann mal einen vereinbarten Listenabschluss Marker setzen und das ist üblicherweise das Nil.
So sehen Listen intern aus und das ist hier übertragen in die Strukturdarstellung.
Ich habe als Funktor immer ein Dotted Pear, das ist ein Dotted Pear.
Das erste Argument ist das Listenelement und das zweite Element
des Dotted Pears ist die Liste,
die danach kommt,
die Restliste
Ja, also wieder ein Dotted Pear mit einem Element hier und einer Fortsetzung der Liste,
die wieder ein Dotted Pear ist, nämlich dieses hier.
Die enthält auch wieder zweistellig ein Element und den Listenende-Indikator,
der sagt, das war die gesamte Liste.
Also das sind Beispiele für rekursiv eingebettete Strukturen und damit werden
wir es im Weiteren zu tun haben. Wie gesagt,
wir sind jetzt auf der Ebene der Turing-Mächtigkeit,
das sollte uns also überhaupt nicht verwundern,
dass wir es jetzt ab sofort mit massiven Terminierungsproblemen zu tun haben.
Das liegt in der Welt der Programmierung in der Natur der
Programmierung begründet, dass ich eben in der Lage bin,
beliebige Zyklen aufzubauen mit meinen Programmen
Eine Verwendung von eingebetteten Strukturen ist etwa zur typsicheren Programmierung.
Wir hatten das ja schon mal bei der Abstraktion uns angeguckt.
In vielen Fällen abstrahieren wir von Eigenschaften der Realität,
dass wir beispielsweise in unserem Programm nicht mitführen, in welcher Maßeinheit irgendwelche Angaben erfolgen.
Und das kann ich natürlich hier erreichen, indem ich die Zahlenangabe
immer obligatorisch einbette in die Typ, den Typ der Zahlenangabe.
Also, dass ich hier eben dazuschreibe,
sind das Dollar oder ist das Euro oder sind das Grubel.
Und dann Muss ich natürlich in der entsprechenden Abfrage immer sicherstellen,
ich möchte den Preis in Euro haben.
Und ich kann natürlich auch sicher, mir Hilfsmittel schreiben,
die immer automatisch die Preise gleich in die Währung umrechnen,
die ich tatsächlich haben will. Und der andere Fall ist Datenabstraktion,
das findet man eben insbesondere auch in den objektorientierten Datenbanken,
dass ich Elementare zu komplexen Strukturen zusammenfasse,
damit ich in der Lage bin,
ein Informationskomplex mit einem Zugriff zu kommunizieren,
also bei Adressangaben beispielsweise,
die ja sich aus mehreren Angaben zusammensetzen
Solche rekursiv eingebetteten Strukturen entsprechen immer einer Baumstruktur,
indem ich eine Abbildung vornehme, dass ich sage,
ja, okay, eine Struktur,
egal ob die äußere Struktur oder eine der eingebetteten Strukturen,
ist immer eine Verzweigung des Baumes,
entspricht also einem Knoten im Baum und die Funktoren,
die Prädikatsnamen, sind die Knotenmarkierungen, also Beschriftungen,
die ich an die Knoten des Baumes schreibe,
der äußerste Funktor ist der Wurzelknoten des Baumes,
seine Argumente sind die Kanten,
die von diesem Knoten ausgehen und das gilt für alle rekursiv eingebetteten Strukturen dann gleichermaßen,
also die Argumente einer solchen Struktur sind immer Kanten im Und wenn ich Atome als Argument habe,
dann sind das die Blattknoten meines Baumes.
Das haben wir, wir können jetzt die Teil von Hierarchie,
die wir ja in der deduktiven Datenbank als Sammlung von Fakten,
Teil von Fakten aufgeschrieben haben,
die können wir jetzt genauso gut auch als eingebettete Strukturen aufschreiben,
dass eben das Auto drei Bestandteile hat, Radmotor und Karosserie,
das ist die erste Ebene dieser Hierarchie.
Und dass das Rad sich wieder aus Felge und Reifen zusammensetzt.
Und der Motor sich aus Zylinderkolben und Kobelwelle zusammensetzt.
Das ist also die gleiche Hierarchie, die wir dort auch
hatten, jetzt aber als eingebettete Struktur beschrieben
Damit haben wir zwei alternative Möglichkeiten, so etwas darzustellen.
Und dann müssen wir uns überlegen, für welchen Zweck ist welche Darstellung
am besten geeignet und können wir gegebenenfalls das eine in das andere umwandeln.
Anderes Beispiel hatte ich schon angesprochen,
sind eben Syntaxbäume für natürliche Sprache, wo ich sage,
dieser Satz, das ist klassische Trumpsky-Modellierung, nicht unumstritten,
die sagt,
ein Satz setze ich immer aus einer Nominalphrase und einer Verbalphrase zusammen,
die Nominalphrase ist hier das Subjekt John und die Verbalphrase ist das Verb mit seinem Kompliment,
also wen liebt Mary
Also das ist hier, wer geliebt wird.
Und das ist das Verb, was die Verbalphrase bildet.
Und beide zusammen bilden einen Satz. Ähnliches findet man in
der Welt der Programmiersprachen, wo ich arithmetische ausdrücke,
vom Compiler her syntaktisch zerlegen muss.
Und wenn ich also einen solchen arithmetischen Ausdruck habe,
dann ist der ja für uns in Infix-Notationen gegeben.
Typischerweise habe ich im Rechner aber so etwas wie Prefix- oder Post-Fix-Notationen.
Hier ist es mal in Prefix-Notationen umgewandelt. Dieser Ausdruck heißt,
multipliziere diesen Term mit diesem Term,
also der äußerste Funktor ist eine Multiplikation
Und diese Multiplikation hat zwei Argumente. Der erste steht hier
, das ist die Addition von 3 und 4.
Und der zweite steht hier, das ist die neuen,
die Differenz zwischen diesem Thermen und diesem Thermen hier.
Und hier an der Stelle muss ich natürlich Präzedenzrelationen mitachten.
Punktrechnung geht vor Strichrechnung, sonst kriege ich die falsche Struktur und ein falsches Ergebnis.
Also, ich muss die Differenz zwischen dieser neuen und
dem Ergebnis dieses Therms bilden, das ist sehr eingebettete Thermen hier.
Und das kann ich wieder als Baum darstellen,
also die Struktur dieses Baumes sieht dann im Endeffekt so aus,
ich habe ein Produkt aus einem einer Summe und einer Differenz zu bilden.
Und die Differenz ist wieder aus einer konstanten und einem Multiplikationsergebnis
zu berechnen. Das sind Verwendungsweisen für solche eingebetteten Ausdrücke.
Und wir müssen jetzt natürlich unseren Unifikationsbegriff erweitern.
Wir haben die Syntags erweitert von Konstanten und Variablen
hin zu Strukturen und eingebetteten Strukturen insbesondere.
Bisher hatten wir nur Strukturen an sich. Und es bleibt dabei,
dass die Unifikation daran besteht,
vaiablen Substitutionen zu finden,
die für zwei Strukturen
die Gleichheit herstellt
Und die Vergleichskriterien bleiben wie bisher auch, dass wir prüfen müssen,
ob der Funktor, der Prädikatsname identisch ist,
wir müssen prüfen, ob die Stelligkeit identisch ist.
Und anstatt nur die Argumente zu unifizieren, müssen wir jetzt rekursiv die Unifikation aufrufen.
Wenn ich an den Argumentpositionen jetzt eingebettete Strukturen vorfinde,
dann muss ich eben dafür sorgen, dass die wieder miteinander unifiziert werden.
Und ansonsten gilt eben alles, wie wir das haben,
wenn eine Struktur auf eine Struktur trifft, dann ist es durch Unifikationen sicherzustellen.
Also ich muss wieder die Argumentposition miteinander unifizieren. Und wenn
eben dort Strukturen stehen, gilt das Ganze rekursiv immer wieder nochmal.
Wenn eine Variable auf eine Struktur trifft, das ist nichts Neues,
dann wird die Variable gebunden mit der Struktur.
Umgekehrt genauso. Das ist symmetrisch. Und wenn Variablen aufeinandertreffen
, wird Co-Referenz hergestellt. Da bleibt alles beim Alten.
Da ändert sich gar nichts, wo sich etwas ändert,
ist eben der Fall, dass eingebettete Strukturen auftauchen.
Und das ist hier das zweite Beispiel. Wenn ich
diese Struktur mit dieser Struktur hier unifizieren soll,
oder fangen wir lieber oben an, diese Struktur, mit dieser Struktur unifizieren.
Ich prüfe, ob der Funktor identisch ist,
ich prüfe, ob die Stelligkeit identisch ist.
Ich muss die erste Argumentstelle unifizieren. Dabei wird die Variablenbindung
X gleich B hergestellt. Ich muss die zweite Argumentstelle unifizieren,
da wird die Variablenbindung Y gleich C hergestellt.
Das ist die erste Zeile. Jetzt in der zweiten habe ich
hier eingebettete Strukturen und dann muss ich also, Funktor ist identisch,
Stelligkeit ist identisch, diese Variable X mit dieser Struktur hier unifizieren.
Dabei wird die Variable X an diese Struktur gebunden und mit
dieser Variablenbindung gehe ich in die zweite Unifikation und ich sehe dort,
ich finde die gleiche Struktur nochmal vor.
Das heißt, die Unifikation ist erfolgreich
Beim Unifizieren dieser Struktur, die ja noch vajablenhaltig ist,
mit dieser Struktur, die ja keine Variable mehr enthält,
wird gleichzeitig noch das Y an B gebunden.
Das ist also die zweite Variablenbindung, die ich dort erhalte.
Der spannendste Fall ist der Fall, wo ich bei diablen Co-Referenz über Rekursionsebenen hinweg habe.
Hier habe ich also eine Variable auf dem Top-Level und hier habe ich eine eingebettete Variable.
Die eine Ebene weiter unten ist in der rekursiven Struktur.
Und jetzt fordere ich durch Unifikation mit diesem Term,
dass eine Chor-Referenz zwischen ersten und zweiten Argument hergestellt wird.
Das heißt, jetzt wird gefordert, dass dieses X
mit A von X unifiziert. Und das Ergebnis,
was wir da herausbekommen, ist,
dass X und Y tatsächlich den gleichen Wert haben und der heißt A von X. Was jetzt passiert ist,
dass ich eine Struktur aufbaue,
ich habe hier einen Baum sozusagen und der Baum sagt,
ich habe ein A von irgendetwas und das ist hier unten.
Und dieses A von irgendetwas
ist jetzt das A selbst wieder
Der Baum ist also kein Baum, sondern ein
entarteter Baum mit einem solchen elementaren Zyklus. Das heißt,
wann immer ich jetzt, ein A von irgendetwas habe,
wird es wieder ein A von irgendetwas sein.
Und das wird wieder ein A von irgendetwas sein und
das wird wieder ein A von irgendetwas sein.
Und das hat Infinitum in die Tiefe. Ich habe hier
durch Unifikation von zwei endlichen Thermen einen unendlich eingebetteten Thermen erzeugt.
Und das ist sozusagen der erste Blick,
den wir tätigen in die schaurigen Abgründe der Unendlichkeit.
Wir haben hier durch eine ganz, ganz simple Operation erreicht,
dass das Ergebnis ein unendlicher Term ist. Neuerdings schreibt,
dass es SWI-Prolog, wenn wir so etwas machen,
wenn wir also diese Unifikation vornimmt, wenn wir das einfach nur unifizieren,
kriegen wir eben tatsächlich heraus, was wir reingesteckt haben,
dass der Wert von X, A von X ist.
Wenn wir tatsächlich ausschreiben wollen auf der Konsole,
wir wollen also sehen, wie der entstehende Term aussieht,
dann sah das früher so aus.
Da gab es eine Printbeschränkung, die gesagt hat,
wir drucken das nur bis zur Tiefe 10 hin und danach
machen wir
drei Punkte
Produktsysteme von vor, was weiß ich, 20 Jahren,
da hat der Bildschirm angefangen zu flackern, weil sie versucht haben,
diesen Therm auszudrucken, was natürlich nicht geht.
Er ist unendlich tief. Ich drucke immer mehr A, Klammer auf,
Klammer auf, Klammer auf, Klammer auf, in die Tiefe.
Das ist ein unendlicher Therm und ich werde auch unendlich viel
Zeit und Platz brauchen, um ihn ausdrucken zu können.
Prolog macht jetzt dieses hier. Wenn immer der Versuch gemacht wird,
einen unendlichen Therm auszudrucken,
dann schreibt er da zwei solcher Sternchen da rein,
um zu signalisieren, das ist etwas,
was ich nicht ausdrucken kann.
Wir können natürlich
den Therm inspizieren
Wir können hingehen, wir unifizieren erstmal A von X mit X. Ja,
das ist genau dieser Zyklus hier, den wir hierherstellen.
Jetzt wollen wir wissen, was steckt denn eigentlich in der
ersten Stufe dieses Terms drin, nachdem wir das erste A abgetrennt haben.
Der Wert dieses As. Und dann wollen wir wissen,
was steckt denn eigentlich in der zweiten,
wenn wir zweimal ein A weggenommen haben,
was finden wir denn dort innen drin wieder?
Und das machen wir, werden wir auch mit dreimal.
Wir nehmen dreimal außen ein A weg von dem Term
und gucken, was sich im Inneren des Terms befindet.
Und wir kriegen als Ergebnis genau das, was zu erwarten war.
Das, was außen ist, das X ist gleich dem,
was wir nach der ersten Ebene finden.
Und was wir nach der ersten Ebene finden, ist genau
das Gleiche, was wir nach der zweiten Ebene finden.
Und das, was wir nach der zweiten Ebene finden,
ist genau das Gleiche, was wir nach der dritten Ebene finden.
Und was wir halt in der dritten Ebene finden,
ist wieder ein A von sich selbst. Ja,
also eine vaiable, die als Wert A von dieser Vajablen hat.
Das heißt, egal wie tief wir jetzt in diesen Thermen
hineingehen, wir finden immer wieder dasselbe Ding vor.
Was uns überhaupt nicht wundert, wenn wir diese Struktur hier sehen.
Wann immer wir sagen wollen, was ist denn dein Wert?
Dann kommt eben raus, ich bin mein Wert
Ja, und das in beliebiger Tiefe. Also hier tun
wir erstmalig einen Blick in die Abgründe der Unendlichkeit.
Und wir werden weitere Gelegenheiten haben. Wir werden auch kennenlernen,
wie wir mit solchen unendlichen Strukturen umgehen können.
Denn im Prinzip haben wir hier eine Variablenbindung. Wir können
das ohne weiteres hinschreiben, dieses Konstrukt. Erst in dem Augenblick,
wo wir es versuchen auszudrucken, laufen wir in Probleme,
weil es keinen endliche linearisierte Variante dafür gibt.
Denn das ist ja keine lineare Struktur hier.
Das ist eine zyklische Struktur. Wir können sie eben nur
nicht linear auftreiben. Das ist eine Beschränkung unseres Terminals,
aber nicht der zugrunde liegenden mathematischen Struktur
So, was wir jetzt gesehen haben sollten, ist,
dass wir Unifikation in ganz, ganz unterschiedlicher Art und Weise verwenden können.
Wir können nämlich es einerseits als Testoperator verwenden.
Wir können testen, ob ein bestimmtes,
ob zwei Strukturen miteinander verträglich sind. Wir können testen,
ob in einer bestimmten Struktur als erstes Argument eine 4 drinsteht.
Oder als drittes Argument ein, was weiß
ich, Stuhlbein drinsteht. Solche Tests können wir machen.
Also wenn wir Argumentpositionen instanzieren,
sind wir in der Lage,
Testbedingungen auszudrücken damit
Wir können es genauso gut als Eccessor oder Selektor oder Observer-Methode verwenden.
Das ist, da gibt es unterschiedliche Terminologien in der Welt der objektorientierten Programmierung.
Das ist das, was wir hier gemacht haben.
Hier haben wir versucht, Teile dieses Terms uns anzugucken.
Wir haben gesucht, gib mir bitte,
nachdem du einmal in die Tiefe gegangen bist, das,
was du dort wiederfindest. Und hier,
nachdem du zweimal in die Tiefe gegangen bist in dem Term,
gib mir das, was du dort findest.
Und hier gib mir das, was du in der Tiefe 3 findest.
Also wir können ja diese Baumstruktur eben darstellen,
wenn wir es linearisieren wollen,
als einen solchen entarteten Baum
Ja, und egal wie tief wir da unten reingehen,
wir finden immer wieder genau das Gleiche vor.
Das ist also sozusagen die Verwendungsweise als Zugrifffunktion zu einem Teil einer Struktur.
Und wir können es auch als drittes verwenden in Form einer,
als Konstruktor. Wenn wir aus elementaren Thermen oder mentalen Strukturen,
komplexe Strukturen zusammensetzen wollen, dann machen wir das auch mithilfe von Unifikationen.
Auf gut Deutsch, wir haben nichts anderes als Unifikation.
Alles läuft auf Unifikation hinaus. Der klassische Begriff
dieser Methoden aus der Objektorientierten Programmierung macht ja keinen Sinn mehr
Das ist genau die Deklarativität, ja. Zu diesen Strukturen
und Tönnen, Strukturen selbst, Variante sein werden.
Nein, Strukturen sind immer Funktor und Attribute. Ich kann
nur eben null Attribute haben, dann ist es ein Atom.
Dann bleibt nur der Funktor übrig. Aber Variablen sind
keine Strukturen. Weil ja, Strukturen können Variablen enthalten.
Sie können aber nicht selber bei Jablen sein. Insbesondere kann
der Funktor keine Variable sein. Dort muss immer ein Atom
stehen. Aber an den Argumentpositionen können natürlich Variablen stehen,
die noch per Unifikation zusammen mit einem Wert versehen werden müssen
Gut, wir gucken uns jetzt also ein Beispiel mal an,
wie wir mit solchen eingebetteten Strukturen arbeiten können.
Das ist kein sehr praxisnahes Beispiel, das will ich
gleich vorneweg sagen, sodass sich nicht irgendjemand beschwert darüber.
Es ist aber das einfachste Beispiel, was ich kenne,
wo wir mit eingebetteten Strukturen sinnvolle Dinge tun können.
Und das ist der Versuch,
die das Rechnen mit natürlichen Zahlen als auf der Logikebene nachzuvollziehen.
Wir haben ja einen Arrithmetikprozessor in jedem Rechner drin,
der relativ leistungsfähig ist, der schnell ist,
der Berechnungen dieser Art sehr schön machen kann
Der allerdings nicht logischen Prinzipien gehörtigt. Und es gab einen italienischen Mathematiker,
der also vor 100 Jahren hingegangen ist und versucht hat,
die Arithmetik natürlicher Zahlen mithilfe logischer Aktionen zu rekonstruieren.
Und das geht natürlich. Das Wichtigste dafür ist,
die Definition, was ist eine natürliche Zahl?
Wir brauchen ein Anfangselement und das definiert er damit.
Die Null ist eine natürliche Zahl und wir brauchen eine Nachfolgerbeziehung,
die sagt, jede natürliche Zahl besitzt einen Nachfolger,
der wird als S von N bezeichnet,
also Nachfolger Successor von N,
der ist wieder
eine natürliche Zahl
Und wir müssen sagen, als Stomädenabschluss,
nichts anderes als die so gebildeten Objekte sind natürliche Zahlen.
Also wenn ich eine natürliche Zahl habe,
dann bist du immer nach dieser Vorschrift gebildet worden.
Und damit habe ich eine regusive Definition.
Ich definiere eine natürliche Zahl dadurch, dass sie ein,
nein, oder ich definiere eine natürliche Zahl dadurch,
dass sie einen Vorgänger hat, der ebenfalls eine natürliche Zahl ist.
So, und das kann ich jetzt,
muss ich irgendwie umsetzen in die Welt der Programmierung.
Und dafür nehmen wir eben jetzt eingebettete Strukturen,
indem wir sagen, die Null ist eine natürliche Zahl.
Und wir haben als Interpretation dieser Struktur,
unsere klassische arithmetische Null im Sinn. Der Nachfolger der Null
ist eine natürliche Zahl und wir interpretieren das als die Eins.
Und der Nachfolger des Nachfolgers der Null ist eine natürliche Zahl und wir
interpretieren Dieses als die Zwei und so weiter und so fort.
Wir zählen also immer, wie viele solche Essstrukturen stehen hier eigentlich davor.
Das ist also vollkommen bewusst, ob das Es oder sonst
da wie heißt, das hätten wir auch a nennen können.
Aber es ist genau eine solche Struktur, ein Baum,
der einfach nur in die Tiefe wächst.
Mit einem Unterschied zu diesem Baum hier,
was ist der Unterschied
Ja. Ist nicht unbedingt wahr, selbstbezüglich Christum,
wenn die dir verzichten. Der ist unmittelbar selbstbezüglich,
ein S-Ausdruck enthält immer wieder einen S-Ausdruck und wieder einen S-Ausdruck.
Und das ist der Unterschied, genau. Hier ist ein
Abschluss da, die Null da drin. Hier ist eben
kein Abschluss da gewesen, wenn wir uns daran erinnern.
Wir können ja nochmal zurückgehen. Da steht ein X drin
, das heißt eine Vagable. Und die Vagable heißt,
dort können wieder beliebig viele Therme drin stehen.
Hier haben wir jetzt einen abgeschlossenen, sorry,
das ist die falsche Richtung
Einen abgeschlossenen Thermen, der immer zwangsweise da hinten eine Null
drin enthält. Und nur wenn wir diese Null finden,
ist es eine PA-No-Zahl, die eine solche arithmetische Interpretation mit hat.
Das sind jetzt also spezielle Prolog-Strukturen, das ist kein Konstrukt der Sprache Prolog,
das benutzen wir, um natürliche Zahlen in Prolog nachzuvollziehen.
Und wir definieren also syntaktisch immer ein PA-Notherm,
ist entweder eine Null oder eine Struktur der Form Functor S
mit einem Argument und das,
was da drin steht,
muss wieder
ein PA-Notherm sein
Gut, und jetzt gehen wir hin und wollen
Programme schreiben für das Rechnen mit natürlichen Zahlen.
Wir fangen mal ganz, ganz einfach an.
Und das ist ein Typtest. Wir wollen also nur testen
, ist eine gegebene Struktur eine natürliche Zahl.
Und wir haben natürlich wieder die Möglichkeit, das Ganze uninstanziert aufzurufen.
Und wir wollen, dass im Falle des uninstanzierten aufruft,
das System anfängt, natürliche Zahlen zu generieren.
Und wir müssen gucken, ob wir das erreichen.
Wir machen erstmal nichts anderes. Wir wollen ja deklarativ programmieren.
Das ist ja das Ziel. Wir wollen nicht irgendwelche Algorithmen realisieren,
sondern wir wollen einfach sagen, wie es ist.
Sagen, wie es ist es hier
Null ist eine natürliche Zahl und jede natürliche Zahl hat einen Nachfolger,
der ebenfalls eine natürliche Zahl ist. Und das schreiben wir
hier auf. Die Null ist eine PA-Notzahl. Und wir sagen,
der Nachfolger einer PA-Notzahl, wenn irgendetwas eine PA-Notzahl ist,
dann ist auch der Nachfolger dieses Dings eine PA-Notzahl.
Wir haben nur die Aktione von PA-No hier aufgeschrieben oder
in Programmiersprache übersetzt und fertig ist unser Programm.
Nicht immer haben wir es so einfach, dass wir bereits
eine rekursive Definition bekommen und die einfach nur als Klauseln umsetzen müssen.
Aber hier klappt es mal ganz einfach.
Was wir nicht brauchen,
ist der Domänenabschluss
Das ist eben genau unsere Closed World Assumption. Nur das,
was die Datenbank weiß, gilt. Wir müssen also nicht
extra sagen, alles andere müssen wir nicht betrachten,
sondern das ist implizit in der, im Domänenabschluss des Prologsystems drin.
So, jetzt rufen wir das vollständig instanziiert auf.
Also ist es von S von Null eine PANO-Zahl und
die Unifikation mit diesem Fakt scheitert,
weil eben Null sich nicht mit dieser Struktur inifizieren lässt.
Da müssen wir eben die Regel zur Hilfe nehmen und die Regel sagt,
unifiziere dieses S von X1 auf diese Struktur hier und die vajablen Bindung,
die du erhältst,
ist X1 kriegt den Wert S von Null
Wesentlich ist, die Struktur wird einfacher.
Die ist nicht mehr so komplex wie die hier oben.
Die hat noch zwei Essschalen, die hat nur noch eine Essschale.
Das ist ein wesentlicher Bestandteil zum Erfolg des Ganzen.
Wir konsumieren etwas von dieser Struktur. Wir machen sie kleiner.
Mit dieser vajablen Bindung X1 gleich ist es von null,
geht es in die Rekussion hinein.
Das ist das neue Ziel, was wir in das System geben.
Die Unifikation mit dem Fakt scheitert wieder, weil das Argument
null nicht mit der Struktur S von null unifizierbar ist.
Deshalb wieder die Regel, die sagt,
unifiziere S von X2 mit dieser Struktur S von null.
Dabei wird die vajablen Bindung X2 gleich null erzeugt
Knapsen wieder etwas ab von dieser Struktur. Und mit diesem,
mit dieser Wertebelegung geht es wieder ins Rennen und jetzt unifiziere das
mit unserem Rekursionsabschluss und das Ganze terminiert mit einem Erfolg.
Die Antwort ist, jawohl, S von S von 0
ist eine PA-Notzahl. Wenn wir das im Drehs uns angucken,
wir gehen also mit diesem Aufruf,
ist die S von S von 0 eine PA-Notzahl herein,
machen einen Entscheidungspunkt auf, wir testen das mit dem Fakt,
das unifiziert hier nicht.
Also Anwendung der Regel, X1 wird an S von 0
gebunden und mit diesem Wert wird das Ganze rekursiv aufgerufen
Wir kriegen einen neuen Entscheidungspunkt. Wir fangen mit der ersten
Klausel an. Die Unifikation mit der Fakt gelingt nicht,
weil Struktur mit Atomen nicht unifizierbar.
Wir kriegen die Regel zu fassen, die Unifikation gelingt,
weil Funkto identisch, Stelligkeit identisch und Argumente sind unifizierbar.
X-2 wird an null gebunden und mit diesem Wert geht das wieder in die Rekursion.
Wir machen einen dritten Entscheidungspunkt auf. Jetzt ist das Teilziel,
ist null eine PA-Notzahl, das unifiziert mit dem Fakt,
den wir in der Datenbank haben,
sodass wir insgesamt
einen Erfolg herausbekommen
Wir müssen ja kein Backtracking machen,
weil in diesem Fall ja nur eine Binär-Entscheidung zu treffen war,
ist es wahr oder ist es nicht wahr?
Wenn wir fragen, ob Susi eine PA-Notzahl ist,
dann scheitert die Unifikation mit diesem Fakt, weil die Atomen ungleich sind,
eine Zahl kann nicht mit einem alphonumerischen String unifiziert werden,
die Regel unifiziert, der Kopf der Regel unifiziert ebenfalls nicht.
Hier haben wir eine Struktur, hier haben wir ein Atom,
das scheitert, das heißt, wir kriegen als Ergebnis
Falls heraus,
Susi ist keine wohlgeformte PA-Notzahl
Wichtig ist eben die Tatsache, dass wir auf der Argumentposition
beim Reco-7-Abstieg tatsächlich immer eine Konsumption haben.
Wir fangen mit S von S von Null an.
Ans nächstes kriegen wir S von Null,
dann kriegen wir Null und dann terminiert das Ganze auch.
Und diese Konsumption ist tatsächlich, die Garantie dafür,
dass das ganze Verfahren zum Stillstand kommt.
Wenn es bei der Null angekommen ist,
dann kann er nicht mehr weiter in die Rekursion gehen.
Weil ganz einfach, wenn ich hier erstmal eine Null
erreicht habe, die Unifikation mit diesem Klauselkopf nicht mehr funktioniert
Dann habe ich eben eine Zahl, die mit dieser Struktur nicht verträglich ist.
Und deshalb kann ich die Regeln nicht mehr anwenden.
Und damit bricht die Rekursion ab. Und das Ganze terminiert
erfolgreich. Und wir haben es ja insbesondere auch mit diesem
kritischen Fall getestet, man versuche es auf etwas anzuwenden,
was nicht in der Domäne tatsächlich drin ist.
Also eine unbekannte, ein unbekanntes Datenelement. Und das ist in
diesem Falle Susi. Unsere Welt der PANO-Zahlen kennt von Susi
nichts. Und das Ganze verhält sich dann auch so,
wie wir es erwarten, nämlich es bricht ab.
Wenn wir das Ganze unterspezifiziert, aufrufen,
also was sind PANO-Zahlen
Und dann gelingt die Unifikation mit der ersten Fakte in der Datenbasis,
die Variablümlung X gleich 0 wird ausgegeben. Und damit wissen wir,
die erste PA-Notzahl ist X gleich 0. Wenn wir nach Alternativen fragen,
nimmt er natürlich diese Regel, hat jetzt hier einen komplexen Term,
den er mit diesem X unifizieren muss,
was dazu führt,
dass X als Wert diesen komplexen Term erhält.
Er ist von X1. Und mit dieser Variablenbindung X SS von X1
geht es in die Rekursion, allerdings ungebunden.
Und hier sehen wir, dieser Aufruf,
der jetzt als neues Teilziel kommt,
ist vollkommen äquivalent zu diesem Aufruf
hier oben
Ja, wir haben hier eine ungebundene Variable. Wir haben
hier eine ungebundene Variable. Dies sind zwar unterschiedliche Variablen,
weil wir über die Umbenennung einen anderen Variablen Namen bekommen haben,
aber es ist wieder eine ungebundene Variable.
Dieser Aufruf ist also vollkommen ein Klon dieses Dings hier oben.
Nur, dass die Variable umbedankt worden ist.
Und damit kriegen wir wieder eine Variablenbindung.
Diesmal für X1. Die kriegen wir aber nicht ausgegeben,
denn dieses X1 muss ja hier rein substituiert werden.
Dann wissen wir jetzt, dass XS von Null ist,
denn X1 ist Null.
Und dann können wir dieses X hier ausgeben.
Was wir jetzt als Wert bekommen,
ist also die Struktur S von Null
Und das ist die zweite PA-No-Zahl, die wir wissen wollen.
Wenn wir nach Alternativen fragen, ja,
dann geht es wieder in die Regel rein.
Und dieser Aufruf ist wieder ein Klon dessen,
was wir hier oben schon mal hatten.
Jetzt wird wieder X1 an, also dieses X1,
hier in dem Aufruf, wird jetzt an S von X2 gebunden.
Wir kriegen über den Fakt eine Variablenbindung für X2. Und wir wissen jetzt,
dass das Ergebnis X ist S von S von 0.
Die zweite PA-No-Zahl ist also S von S von 0.
Und wir können wieder nach Alternativen fragen und so weiter und
so fort
Wir sehen, hier wird jetzt eine Struktur konstruiert.
Mit genau dem gleichen Programm und dem gleichen Mechanismus,
den wir benutzt haben, um die Struktur zu zerlegen,
um rauszukriegen, ob es eine PANO-Zahl ist.
Also die Frage, ob ich in eine Struktur hineingehe und
einen Accessor mache oder ob ich einen Konstruktor baue,
ist nur eine Frage der Instanzierung.
Hier ist es uninstanziert und deshalb ist es ein Konstruktor.
Im anderen Fall war es instanziert und deshalb ist es
ein Accessor. Nur durch die Instanzierung entscheiden wir,
wie die Unifikation sich tatsächlich auswirkt
Das heißt, wir haben jetzt nachgewiesen,
das Ganze funktioniert instanziert, das funktioniert nicht Instanz.
Ihr, das funktioniert auch für negative Beispiele. Das heißt,
wir können jetzt unser Prädikatschema Alpha allgemeinern und sagen,
ja, das ist für Beliebige PA-Notherme anwendbar.
Instanz, sowohl instanziiert als auch nicht instanziert.
Das ist das Ganze nochmal im Dress,
das können wir jetzt langsam anfangen zu überspringen.
Also erster Entscheidungspunkt, der Fakt liefert das erste Ergebnis,
X gleich 0. Die Frage nach der Alternative aktiviert die Regel
durch den uninstanzierten Aufruf des Ganzen,
ja, das ist ja uninstanziert,
wird hier
eine Struktur konstruiert
Also in diesem Zeitpunkt, zu diesem Zeitpunkt steht bereits fest,
mein Ergebnis wird S von irgendetwas lauten.
Alles, was jetzt hier passiert, ist, was ist das X1 eigentlich?
Also was muss ich dort hineinstopfen oder hineinunifizieren,
damit das Ergebnis mit maximal vieler Informationen angereichert wird.
Und dafür mache ich den Rekord 7 Aufruf.
Ich kriege also jetzt für das Innere als erstes
den Wert Null und diesen Wert stopfe ich hier hinein.
Und das ist das, was mir hier ausgegeben wird,
von Null ist das nächste Ergebnis. Und wenn ich das wieder mache,
dann habe ich eben, kriege ich wieder mit dem Fakt
einen neuen Wert und dieser Wert muss hier eingefüllt werden,
erscheint dann hier mit der NS-Schale und jetzt müssen wir es hier hinten,
haben wir es aufgeschrieben.
Dieses X1 ist das, was hier hineingehört.
Also die Null wird hier hineinunifiziert, dass dieser Term wird
hier hineinunifiziert und damit steht das Endergebnis fest.
Tatsächlich stand das Ergebnis hier bereits fest.
Diese Struktur war bereits bekannt, nur ihr Inneres
noch nicht
Das Innere des X1 ist hier ermittelt worden,
dann stand bereits fest auf dieser Ebene,
obwohl die Berechnung überhaupt noch nicht abgeschlossen ist.
Auf dieser Ebene stand bereits fest,
es wird mal die Struktur S von S von irgendetwas sein.
Nur dummerweise kenne ich das irgendetwas noch nicht.
Und dieses irgendetwas wird hier berechnet mit diesem Fakt,
sorry, wird dieses irgendetwas gebunden und damit ist klar,
S von S von Null ist das nächste Ergebnis.
Also wir sehen hier,
und das möchte ich an der Stelle schon mal schon mal deutlich machen,
in dem Augenblick, wo ich die allererste Regel aufrufe,
ist bereits wesentliche Informationen über das Ergebnis bekannt.
Ich weiß bereits, wie das außenrum aussehen wird. Alles,
was ich noch nicht weiß, ist, was da innen reinpasst.
Aber dafür habe ich den rekursiven Aufruf, um genau das
zu berechnen. Und das ist also tatsächlich ein Effekt,
der so ein bisschen an Magie erinnert.
Und das ist nicht von weit her geholt,
wenn man so einen Vergleich macht.
Das Gute oder ein guter Zaubertrick ist immer so aufgebaut,
dass der entscheidende Trick passiert ist, lange bevor es der Zuschauer vermutet.
Und das passiert ja auch.
Der entscheidende Trick ist bereits hier passiert.
Lange bevor
das Endergebnis feststand
Da steht bereits ein großer Teil des Ergebnisses fest.
Nur noch ein kleines Detail fehlt. Und hier unten wird
mehr oder weniger das Kaninchen nur noch aus dem Hut gezaubert.
Da war es aber schon die ganze Zeit drin.
Das ist also durchaus eine tragfähige Analogie hier dazu.
Wir bauen das Ergebnis sukzessive auf und zwar,
indem wir das nicht von innen nach außen konstruieren,
sondern von außen nach innen konstruieren.
Und dadurch kennen wir die Struktur schon,
wir brauchen sie überhaupt nicht mehr zu verändern.
Wir reichern sie nur durch zusätzliche Informationen an.
Das ist Programmieren durch Informationsanreicherung
Gut, was wir jetzt hier also haben,
ist Rekursion durch die selbstbezügliche Definition eines Prädikats,
aber jetzt nicht mehr unbeschränkt,
wie wir das in den deduktiven Datenbanken hatten,
sondern jetzt beschränkt dadurch, dass wir ein Element konsumieren,
dass wir eine große Struktur sukzessive kleiner machen in unserem einfachen Fall,
also wir nehmen außenrum eine solche Essschade weg und versuchen das für den inneren Teil nachzuweisen.
Und wir haben dann für den inneren Teil irgendwann mal
eine Terminierungsbedingung. Das war in unserem Fall,
wenn dort drin eine Null steht, dann bist zu Ende.
Und das garantiert uns tatsächlich die Terminierung eines solchen Verfahrens,
weißt du, hier ungefähr haben, wir machen ein großes Problem,
immer sukzessive kleiner, Schritt für Schritt.
Bis wir irgendwann bei einer Terminierungsbedingung ankommen,
das könnte hier sein, ich bin auf der Pixel-Ebene angekommen.
Kleiner geht es nicht mehr. Es könnte aber auch sein,
dass es gar keine Terminierungsbedingungen gibt. Dann geht der Prozess
eben, Ad Infinitum weiter und kommt nicht zum Halten.
Die Terminierungsbedingungen, für die müssen wir natürlich sorgen,
um das Ganze zum Halten zu bringen.
Und wir haben hier bereits einen wichtigen Spezialfall kennengelernt von Rekursion,
die sogenannte Endrekursion, der Das wird eben bei Frau Dröschler in der gleichen Weise begegnen.
Nur, dass es hier viel,
viel mehr endregusive Prozeduren gibt, als in der funktionalen Programmierung.
Ich kann, das kann ich schon mal vorwegnehmen,
in der Logikprogrammierung Dinge endregusiv programmieren,
die hier in der funktionalen Programmierung nicht eintrikotsiv hinbekomme.
Und wir sehen es eben hier, das Ganze ist endregusiv,
weil eben das Ergebnis hier oben bereits feststand.
Nur leider noch ein bisschen unterspezifiziert war. Und diese Unterspezifikation ist ein ganz,
ganz wichtiger Aspekt, der uns jetzt immer wieder begegnen konnte.
Wir konnten hier das Ergebnis bereits beschreiben, nur noch nicht vollständig.
Hier können wir es schon ein bisschen besser beschreiben,
aber immer noch nicht vollständig. Erst wenn wir diesen Wert
kennen, wissen wir tatsächlich, wie das Ergebnis aussieht.
Es wird also sukzessive mit Informationen angereichert. Und das ist also
ein endregusiver Fall, wenn ich hier unten angekommen bin,
dann habe ich bereits das Ergebnis vorliegen. Und das,
was als Frau Drescher immer als Nachklappern bezeichnet,
dass ich jetzt anschließend das Ergebnis nochmal aufbauen muss,
also die Null muss eingebettet werden, hier rein und muss eingebettet,
das muss eingebettet werden hier rein,
das muss ich gar nicht mehr machen.
Das ist längst passiert. Das ist genau der Zaubertrick.
Das ist alles bereits beim Reco-7-Abstieg über die Bühne gegangen.
Hier unten habe ich das Ergebnis da.
Und deshalb brauche ich überhaupt keinen Reco-7-Aufstieg mehr zu machen.
In diesem Fall, weil es eine endregusive Prozedur ist
Und das ist eben tatsächlich etwas, solche Programme,
wo ich Strukturen konstruiere,
die kann ich in der funktionalen Programmierung nicht end-trikotsiv programmieren,
weil ich zum Schluss immer noch einen Kunst machen muss,
das muss ich hier nicht machen,
das ist hier längst passiert, dank der Richtungsunabhängigkeit der Unifikation.
Dass ich die eben sowohl als Konstruktor, als auch als Selektor benutzen kann.
Und das ist so ein bisschen das Wunder der Logikpromierung und das
gucken wir uns im Weiteren immer und immer wieder an,
an den verschiedenen Anwendungsbeispielen.
Soviel für heute, schönen Dank für die Aufmerksamkeit und ein
schönes Wochenende