Da kommt ein Zyklus, immer und immer wieder.
Rekursion heißt ja, Bezugnahme auf das vorherige Zwischenergebnis.
Ja, das ist gut, aber die Geschenkegrößen sehen.
Jetzt wären Hypothesen gewagt, ja. Also ich sehe keine
Rekursion, um es kurz zu machen. Ich wollte nur
einen Cartoon auswählen, der ein bisschen zum aktuellen Geschehen passt.
Deshalb habe ich diesen genommen. Früher habe ich hier immer
eine Kerze auf das auf das Pult gestellt,
wenn es daran geht, die letzte Vorlesung vor Weihnachten zu haben.
Ich weiß nicht, ob es Ihre letzte ist,
aber ich vermute mal, dass das so ziemlich die letzte Lesung noch zu absolvieren haben.
Das verkneife ich mir, da wir ja über Eurovision
die Zuschauer zu Hause zugeschaltet haben.
Das ganze Videomaterial kann hinterher gegen mich verwendet werden vor Gericht
, wenn dieses Gebäude hier abbrennt.
Also verkneife ich mir das. Wir haben natürlich heutzutage viel,
viel elegantere Möglichkeiten, mit dem Problem fertig zu werden und
die Lösung heißt, Ist das nicht schön?
Da kann man auch noch irgendwo den Vollbildmodus einstellen,
Herr Gott, wo ist er denn hier?
Ah, da unten, ja, richtig. So. Ja,
ja, ja, ja, ja, ist gut.
Ist das wunderschön. Das geht über eine Stunde lang.
Also man kann so richtig schön relaxen dabei.
Und man hat die brandschutzrechtlichen Vorschriften beachtet.
Also insofern hat man dann auch kein Problem mit der Feuerpolizei.
Gut, nun aber dann doch wieder zu den Inhalten.
Wir sind ja ein bisschen hups,
was ist jetzt passiert
Wir sind ja ein bisschen hinterher, weil die eine Veranstaltung
nicht stattgefunden hat. Ich hatte es ja schon angekündigt,
ich habe den gesamten, das gesamte Kapitel der aktiven Datenstrukturen
jetzt übersprungen oder ich überspringe es gerade.
Ich bin gerade dabei, es zu überspringen. Ich komme
aber zum Schluss, so denn die Zeit reich darauf nochmal zurück.
Was heute kommt, ist sozusagen die Vorbereitung,
die wir brauchen, um dann Metaprogrammierung betreiben zu können.
Also an verschiedenen Stellen blitzen jetzt immer wieder Dinge auf,
die wir brauchen werden,
wenn es da rangeht,
ein Skim-Interpreter-In-Prolog zu schreiben
Und dafür braucht man ein paar technische Hilfsmittel,
die bis jetzt noch nicht behandelt worden sind. Also,
worum es jetzt geht, sind sogenannte Extra- und metralogische Prädikate,
Extralogik heißt also, das hat mit Logik überhaupt nichts zu tun.
Sowas wie Input und Output, das kann ich nicht
mit dem Mittel der Logik beschreiben. Das sind Seiteneffekte,
die ich auslöse über Input, Output-Kanäle,
die ja mit der Logikprogrammierung nichts zu tun haben,
die wir aber brauchen, wenn wir damit praktische Aufgaben lösen wollen.
Metalogik heißt immer, ich schaue auf von einer Metaebene auf
das Geschehen der Ableitung. Bisher haben wir immer nur Ableitung betrieben,
indem wir ein Ziel in die Bearbeitung hineingeworfen haben,
dann fängt die Maschine an zu rödeln.
Und zum Schluss kommen bei Yablen, Belegungen heraus,
wenn dann alles glatt gegangen ist, kommen bei Yablen Überlegungen heraus, die das Berechnungsergebnis darstellt.
Und jetzt gucken wir also auf diese Verarbeitung drauf und wollen steuern,
dass bestimmte Ergebnisse nicht mehr produziert werden, dass bestimmte Bedingungen eingehalten werden,
eh irgendetwas produziert wird und so weiter und so fort.
Das ist also zum Beispiel dieser Bereich der Suchraummanipulation.
Das ist aber auch der Bereich der kontrollierten Instanzierung,
wo es jetzt eben hingehe, Und dem System,
die Unifikation aus der Hand nehme und sagen,
das möchte ich bitteschön selber regeln, unter welchen Umständen was tatsächlich instanziert wird.
Das haben wir ja bisher immer als Service der Unifikation dankbar entgegengenommen,
die erledigt die Variablenbindung für uns. Jetzt werden wir dahin gehen,
gezielt bei Jablen zu binden und das brauchen wir dann,
wenn wir hingehen und Metainterpreter schreiben,
die jetzt eine Prolo-Klausel nehmen, die Prolo-Klausel aber erstmal angucken,
Bedingungen abtesten und dann erst entscheiden,
ob sie die Prolo-Klausel so ausführen oder in modifizierter Form ausführen.
Das ist genau Meta-Programmierung
Und dazu muss ich erst mal verhindern,
dass der Normalapparat, nämlich unifiziere den Kopf,
substituiere, die Bajablen in den Körper der Klause,
rufe den Körper der Klausel auf.
Genau dieser Prozess, den müssen wir dann in die eigene Hand nehmen.
Und zu dem Zweck müssen wir also Mittel zur Verfügung stellen,
dass wir, dass das Prolog-System nicht sein Standardprogramm abwickelt,
sondern dass wir die Kontrolle haben, was passiert.
Das ist also dieser Bereich hier. Dann ein Typ,
eine Typkonvention, das ist das, was wir in jeder Programmiersprache viel,
viel Reichheit hier haben als hier.
Hier taucht das nur an eigenen Stellen auf und ich möchte
nur kurz den Eindruck davon vermitteln, was so typischerweise zur Verfügung steht.
Und dann ist der große Bereich der extra logischen Prädikate.
Also das ist sowas wie Input Output
Gut, die Lupe hier eben als Indiz daran. Dafür,
jetzt gehen wir hin, nehmen Prolog-Programme und schauen uns an,
wie die Verarbeitung da tatsächlich passiert. Da gehen wir dann
in die Details rein. Also hier noch mal die Gegenüberstellung,
was metalogische Prädikate sind und was extralogische Prädikate sind.
Die in Klammern setzten, werde ich hier nicht weiter behandeln.
Ich gehe nur auf die Sachen ein,
die nicht eingeklammert sind
Gut, also wir haben im Prolog,
im Prinzip diese vier atomaren Basistypen, Inditscherzahlen.
Wir haben schon gesehen, das sind sehr große Inditscherzahlen,
die wir zur Verfügung haben. Wir haben Fließkommazahlen zur Verfügung.
Wir haben Atome zur Verfügung und wir haben Zeichenketten zur Verfügung.
Und mit den ersten dreien haben wir schon gearbeitet,
also Inditscher haben wir meistens eingegeben. Manchmal haben wir Gleitkommazahlen herausbekommen,
aber natürlich in einer dezimalen Notationsverlehnung, notwendigerweise.
Wir haben da auch, als es um die Genauigkeit ging,
auch als Gleitkommazahl rausbekommen. Atome haben wir typischerweise verwendet,
womit wir überhaupt noch nicht umgegangen sind,
so Zeichenketten
Die sind, glaube ich, noch gar nicht,
doch, sie sind an einer Stelle, sind sie aufgetaucht.
Wir können aus diesem Basistypen dann abgeleitete Typen bilden,
also so etwas wie Number ist eben Intager oder Float und Atomic
ist dring, Atom oder At Number.
Auf die Art und Weise hat man die Hierarchie von Datentypen
zur Verfügung. Das ist alles nicht sonderlich spannend,
aber man kann natürlich mit Hilfe solcher solcher Typtests jetzt eine
ganze Menge Sachen machen, indem man eben hier testet,
dass nur bestimmte Datentypen an Argumentpositionen übergeben werden können.
Und auf die Art und Weise kann ich eben verhindern,
dass das eine ein Terminierungsproblem entsteht. Allerdings bezahlen wir natürlich einen Preis dafür,
denn auf die Art und Weise geht die Richtungsabhängigkeit verloren.
Also wir fordern jetzt dadurch hier,
dass dieses X und das Y instanziierte Variablen sein müssen.
Macht beim größten gemeinsamen Teil andersrum auch wenig Sinn.
Also wenn wir das andere hätten, dann hätten wir eine
Faktorenzerlegung und ganz etwa kurz davor irgendwelchen Programmcode zu knacken.
Gut, und der Effekt davon ist,
wenn wir das Ganze jetzt also tatsächlich hier mit Gleitkommazahlen aufrufen,
dann scheitert das Ganze ganz einfach deshalb,
weil diese Typtests hier vorn bereits scheitern und er gar nicht dazu kommt,
die eigentliche Berechnung durchzuführen.
Das ist alles nicht sonderlich spannend. Zum Zeichenkettentyp zum Datentyp
Zeichenkette, da gibt es zwei Varianten,
die ursprüngliche und logisch saubere Form ist die sogenannte Edinburgh-Variante.
Da ist nämlich eine Zeichenkette immer eine Liste von Aski-Werten.
Also wenn ich dieses Zeichenkette hier auf der Oberfläche sind,
die doppelten Anführungsstrichelchen immer das Zeichen für Zeichenketten,
wenn ich das eingebe und anschließend mir den Wert anzeigen lasse,
dann sehe ich, was passiert ist.
Es kommt eine Liste heraus,
wo die Aski-Werte dieser Buchstaben,
also der Einzelzeichen drinstehen
Das ist, hat einen schönen Vorteil, dass ich nämlich,
wenn ich eine solche Zeichenketten habe, Zeichenkettendarstellung habe,
ich die ganzen Vorteile der Listenverarbeitung damit erbe.
Und wir hatten gesehen, die Listenverarbeitung als das Kernstück der
richtungsunabhängigen Programmierung. Wir haben so gut wie nirgendwo gesehen,
wenn wir nicht auf Archithmetik zurückgreifen,
aber solange wir im Bereich der Listenverarbeitung waren,
können wir immer alles in allen Richtungen aufrufen.
Und diese Eigenschaft erbt dann auch diese Zeichenkettenverarbeitung hier.
Natürlich bezahlen wir wieder einen Preis dafür und der Preis heißt,
Öh, das ist nicht das Problem.
Wir können, das können wir sogar erreichen,
dass er uns immer wieder diese Oberflächenvariante ausgibt.
Das ist nicht das Problem. Das können wir abschirmen und von außen sozusagen immer mit der,
mit irgendetwas lesbarem Arbeiten, dass er intern diese Repräsentation verwendet,
müssen wir eigentlich gar nicht wissen, ja.
Oh, das passiert sich auf den zweiten Teil dort,
dass er nicht mehr feststellen kann, wohin sie das bekommt.
Das ist ein Wahnsinn. Warum will man
das auch feststellen
Ach so, ja, das liegt aber an dem Typ-Teststring hier,
der nur sagt, ich bin keine Adambro-List,
ich bin keine Adambro-Zeichenkette, sondern ich bin eine ISO-Zeichenkette,
die kommen gleich auf der nächsten Folie.
Nee, der Preis ist ganz einfach, diese Liste hier
ist natürlich Speicherplatzfressen, bis zum geht nicht mehr.
Das sind immerhin, das haben wir hier sechs Konzellen,
die vermutlich alle acht byte groß sind und wenn nicht größer
und dann nochmal die einzelnen Buchstaben,
auf die die einzelnen Pointer zeigen.
Also das ist eine Speicherplatzfressende der Repräsentation
Während ich hier genau sechs Bytes brauche, um das abzuspeichern,
okay, hängt von der Kodierung ab, die ich verwende,
brauche ich hier also bestimmt das Achtfache davon.
Und deshalb ist das also nicht sehr angenehm,
weil sehr viel Spreichspeicherplatz dafür gebraucht wird, um solche Listen zu verwalten.
Und deshalb gibt es die Alternative, das ist nach
der ISO-Norm dann der Datentyp drin.
Und wenn ich jetzt unter der ISO-Norm arbeite und diese Zeichenkette
eingebe und den Wert wieder abfrage,
dann kriege ich ihn
auch tatsächlich so ausgegeben
Und jetzt klappt auch dieser Typ-Testring hier, der testet nämlich
genauer gesagt, bist du eine ISO-Zeichenkette oder nicht.
Und wenn du eine Edinborger-Zeichenkette bist, dann scheitert er.
Nun ist die Frage, womit arbeiten wir standardmäßig, ist ISO eingestellt?
Ich kann aber in SWI-Prolog und das ist ja systemabhängig,
das macht jedes System, wie es möchte.
Ich kann in SWI-Prolog umschalten mit diesem Prädikat-Style-Check,
in dem ich jetzt hier String einschalte, dann ist der ISO-Standard eingeschaltet,
das sehen wir hier dran, wenn ich Minus-Dring hier angebe,
dann sehen wir, dass der Edinborger Standard eingeschaltet ist.
Also was immer, welche Art und Weise der Behandlung man haben will,
die kriegt man auch. Also es ist ja der Zusatz,
die Zusatzleistung ist ja der ISO-Sache,
da muss ich einen Extramechanismus für die Zeichenketten,
insbesondere ein extra Speicher für die einrichten,
während im Edinburgro-Modus natürlich alles auf die übliche Listenverarbeitung zurückgeführt wird und überhaupt nichts extra erforderlich,
außer der einen Tatsache, dass wann immer das am Input erscheint,
ich es sofort in eine solche interne Liste umwandeln muss.
Aber das ist
relativ einfach getan
Gut, also so viel zur Zeichenkettenbehandlung. Deshalb habe ich,
weil das ein bisschen undurchsichtig ist, habe ich das
am Anfang völlig ausgespart und immer nur mit Atomen gearbeitet.
Und wenn man eben Atome hat, die nicht der konstanten Konvention gehorchen,
da macht man eben die einfachen Hochkommas drumherum, um zu signalisieren,
hallo, hier sind so taktisch irreführende Elemente mit dabei.
Also Zeichenketten ist nicht Zeichenkette, ist nicht gleich Atomen.
Das ist die Botschaft hier dabei,
da muss man
ein bisschen aufpassen
Es gibt da eine ganze Reihe von Strukturtypen. Eins,
allerseits Kompo und Kompo und ist also alles, was nicht Atomic ist.
Und der Tomic waren eben die Atome, die Zahlen und so weiter.
Und ja, gut, das ist eigentlich nur die Meditation.
Dann gibt es die Unterscheidung, die auch in der funktionalen Programmierung,
ob ich ein gepunktetes Paar habe, das heißt im SPI-Prolog ist List.
Das schaut nur, ob ganz oben das erste Listenelement eingepunktetes
Paar ist mit K und CDR im Skim-Sinne. Wenn ich wissen will,
ob es wirklich eine richtige Liste ist, das heißt also,
dass sie über alle Rekursionsebenen bis hinten hin eine Liste darstellt,
dann muss ich Propalist verwenden.
Das ist also so ähnlich wie in Skim auch.
Also nur zur Erinnerung, Propalist heißt,
ich muss die Matroschka zerlegen, bis ich mich davon überzeugt habe,
dass ganz innen drin auch eine Matroschka ist.
Während Ist List nur guckt, ob die äußere Höhle eine Matroschka ist.
Was drin ist, darüber mache ich keine Aussage.
Terminierperialist auch für zyklische Listen. Das würde ich nicht glauben.
Habe ich noch nicht ausprobiert, aber wir können es ausprobieren.
Das ist relativ schnell getan
Wollen wir das jetzt machen? Muss ich jetzt sein? Ja,
es gibt noch die Variante davon, das ist das SZ und das SZ.
Das ist Zeit, die Vorlesung abzubrechen. Pointer geht nicht mehr
. Das SZ ist wie Properliste, nur dass es nochmal guckt,
ob Duplikate in der Liste enthalten sind oder nicht.
Das ist also ein Spezialform davon. Arrethmetische Funktionen dienen im Wesentlichen dazu,
um Typumwandlungen zu machen, also eine Flow-Zahl herzustellen,
eine Intex-Stadtzahl herzustellen und so weiter.
Die haben wir teilweise schon gesehen, als es darum ging,
den, das Sub-Atom zu schreiben, ein Prädikat,
was beispielsweise ein Atom nimmt und als ein Produxtring umwandelt oder eben
einen Namen nimmt und in eine solche Zeichenkette umwandelt und so weiter und so fort.
Das ist also, ich gehe da jetzt nicht im Einzelnen durch,
das ist typisches Manual-Wissen, das muss man im Ernstfall sowieso nachgucken.
Das muss ich auch nachgucken, nur damit man weiß,
es gibt also alle möglichen Konversionswerkzeuge.
Wenn ich irgendetwas gegeben habe, was den falschen Typ hat,
gibt es typischerweise immer irgendetwas, was ich dann umwandeln kann.
Hier kann ich zum Beispiel ganze Therme in einen Atom umwandeln
Therme sind ja immer Funktor mit Argumentliste. Kann ich in einer Atomumwandel,
wenn ich das denn haben will, für irgendeinen Zweck?
Weiß ich im Augenblick auch nicht, wofür man das gut gebrauchen könnte.
Aber da gibt es eben die üblichen Sachen dafür.
Hier sind ein paar Beispiele. Also wenn ich
dieses Atom-ABC habe und in Adam Codes darauf anwende,
dann kriege ich eben hier diese Liste der Aski-Werte.
Und allerdings ist das, was ich hier eben erzeuge,
das haben wir vorhin schon gesehen,
kein Datentyp-String
Ja, es gibt dann extra dieses Strink-to-Attem, die sind fast,
ja, die sind eigentlich alle auch richtungsunabhängig gemacht,
also ich kann sie immer benutzen,
um von stringen nach Atom umzuwandeln,
als auch von Atom nach String umzuwandeln.
Hier also die Richtung, ich gebe einen Atom vor,
mache einen dringend draus und dann ist das,
was hier rauskommt, tatsächlich an ISO.
String, muss ich dazu sagen,
ist unter ISO-Standardeinstellung gemacht und dann ist dieser Test,
ob es denn ein ISO-String ist,
auch erfolgreich
Ja, für Atome gibt es eben so etwas wie die Länge eines Atoms.
Da kann man sich vorstellen, wie es implementiert ist fürs
Strings, das Gleiche nochmal. Substring gibt es genau,
wie ähnlich wie das Subatom, was wir programmiert haben.
Gut, Philisten, das haben wir schon gesehen,
die sind wir da alle durchgegangen. Für Atome,
Typkonstruktoren, wenn ich also eine Liste von Zeichen habe,
dann kann ich die benutzen,
um einen Atom zu generieren oder wenn ich einen Atom habe,
kann ich es eben in seine Zeichen zerlegen.
Das Gleiche gilt für Zahlen zu machen.
Und es gibt auch Conkett, das ist im Prinzip,
wie er pennt für Listen, ist das über Atomen definiert.
Also verkette dieses Atom mit diesem Atom und du
kriegst das dritte Atom heraus. Und wir sehen auch,
da stehen überall Fragezeichen drin, dass es richtungsunabhängig implementiert.
Auch wie Appent, Appent gibt es in zwei Varianten,
die eine Variante hatte ich hier noch gar nicht behandelt,
ein paar kluge Köpfe haben es in der Übung bei mir aber benutzt.
Es gibt auch für Appent die Variante,
dass ich die eine Liste von Listen liefere und dann ein Appent
darauf anwende und die Verkettung all dieser Unterlisten herausbekomme Ähnliches gibt es auch fürs Comcat,
dass ich hier eine Liste von Atomen reingebe und einen Atom herausbekomme.
Hier sieht man natürlich,
dass die Richtungsunabhängigkeit Richtungsunabhängigkeit nicht so richtig sinnvoll ist,
also für einen Atom zu sagen, gib mir alle Listen,
in die ich das Atom zerlegen kann.
Das ist eine gigantische Ergebnismenge ohne großen praktischen Nährwert.
Was wir brauchen werden für die Metaprogrammierung, dringend brauchen werden,
das ist ein Operator, der hier als Gleichpunktpunkt vereinbart ist.
Der erwartet auf der linken Seite einen Term, also etwas Funktor mit Argumentliste
Und auf der rechten Seite wandelt er dies in eine Liste um.
Oder in beiden Richtungen. Gegeben, eine Liste kann ich daraus einen Therm produzieren.
Oder gegeben einen Therm kann ich daraus die Liste produzieren.
Also ich kann diesen Therm nehmen und sage, gib mir
bitte den Funktor plus die Argumente in Form einer Liste.
Oder umgekehrt, ich gebe dir diese Liste und mache mir bitte einen Therm draus.
Und das ist genau die Verwendungsweise, die wir brauchen werden.
Hier können wir uns jetzt nämlich Prädikate selber zusammenbasteln.
Das ist der einzige Weg, den wir haben,
um den Funktor valiabel
zu halten
Der Funktor spielt ja eine Sonderrolle in der Thermounifikation,
die wir hier verbinden. Es gibt Graf-Unifikationssysteme, da gibt
es diese Sonderrolle nicht, aber wir haben sie hier.
Wenn wir jetzt also einem Ziel beispielsweise konstruieren wollen,
was von mir aus Finde, XY heißt,
dann haben wir Schwierigkeiten, diesen Namen Finde dort reinzusetzen.
Das müssen wir immer vorher definieren, indem wir schon
alle Möglichkeiten aufzählen. Wir wollen aber die Möglichkeit schaffen,
das dynamisch generieren zu können und dynamisch zu generieren,
können wir es auf die Art und Weise,
dass wir diese Liste finde XY aufbauen und dann Mittelshilfe dieses Operators uns diesen Thermen konstruieren lassen.
Und diesen Thermen können wir dann sofort wieder in die Suche
schicken und sagen, jetzt arbeite ihnen ab.
Und das ist der Missing Link, den wir brauchen für
die Metaprogrammierung. In etwas ähnlicher Form,
aber da haben wir die Dinger nicht selber generiert,
sondern dann haben wir sie eingelesen aus der Datenbank.
Sie erinnern sich bei diesem Prädikat für die Testautomatisierung.
Da haben wir Aufrufe aus der Datenbank eingelesen und sofort etwas ausgeführt.
Und hier haben wir jetzt die Möglichkeit,
Aufrufe zu konstruieren und dann auszuführen.
Das werden wir also brauchen an dieser Stelle
Wir können gucken, ob eine Variable einen Wert hat oder
nicht, also ob sie instanziert ist oder nicht.
Das ist ein einstelliges Prädikat. Und wenn ich eben eine Uninstanz und ihre Variable dort reingebe,
dann kommt das hier raus, heißt Truh im Prinzip,
und sagt, jawohl, X ist noch vaiabel.
Während, wenn ich hier eine Konstante angebe,
dann ist es nicht mehr vayabel, dann ist es False.
Genauso natürlich, wenn ich Sequenzen angebe,
das hängt jetzt nicht an der Konstanten da,
wenn ich also so etwas mache wie
Dann kommt natürlich auch Fortes heraus,
weil die Variable eben nicht mehr vajabel ist.
Sie ist bereits instanziiert. Also das ist ein Test auf Instanzierung,
ob eine Variable bereits einen Wert hat oder nicht.
Und das kann ich natürlich benutzen, um richtungsunabhängigkeit zu programmieren an Stellen,
wo sie auf natürliche Art und Weise nicht gegeben ist.
Der Trick ist natürlich auch, nichts kommt umsonst.
Achso, ja, das ist das Gegenstück dazu.
Der Trick ist natürlich nicht umsonst, sondern erfordert,
dass sich alle Berechnungsrichtungen ausbuchstabieren muss.
Alle Berechnungsrichtungen, die ich tatsächlich haben will,
die müssen
explizit ausprogrammiert werden
Das Gegenstück, das ist eigentlich das,
was ich typischerweise brauche, dass ich teste,
ob eine Variable bereits einen Wert hat und nur dann die Berechnung darüber durchführe,
funktionale Berechnung, wenn der Wert da ist.
Also das ist der typische Aufruf, den ich brauche,
ich teste, ob irgendetwas nicht vaabel ist und wenn es nicht vaabel ist,
dann ist es eine Konstante, mit der ich rechnen kann.
Also so etwas wie A ist natürlich nicht vaabel,
so etwas wie A von X, und da sehen wir hier,
können wir auch eine Therme reingeben,
enthält dieser Therm,
das ist eigentlich die Bedeutung,
enthält noch eine uninstanzierte Variable und deshalb ist das ganze Trom
Und die Verwendung als Guard, die sollte jetzt aus so einer,
das noch wieder eine Variante, wir können auch testen,
ob ein Thermenground ist, ground heißt also, er enthält keine Variablen
Variablen mehr. Dieser Therm enthält noch eine freie Variable,
deshalb ist er nicht ground. Dieser Therm enthält keine freie Variable mehr.
Deshalb ist er, ist ground von dem Therm wahr.
Jetzt kommt endlich das Beispiel, auf das ich die
ganze Zeit warte. Hier können wir jetzt die Addition,
was ja eine funktionale Angelegenheit ist, wenigstens überwiegend relational machen.
Es ist nicht vollständig, relational den Grund können wir uns
ja gleich angucken. Also was ich erreichen möchte,
ist, dass alle diese Argumente unterspezifiziert sein können,
in der vollen Schönheit ist es nicht so ganz einfach,
aber wir machen es hier wenigstens so, dass zumindest zwei Argumente instanziert sind.
Aber wir wollen nicht festlegen, welche es sein müssen.
Also in der klassischen funktionalen Verwendungsweise muss ich ja immer sagen,
die so Manden sind gegeben und die Sommer rechne ich dir aus.
Ich möchte jetzt auch, dass die Summe gegeben ist und
ich will einen so Mannen davon ausrechnen.
Und da muss ich eben einfach testen,
ob in diesem dieser Signatur hier X oder wenn X und Y nicht vaiabel sind,
dann kann ich eben ganz normal X und Y addieren und
kriege dann das Ergebnis Z heraus
Wenn denn X und Z nicht vajabel sind,
dann muss ich eben, dann ist das Y die Variable,
dann muss ich Y berechnen,
indem ich aus der Summe den Summanten abziehe, den gegebenen.
Und wenn denn Y und Z nicht vajabel sind,
dann ist X die gesuchte Variable,
dann muss ich eben von Z Y abziehen,
um das gesuchte X herauszubekommen.
Auf die Art und Weise kann ich eben jetzt
die verschiedenen Berechnungsrichtungen da rein bringen. Wie gesagt,
immer unter der Einschränkung, dass wenigstens zwei Argumente gegeben sind.
Wenn ich das auch für nur ein Argument es gegeben machen will,
dann muss ich hier irgendwie den Nicht-Determinismus einbauen.
Dann wird es aufwendiger. Es geht natürlich im Prinzip auch,
aber es ist nicht mehr ganz so elegant
Aber wir können auf die Art und wenige Art und Weise
wenigstens so eine gewisse Richtungsunabhängigkeit vortäuschen. Aber es ist natürlich nur vorgetäuscht,
wir müssen das hinter den Kulissen eben in jeder Richtung ausbuchstabieren.
Okay, das sind nur die Aufrufe,
damit man sieht, das funktioniert dann tatsächlich auch.
Also das ist der kanonische Aufruf 3 plus 4, ist dann eben 7.
Hier ist dann, was muss ich zu 1 addieren,
damit 4 rauskommt und in dem Augenblick,
wo ich zwei uninstanziert lasse, da kommt ein False raus,
weil eben die,
es ist keine einzige Variante in der Implementierung gibt,
die mit einer instanzierten Variablen arbeiten würde.
Zwei fordern sie alle
Es gibt so etwas, wie einen instanziierungsfreien Vergleich,
das ist auch etwas, nein,
das ist ein Beispiel dafür, wo ich Operatoren habe,
die dem System einen Teil der Funktionalität aus der Hand nehmen.
Also die Standardfunktionalität ist ja die Ulifikation, ich habe zwei Therme,
die unifiziere ich miteinander. Und wenn ich das mache,
werden sofort, und ich kann es nicht verhindern,
alle Variablen instanziert, die sich instanzieren lassen.
Und manchmal möchte man aber einfach nur gucken,
und das werden wir bei der Metaprogrammierung wieder sehen,
passen die Dinger zusammen.
Aber um Gottes Willen,
unifiziere noch nicht
Das werden wir, wenn wir Meta-Programmierung für Logikprogrammierung betreiben,
brauchen. Wir wollen noch mal gucken, ob der Kopf
der Klausel passt. Aber wenn er schon instanziert hat,
dann kann ich die, habe ich die Kontrolle darüber verloren.
Dann geht die Verarbeitung automatisch weiter. Wir wollen nur gucken,
ob er passt. Und anschließend selber in die Hand nehmen,
was mit dem Thermen und den Ausdrücken passieren soll.
Und dafür brauchen wir so etwas, es gibt dann zwei Varianten.
Das eine ist strukturelle Identität und das andere strukturelle Gleichheit,
strukturelle Identität heißt eben, sie müssen den gleichen Typ haben,
sie müssen den gleichen Funktur haben,
sie müssen die gleiche Stelligkeit haben und die gleichen Argumente haben
Und wenn denn Variablen dort aufeinander stoßen, dann heißt es,
sie müssen entweder den gleichen Namen haben oder vorher unifiziert worden sein.
Das heißt, strukturelle Identität. Und das kann man jetzt
für die verschiedenen Typen, die wir haben, durchdeklinieren,
indem wir eben zwei Konstanten sind, strukturell identisch.
Eine Variable und eine Konstante sind strukturell nicht identisch.
Wie gehen wir die Liste durch? Zwei Therme,
die keine Variablen enthalten, sind strukturell identisch.
Zwei Therme, die Variablen enthalten und die Variablen sind gleich.
Das ist diese Bedingung hier, sind auch
strukturell identisch
Therme, die Vayablen enthalten, die nicht den gleichen Namen haben und die
nicht vorher unifiziert worden sind, dass die sind nicht identisch.
Aber wenn ich sie vorher miteinander unifiziert habe,
dann sind die beiden wieder strukturell identisch. Gut,
also auf die Art und Weise kann man jetzt also,
nein, kann man hier noch nicht, das sehen wir dann im Nächsten.
Das ist die strukturelle Gleichheit. Das ist wie ein Identitätstest,
aber die Vayablen sind auch dann gleich,
wenn bei konsistenter Umbenennung die gleichen Kurreferenzbeziehungen entstehen.
Und jetzt haben wir einen Operator,
der ist so ähnlich wie die Unifikation.
Nur mit dem einen Unterschied,
dass er nicht instanziert
Also hier können, wir können jetzt so etwas testen,
wie unifizieren die beiden miteinander? Ja, sie tun es.
Aber ich habe gar nicht imifiziert. Ich habe nur getestet,
ob es klappen würde, wenn man es versuchen würde.
Okay, in gewisser Weise unterscheiden sie sich dann doch wieder hier.
Das würde unter Unifikation natürlich klappen, aber allen struktureller Gleichheit scheitert es.
Dieser Therm hier und dieser Therm scheitert auch,
weil Diko-Geverratsforderung hier nicht mit dem hier verträglich ist
Das gelingt hier aber, weil die gleiche Chor-Referenzforderung,
die hier existiert, auch hier existiert,
also Umbenennung der vajablen Würde, dann wieder auf strukturelle Identität führen.
Und hier unten haben wir keine Chor-Referenz hier und keine Chor-Referenz
hier und deshalb ist das Ganze auch erfolgreich.
Wir haben also jetzt eine Hierarchie von unterschiedlich scharfen Operatoren,
einerseits die strukturelle Identität, die die schärfsten Bedingungen stellt und
die sind sukzessive aufgeweicht bis hin zur Unifizierbarkeit.
Und da sehen wir eben hier den wesentlichen Unterschied zur Unifizierbarkeit,
Unifikation stellt die vajablen Bindung her,
die strukturelle Gleichheit tut es nicht
Gut, auch das ist im Wesentlichen Manual Wissen,
was man im Ernstfall nochmal nachschlagen muss.
Kein Mensch kann so etwas aus dem Kopf reproduzieren. Na,
es gibt Leute, kein Mensch ist falsch, ich könnte es nicht.
Wir können Strukturen erzeugen, die freie Variablen enthalten.
Also wir können erstmal testen, ob ein, nein,
sorry, wir können alle freien Variablen aus einem Therm uns generieren lassen,
indem wir einen Therm da reinwerfen und wir kriegen eine Liste aller freien Variablen daraus.
Das brauchen wir zum Beispiel, wenn wir die Verwaltung,
der war ja selber in die Hand nehmen wollten,
wenn wir es nicht mit dem System überlassen,
sondern wo wir jetzt sagen, wir machen es selbst aus bestimmten Gründen,
die können wir uns dann angucken.
Copy-Therm erzeugt eine Kopie eines Therms, der Struktur gleich ist,
aber der frische Variablen enthält, die noch nicht instanziert sind.
Das kann man sehr schön benutzen,
um mal Probeheirat zu unifizieren, mit dem kopierten Therm,
ohne dass man den ersten, den eigentlichen Thermen anfasst.
Ich kann uninstanzierte Therme erzeugen, indem ich eben sage,
konstruiere mir Nein, die zerlege erst mal,
konstruieren es hier unten, hier oben ist zerlegen.
Ich gebe dir einen Therm und sage mir bitte,
wie heißt der Funktor dieses Therms und wie heißt seine Stelligkeit.
Und das ist eben, das Ding ist einstellig und hat
den Funktor A. Und ich kann das Ding umgekehrt verwenden,
hier oben stehen wieder Fragezeichen drin. Das sind also alle
Instanzierungsvarianten möglich. Ich kann es umgekehrt verwenden und sage,
erzeuge mir bitte einen Therm mit dem Funktor A,
der zwei Argumente hat.
Und diese, das sind jetzt uninstanzierte Variablen, die hier drinstehen.
Aber ich bin auch über dieses,
dieses Prädikat in der Lage,
diesen Funktor hier
frei dynamisch zu generieren
Man kann also auf die Art und Weise beliebige Predikats nahen beispielsweise erzeugen, wenn ich das möchte.
Ich kann hingehen und einzelne Argumente instanzieren, indem ich eben hier sage,
das erste Argument in diesem Thermen soll diesen Wert bekommen.
Also ich, okay, das ist die Zerlegungsvariante,
die Konstruktionsvariante kommt dann gleich wieder, also die Zerlegungsvariante heißt,
was ist das erste Argument in diesem Therm und binde es
an die Vayable X und dann zerlegt er eben diesen Therm,
nimmt das erste Argument hier raus und ich kriege dieses Ergebnis angegeben.
Jetzt konstruktiv verwendet, baue, nein, Moment,
baue mir einen Term mit dem Funktor A und zwei Argumenten,
setze das erste Argument in diesen Termen auf die Konstante B
und setze das zweite Argument in diesem Term auf die konstante C.
Und dann kriege ich eben diesen vollständig instanzierten Termen heraus.
Das ist eine Alternative dazu zu dem, was wir vorhin gesehen haben,
nimm eine Liste und wandel sie in einen Term um.
Hier können wir jetzt auch gezielt einzelne Teile eines Terms manipulieren und
auf die gewünschten Konstanten setzen. Insbesondere eben Für die Argumente,
da können wir ja auch immer über die Infikation arbeiten,
aber über den Funktor. Das ist die Verwendungsweise,
die wir brauchen werden, wo ich in der Lage bin,
den Prädikatsnamen nach meinen Wünschen zu gestalten.
Okay, so viel zu den Typangelegenheiten und der vajablen Inspektion.
Jetzt kommt der Bereich der Suchermanipulation, oft gewünscht,
oft benutzt in den Übungen, meistens ohne Sinn und Verstand benutzt.
Jetzt kommt die Erklärung, wie man tatsächlich mit dem Cut
umgehen kann
Der Cut dient also dazu, um unerwünschte Suchpfade auszuschließen und abzuschneiden.
Und wir müssen uns angucken, wie genau funktioniert er,
welche Suchpfade müssen dran glauben, in dem Augenblick,
wo der Cut mal ausgeführt wird.
Also das ist ein spezielles nullstelliges Prädikat, das hat,
das gelingt immer, das kommt immer Truh raus,
aber es erzeugt einen Seiteneffekt und der Seiteneffekt heißt bestimmte Dinge verschwinden aus dem Suchraum.
Und das kann ich eben benutzen, um eine eindeutige Berechnung
zu erzwingen. Also beispielsweise nach dem ersten Ergebnis aufzuhören
Die Semantik ist dann diejenige und das muss man sich jetzt auf der Zunge zergehen lassen.
Es legt die Suche immer auf eine bestimmte Auswahl fest.
Und die, das ist die Auswahl, die seit der
Unifikation des Klauselkopfes entstanden ist, in der der Cut auftritt.
Also der Cut steht irgendwo im Körper einer Klausel und ich habe
bis zu dem Cut bereits eine Anzahl von Teilzielen aus abgearbeitet.
Dann wird der Cut prozessiert und der legt die Suche auf die Auswahl
fest, die seit der Unifikation des Klauselkopfes entstanden ist.
Und das hat zwei Konsequenzen. Die erste Konsequenz ist,
dass alle Klauseln, die danach kommen, nicht mehr sichtbar sind,
Und die zweite Konsequenz ist, dass die vajablen Belegungen,
die ich bei den Teilzielen bis zum Cut bereits festgelegt habe,
dass die nicht mehr da in Frage gestellt wird.
Also dort gibt es kein Backtracking mehr über die Teilziele,
die ich dort bereits abgearbeitet habe.
Das sind die beiden Wirkungen, die in einem Operator vereinigt sind.
Also, er schneidet alle Suchpfade ab,
die sich aus den alternativen Klauseln für das Betreffende Prädikat ergeben.
Also alle Klauseln, die auf die Klausel folgen,
wo der Cut abgearbeitet wird,
die verschwinden und alle alternativen Wertebindungen aus den bereits abgearbeiteten Teilzielen
links vom Cut,
die verschwinden ebenfalls
Und deshalb kann man also nicht mehr in Teilziele in Teilziele
nach Alternativen suchen, die links von dem Cut stehen.
Und alle Klauseln, die auf den Cut,
auf die Klausel mit dem Cut folgen,
die werden temporär unsichtbar bei der Abarbeitung des aktuellen Teilziels.
Sie verschwinden nicht aus der Datenbank. Sie werden nur von
der Suche nicht weiter benutzt. So, und das gucken
wir uns jetzt an einem abstrakten Beispiel mal an.
Hier ist natürlich erstmal eine Implementierung ohne Cut, damit wir den Suchraum bewundern können.
Der Suchraum ist hier also in Form eines Baumes angegeben.
Die Klauseln, die wir die wir benutzen, die stehen hier links.
Wir rufen das Ganze mit diesem Zeil auf,
A von X, und wir haben zwei Klauseln,
die etwas über Art von X sagen.
Die eine steht hier links, die andere steht rechts,
eine PD-Verzweigung. Wenn wir die Linke wählen,
dann wird hier vaiablen Korreferenz zwischen X und X1 hergestellt.
Mit dieser Bindung geht es in den Körper der Klausel.
Und da wird diese Sequenz von Teilzählen aufgerufen.
Jetzt müssen wir als erstes B abarbeiten, da gibt es
auch zwei Klauseln hier. Die erste SB von A,
Komma B. Da kriegen wir diese vaiablen Bindung hier.
Und dann müssen wir mit dieser vaiablen Bindung das zweite Teilziel abarbeiten,
also C von Y1. Und Y1 ist an B gebunden worden
Und dieser Aufruf hier, der geht jetzt wieder an die Datenbank, da scheitert,
da gelingt es mit dem ersten Fakt, dem C von B.
Und es scheitert mit dem zweiten Fakt. Und wenn wir jetzt über das Backtracking,
wir haben damit ein, sorry, ein Ergebnis gehabt,
hier über das Backtracking hierfür scheitert, wir gehen hier hoch,
wir können diese Alternative hier benutzen,
wir kommen auf diesen Aufruf und jetzt ist es genau umgekehrt,
der erste scheitert, der zweite ist erfolgreich.
Und wenn wir wieder nach Alternativen fragen,
dann landen wir hier oben bei dieser Klausel und die sagt uns jetzt,
wir müssen D von etwas aufrufen und da gibt es wieder zwei Möglichkeiten,
wie das erfolgreich ist,
NFB da mit X1 gleich D oder mit X1
Das sind also die vier Ergebnisse, die hier rauskommen können.
Also es gibt vier Möglichkeiten, diesen Aufruf wahrzumachen. Und gut,
jetzt bauen wir einen Cut ein in das ganze Spiel und
gucken uns, was passiert. Hier oben ist der Cut,
der steht also jetzt in dieser Sequenz von teilzielend, nee,
zwischen dem ersten und dem zweiten Teilziel im Körper der ersten Klausel.
Das heißt, wenn wir jetzt den linken Zweig besuchen,
dann wird als erstes dieses erste Teilziel aufgerufen
Alles ganz normal. Dieses erste Teil,
sie wird bearbeitet, wir gehen wieder hier runter.
Wenn wir jetzt dieses erste Teilziel abgearbeitet haben,
müssen wir ja in der Sequenz der Teilziele weitermachen.
Und da kommt jetzt der Cut, der wird hier abgearbeitet.
Und danach geht es mit dem C weiter.
Das C produziert hier ein Ergebnis und scheitert hier.
Der Cut hat aber bewirkt, dass alle Teilziele,
die bereits abgearbeitet worden sind, das ist dieses hier,
auf ihre vaiablen Bindung festgelegt werden.
Das heißt, diese alternative Zweig hier,
den gibt es nicht mehr
Also, er ist hier abgearbeitet worden in dieser Klausel,
das heißt, in dieses Teilziel kann ich kein Backtracking mehr machen.
Also, der Cut ist hier abgearbeitet worden,
wenn immer jetzt einer fordert, gib mir bitte die Alternative hier,
die ist nicht mehr da. Und es führt dazu,
dass alle Alternativen, alternativen Klauseln für das aktuelle Teilziel,
für den aktuellen Kopf sozusagen, der Klausel, die werden weggeschnitten.
Wir sind bei der Abarbeitung dieser Klausel und dann
wird die nächste Klausel auch mit weggeschnitten.
Das heißt, dieser ganze rechte Zweig hier ist ebenfalls verschwunden.
Wir sind auf dieses Ergebnis festgelegt
Der Witz an der Sache ist. Das heißt, in
diesem Falle muss das Ergebnis nicht eindeutig werden. Der Cut verhindert nicht,
dass von diesem Teilziel hier hinten möglicherweise mehrere Ergebnisse produziert werden.
Das ist überhaupt nicht ausgeschlossen. Alles, was er sagt,
ist, dieses Ding erzeugt kein weiteres Ergebnis mehr.
Und die zweite Klausel hier verschwindet. Aber hier könnten durchaus
jetzt noch positive Ergebnisse erzeugt werden. Also,
dass das hier scheitert, ist ein Artefakt dieses Beispiels.
Wenn jetzt hier dieser Aufruf C von C aus irgendeinem Grunde
ebenfalls ein positives Ergebnis Ups.
Halt. Wo war ich denn geblieben? Achso,
ja, der Aufruf AC von B und C von B
ist eben nur damit erfolgreich, aber nicht damit erfolgreich.
Wenn jetzt also dieser Aufruf nur etwas anders ausgesehen hätte,
dass er auch noch mit dem zweiten Fakt ein positives Ergebnis produziert,
dann hätten wir zwei Ergebnisse bekommen.
Das ist also nur in diesem Beispiel so.
Also hier unterhalb alles, was auf den Cut folgt,
da können durchaus mehrere Ergebnisse
produziert werden
Bei allem, was den Cut vorangeht, da verschwinden die Alternativen.
So, nun gucken wir uns an, wofür brauchen wir
so etwas? Es gibt im Prinzip zwei Verwendungsweisen,
das eine nennt sich der grüne Cut,
der wird immer dann benutzt, wenn ich ohnehin hinweise,
dass es sich um einen eindeutigen Kontext handelt.
Es kann nur ein Ergebnis geben und ich brauche nicht nach
einem zweiten Ergebnis zu suchen, weil es keins geben kann.
Dann führt das oftmals dazu,
dass sich durch den Cut eine Effizienzsteigerung erreichen kann,
weil ich, nachdem ich das erste positive Ergebnis gefunden habe,
aufhören kann mit der Verarbeitung.
Also beispielsweise in einer sortierten Liste kann ich aufhören, ich,
an der Position vorbeigekommen bin, wo nur noch größere Elemente stehen.
Dann weiß ich, das gesuchte Element kann in der Restliste
nicht mehr vorkommen. Das ist eine Möglichkeit, wo man
durch den Greencut verhindern kann, dass unnötiger Suchaufwand entsteht.
Interessanter sind die Redcut-Fälle, das ist immer dann,
wenn ich das Verhalten des Prädikats verändere.
Also im ersten Fall ändert sich an der Semantik des zugrunde
liegenden Prädikats überhaupt nichts. Einzig und allein der Nachweis,
es gibt kein weiteres Ergebnis mehr,
den spare ich mir oder den kürze ich ab.
Dort entsteht der Effizienzgewinn. Das Verhalten des Prädikats ist
vollkommen unverändert. Im zweiten Fall bei dem Redcut ändert sich
das Verhalten des Programms
Da habe ich eben jetzt eine mehrdeutige Relation, die ich in eine funktionale Umwandle.
Und das heißt immer, dass die Programmsemantik sich ändert,
dass man also mit Effekten rechnen muss,
die möglicherweise nicht im beabsichtigten Sinne waren.
Also hier ein Beispiel für einen Greencut. Ich möchte,
ganz einfaches Beispiel, ich möchte das Maximum zwischen zwei Zahlen ermitteln,
ein dreistelliges Prädikat, was zwei Zahlen geliefert bekommt,
da steht ein Plus davor.
Und was mir das Maximum zurückliefern sollte
Und das ist hier mit Guards gemacht,
also wann immer X größer ist als Y, ist X,
der gesuchtes gesuchte Maximum, wann immer Y größer ist als X,
ist Y das größte Uptemaximum.
Wir wissen jetzt aufgrund unserer Problemkenntnis, dass wann immer die erste Klausel scheitert,
die zweite Klausel, die das richtige Ergebnis liefert.
Und dann, oder umgekehrt, der interessante Fall ist natürlich umgekehrt,
wenn die erste Klausel erfolgreich ist,
wenn die mir bereits das richtige Ergebnis liefert,
dann brauche ich die zweite Klausel gar nicht mehr anzufassen.
Das ist der interessante Fall. Deshalb kann ich,
wenn die erste Klausel erfolgreich ist, einen Cut setzen.
Das heißt, wenn ich diese Bedingung einmal erfolgreich abgearbeitet habe,
wird es keine weitere Klausel mehr geben,
die auch noch eine erfolgreiche Arbeitung liefert,
weil eben dieser Operator und dieser Operator des Jung zueinander sind.
Die können nicht gleichzeitig wahr sein. Und dann spare ich mir eben
diesen zweiten Aufruf hier, der jetzt auch nochmal testen muss,
ob die Größenrelation in der umgekehrten Reichtum vorliegt.
So nun könnte man ja kühn werden und sagen,
okay, brauche ich diesen Guard hier denn überhaupt noch?
Kann ich den nicht
auch einsparen
Ich weiß ja hier, diese Klausel hat mir bereits das Ergebnis geliefert.
Die zweite wird das nicht tun. Im umgekehrten Fall,
das ist der, den ich vorhin zuerst diskutiert habe.
Im umgekehrten Fall weiß ich, das Ding hier scheitert,
dann weiß ich doch, Y muss das Maximum sein.
Muss ich dann extra noch mal testen, ob Y
größer X ist? Oder kann ich nicht davon ausgehen?
Ja, es muss es auf jeden Fall sein.
Ja, ich denke, das ist hier schwierig,
würde die Berufsbarkeit
auch sein
Dann ist es trotzdem das Maximum. Das ist auch Gleichheit,
ich will ja nur das Maximum messen,
ob die beide gleich sind, ist mir egal.
Hauptsache, ich kriege die maximale Zahl raus.
So ist ja die ganze Definition hier. Hier oben steckt ja
das gleiche Zeichen auch drin. Wenn die gleich sind,
dann ist die zweite Klausel ja sowieso einschlägig. Ja,
es könnte natürlich ein Problem bei Nicht-Typgerechnung auch Buch geben,
wenn man keine Zahlen dann reinsteckt,
aber in dem Fall würde er wahrscheinlich den Error niemanden,
der von der ersten 3000 geredet ist
Gut, also mit Verletzung von Zusicherungen kann man natürlich immer
alle Effekte erzielen. Im Prinzip machen wir jetzt nichts anderes.
Wir gucken uns mal das Beispiel an. Also wir lassen jetzt
hier den Guard weg und lassen das Prinzip Hoffnung walten.
Es wird schon alles gut gehen. Das ist auch tatsächlich
der Fall. Wenn wir also hier das mit einem Klassischen Beispiel,
dieses instanziiert, das dritte nicht instanziiert aufrufen,
dann kriegen wir tatsächlich X, wird an 3 gebunden,
Y wird an 2 gebunden, X ist größer als Y,
also wird X als Ergebnis ausgegeben und dann wird der Cut prozessiert und wir sind fertig.
Umgekehrt, ach so, nein, nicht umgekehrt,
wir instanzieren das Ganze jetzt und fragen, gibt mir das Maximum,
ist das Maximum von 3 und 2, 3,
das geht auch glatt aus den gleichen Gründen,
X und Y werden instanziiert,
glücklicherweise aber ist erste und die dritte Position richtig besetzt.
Deshalb gelingt diese Choreferenz. Ich kann das aber zum Scheitern bringen.
Und jetzt kommt der spannende Punkt, wenn ich das aufrufe,
was ja durchaus ein legitimer Aufruf ist,
der nicht mal irgendeine Zusicherung verletzt, sondern der nur ein Beispiel bringt,
wo ich eigentlich False erwarte.
Gib mir das Maximum von 3 und 2 oder ist das
Maximum von 3 und 22, da würde ich hier False erweist, Martin.
Was passiert? Die Unifikation im Klauselkopf scheitert bereits,
weil diese Choreferenz hier nicht erfüllt ist.
Das heißt, er geht automatisch zum 2 zur zweiten Klausel
und gibt mir das Y als Maximum aus
Ja, also, der Witz ist, dass hier im Klauselkopf
bereits eine Co-Referenzforderung gestellt wird und nicht erst im Klausel-Körper.
Hier an dieser Stelle ist das,
wo ich in den Übungen immer mit Engelszungen dafür plädiere,
nutzen Sie die Unifikation im Klausel-Kopf.
Hier wendet sie sich gegen uns. Aber es ist nicht
so sehr die Unifikation, die sich gegen uns wendet,
sondern wir haben unsauber programmiert.
Und an solchen Stellen kann man tatsächlich Effekte erzeugen,
die man nicht mehr so einfach übersieht.
Dass man zwei Ideen zusammenbringt. Einerseits,
ich teste einen Teil der Bedingungen hier im Klausel-Kopf ab und
ich teste einen anderen Teil der Bedingungen im Klausel-Körper ab
Und es kann bereit sein, dass das scheitert,
weil es im Klausel-Kopf scheitert und dann kommt es unkontrolliert in
die zweite Klausel und gibt uns ein falsches Ergebnis aus.
Also das ist etwas, wo wir, ja,
und das ist schon ein schönes Zeichen.
Wir hatten ja bisher immer die, so intuitiv mitgekriegt,
das Testen auf konstanten, macht uns ja eigentlich kaum Probleme.
Also für die nachzuweisen, dass das vollständig instanziiert,
das richtige Verhalten zeigt,
das ist immer
relativ einfach möglich
Gefahren, also so etwas wie Terminierungsprobleme, die lauerten immer dann,
wenn wir bestimmte Instanzierungsvarianten mit Variablen hatten. Wenn wir also Informationen,
die gebraucht wurde für die Terminierung, nicht mit hinzugeliefert haben.
Und hier liefern wir jetzt alle Informationen, die wir haben.
Es ist alles instanziert, mehr haben wir nicht.
Mir können wir gar nicht zur Verfügung stellen und trotzdem,
und unter diesen Bedingungen kommt ein falsches Ergebnis heraus,
während der Uninstanzierung oder partiell und instanzierter Aufruf sich richtig verhält.
Hier ist plötzlich die Welt auf den Kopf gestellt und
das ist genau das, was der Cut bewirkt.
Das gucken wir also weitere noch an. Der führt nämlich
ein neues Ein neues Ableitungsschema sozusagen ein, in die Logikverarbeitung,
wo wir von dem bisher beschrittenen Weg des monotonen Schließens abweichen zum Nichtmonoton schließen.
Und das ist nicht verwunderlich, das sagt der Name Cut schon,
ich nehme Ergebnisse weg. Und das schlägt hier bei uns
durch. Also wir müssten hier tatsächlich jetzt sagen,
wenn wir das richtig beschreiben wollen, das Maximum darf nicht instanziert sein.
Deshalb, das ist der Grund,
weshalb ich Anders als im Menuell zur SWI-Prolog bisher immer hier ein Fragezeichen verwendet habe,
wenn es egal war, ob es instanziert oder uninstanziert wird.
Im Menuell steht an dieser Stelle meistens ein Minus.
Aber wenn ich dieses Minus hierfür schon verwende,
kann ich es hierfür nicht mehr verwenden.
Hier ist es jetzt tatsächlich so, dass das Instanziertsein einen
Fehler erzeugt. Und das muss ich ausschließen. Gut,
also die Variante sollte man lieber nicht verwenden.
Andere Stellen, wo wir mit dem Redcut arbeiten,
wo wir aber möglicherweise seine Wirkung haben wollen, gibt es auch.
Nicht immer ist der rote Cut ein schlechter Cut.
Es gibt tatsächlich sinnvolle Verwendungsweisen und das ist eben immer dann,
wenn ich garantiert eindeutige Ergebnisse habe, das Jahr geht zu Ende,
meine Batterien gehen auch zu Ende.
Wenn ich ein garantiert eindeutiges Ergebnis habe, zum Beispiel bei der Frage der Mutter,
es kann nur eine Mutter geben, wenn die Datenbank einigermaßen korrekt ist.
Das heißt, wenn ich eine Mutter gefunden habe,
kann ich die Suche abbrechen,
eine aussichtslose Suche nach anderen Müttern wird dann nicht mehr durchgeführt,
aber der Preis ist immer ein relativ hoher,
das Ganze macht das wirklich, in diesem Fall geht der relationale Charakter verloren.
Ich bin nicht mehr in der Lage, Mütter aufzuzählen.
Also ich bin nicht mehr in der Lage,
jetzt zu fragen, gib mir bitte alle Mütter.
Gut, in anderen Fällen heißt das, dass,
also wenn ich zwei Argumente habe, ich kann durch den Cut auch nicht sicherstellen,
dass Eindeutigkeit in einem Argument hergestellt wird.
Der Cut stellt immer Eindeutigkeit in beiden Argumenten her oder in mehreren Argumenten her,
weil er sich auf die erste Lösung festlegt nicht auf die möglicherweise das gewünschte Verhalten in einem Argument.
Also was hier passiert ist eben, dass ich dann alternative Berechnungsergebnisse verliere,
wenn ich diese zweistellige Prädikatmutter vonnehme, wie wäre das üblicherweise,
verwendet haben und nach der ersten Mutter wird abgebrochen,
dann kann ich eben hier noch die, wenn ich frage,
wer ist die Mutter von Susi?
Nein, Quatsch, von wem ist Susi die Mutter?
Dann finde ich noch das Ergebnis mit dem ersten Fakt hier.
Danach wird der Cut irgendwann prozessiert. Und damit fällt die
zweite Klausel hier weg. Er ist festgelegt auf die Auswahl,
die bei dem ersten Elternteilaufruf passiert ist.
Und das ist eben die Festlegung auf Habens.
Und wenn jetzt ein Backtracking passiert und ich weitere Ergebnisse sehen will,
dann kann er in diesem Aufruf nicht erneut betreten,
dass Ritai es weggestrichen worden und ein weiteres Ergebnis gibt es nicht,
obwohl die Datenbank eben nahelegen würde,
ja auch Anna ist eben Tochter von Susi oder andersherum Susi ist die Mutter von Anna.
Also da gehen dann Berechnungsergebnisse verloren in beiden Richtungen.
Ich möchte eigentlich, dass es nur eindeutig im ersten Argument
ist, Aber das kann ich nicht spezifizieren.
Wenn ich den Cut setze, macht er es in beiden Argumenten eindeutig.
Dann hat eben Susi nur noch ein Kind und das Gewünschte
natürlich, Hans hat nur noch eine Mutter.
Das ist gesichert. Aber in der anderen Richtung geht das Ergebnis verloren. Das ist,
insofern ist der Cut ein sehr grobes Mittel,
wenn man das feiner haben wollte, müsste man wieder auf das Hilfsmittel zurückgreifen,
variablen Instanzierung überprüfen und für alle Instanzierungsvarianten separate Implementationen zur Verfügung stellen.
Der klassische Fall, wo wir den Cut brauchen und zwar dringend brauchen,
ist, dass die vor Ort schließen,
Die fortschließen heißt immer, ich habe eine Standardannahme,
die gültig ist, die gilt in der Welt.
Und es gibt Ausnahmen dazu. Und die Ausnahmen möchte ich immer nur dann,
oder die Ausnahmen möchte ich immer zur Rate ziehen,
wenn die Ausnahme gegeben ist.
Und wenn keiner der Ausnahmen zutrifft, dann möchte ich auf den Standardfall zurückgreifen.
Ich habe das hier mal an einem biologischen Beispiel gemacht
und möchte die Beinanzahl ermitteln. Böse Zungen behaupten ja,
da sei die Hauptbeschäftigung von Biologen, dass sie Käferbeinchen zählen.
Also nehmen wir bei verschiedenen Spezies her,
also verschiedene Tiergruppen und charakterisieren deren Beinanzahl.
Wir müssen uns einigen, was der Standard sein sollte.
Und ich habe natürlich die Gruppe genommen, Lebewesengruppe genommen,
die die meisten Arten hat. Das sind die Insekten.
Und Insekten haben immer sechs Beine. Das heißt also,
die Standardannahme ist, alles hat sechs Beine.
Es sei denn, dass irgendeine Ausnahme gilt. Und die
Ausnahmen sind eben hier, wenn es ein Säugetier ist,
dann hat es nur vier Beine.
Oh hups, der Mensch ist auch ein Säugetier,
also müssten wir wieder eine Ausnahme einbauen.
Es sei denn, es ist ein Homosapiens,
dann hat er
nur zwei Beine
Ich weiß gar nicht, wie man,
wahrscheinlich müsste man Primaten sagen. Ich nehme mal an,
bei den Affen redet man auch von armen Beinen.
Ja, dann, oh gut, einnehmen wir uns also auf Primaten.
Loche etwa haben vier Beine. Aber natürlich auch nicht ausnahmslos
. Also die Blindschleiche ist ein Loch, aber hat leider
keine Beine. Das heißt, sie hat natürlich beide,
nur sieht man sie von außen nicht.
Also da muss man sehr ins Kleingedruckte gehen.
Spinnen etwa haben acht Beine, Vögel, zwei Beine.
Da gibt es, glaube ich, keine Ausnahmen dazu.
Aber immerhin, all das sind Ausnahmen zu der Standardannahme,
was immer da kreucht und fleucht.
Es hat sechs Beine, Beine
Wenn ich das so mache, kriege ich natürlich jede Menge unsinnige Ergebnisse raus.
Also wenn ich frage, wie viele Beine hat denn die Spinne,
dann kriege ich acht raus. Und wenn ich nach Alternativen frage,
kommt dann natürlich immer ein Standardfall hinein und sagt,
ja, sechs Beine könnte sie auch haben.
Ja, geht ja auch. Es soll ja Spinnen mit sechs Beinen geben,
wenn böse Kinder kommen und so weiter. Ja,
wenn ich dann frage, was hat denn alles vier Beine, dann,
das ist tatsächlich richtig, die Loge und die Säugetiere,
die haben vier Beine.
Das ist richtig, aber was unschön ist,
ist dieses Ergebnis hier oben,
was nicht das Intendierte ist
Also machen wir die ersten Klauseln jetzt tatsächlich zu,
ach hier habe ich die Primaten sogar dabei.
Interessant. Machen wir das zur Ausnahme, das können wir
ganz einfach dadurch erreichen, indem wir hier immer einen Cut hinsetzen.
Und dann sagen, ja, Vögel haben zwei Beine.
Und wenn du diese Klausel benutzt hast,
prozessiere den Cut und alle anderen verschwinden aus der Betrachtung.
Gut, die ersten interessieren hier nicht,
aber insbesondere verschwindet auch diese Standardannahme hier unten aus der Betrachtung.
Und das gilt für jedes einzelne Ding.
Wir gehen immer von oben nach unten durch. Und
wenn wir denn jetzt die Loche hier am Wickel haben,
dann legen wir uns auf diese variablen Bindung fest und alle
anderen Alternativklauseln
Verschwinden damit. Und jetzt kommt tatsächlich raus,
wie viele Beine hat die Spinne und dann kommen eben acht
raus und wir sind fertig. Und wie gibt es,
das ist wieder der Preis dafür, welche Tiere haben vier Beine,
dann lege ich mich eben auch wieder.
Das ist das, was ich vorhin diskutiert habe,
auch wieder auf die erste Auswahl fest und das sind die Lorche.
Alle anderen Klauseln werden weggeschnitten und die Säugetiere sind verschwunden.
Also auch hier ein roter Cut, wir machen das insgesamt eindeutig.
Also das Gesamte hat Beineprädikat, machen wir eindeutig in allen Instanzierungsrichtungen.
Die erste hätten wir gern,
die zweite ist ein unangenehmer Nebeneffekt,
der mit erzeugt wird
Hier geht also auch wieder eine Instanzierungsvariante verloren,
aber das ist der Preis, den wir dafür zahlen müssen,
dass wir eben überhaupt so etwas wie Default schließen einbauen können.
Und die Fortschließen ist eben etwas,
was im täglichen Leben eine unglaublich wichtige Rolle spielt,
wenn wir nicht in der Lage wären,
Standardschlüsse zu ziehen, wären wir nicht lebenstüchtig.
Das ist also in jeder Hinsicht, machen wir Standardannahmen von der Art,
wie ich werde nicht tot umfallen, wenn ich jetzt drei Schritte weitergehe.
Wenn ich solche Annahmen nicht machen würde,
könnte ich die elementarsten Verrichtungen nicht mehr machen,
weil ich immer mit allem rechnen müsste.
Und das muss ich nicht, weil ich eben davon ausgehe,
dass es Standardsetzungen in der Welt gibt,
mit denen
ich leben kann
Zum Beispiel, dass ein Auto Räder hat und dass
ein Tisch Beine hat und alles mögliche.
Dass sich Licht an- und ausschalten kann, alles in Standardannahmen,
die wir ständig machen und bitte werden wir ganz selbstverständlich umgehen.
Hier, das ist der Weg, wie man es mit einfachen Mitteln nachempfinden kann.
Und das ist tatsächlich etwas, wo ich ohne den Cut nicht weiterkomme.
Da brauche ich eben. Dass die fortschließen und jetzt können
wir nochmal die Brücke bauen zum allgemeinen Bereich der Wissensrepräsentation,
dass sie fortschließen, ist ein Spezialfall, ein wichtiger Spezialfall des Nicht-Monotonenschließens.
Wir haben bisher immer nur monotone Schließen betrachtet, wir uns nicht Monotonie an.
Und Monotonie heißt immer, wenn ich zu einer Theorie,
eine Theorie ist eine Menge von Klauseln,
neue Axiome hinzufüge, neue Klauseln hinzufüge,
dann ähnen wir keine Ableitungsmöglichkeiten, keine Theorien verloren.
Das hat eigentlich keine Schlussfolgerung geben verloren. Ich kann alles,
was ich vorher aus der Theorie schließen konnte,
kann ich auch nach Erweiterung der Theorie immer noch schließen.
Möglicherweise mehr, aber niemals weniger.
Das ist das, was wir bisher gemacht haben.
Wenn wir eine neue Klausel hinzugefügt haben, dann hat uns
diese neue Klausel zusätzliche Ergebnisse produziert.
Aber sie hat nie
frühere Ergebnisse weggenommen
Und genau das geht jetzt verloren. Durch den Cut kann es passieren,
dass Ergebnisse, die wir vorher gesehen haben,
nach hinzufügen, einen neuen Klausel nicht mehr zur Verfügung stehen.
Also jetzt neue Aktionen hinzugenommen, kann dazu führen,
dass sich die Zahl der ableitbaren Theorien verringert.
Also nicht nur die Zahl, sondern die Menge auch.
Das heißt, dann gehen Schlussfolgerungen tatsächlich verloren,
was manchmal gewünscht ist, aber nicht immer gewünscht ist.
Also man kann sich das hier angucken,
wenn wir denn diese Regelmenge mit den Beinen wieder hernehmen,
Gucken uns erst den Fall der Monotonie an,
also ohne Cut, dann den Fall der Lichtmonotonie.
Wir haben eine einfache Datenbank, die sagt eben nichts anderes als dieser Default-Klausel.
Was immer da ist, es hat sechs Beine.
Und wir können dann eben fragen, wie viele Beine hat
ein Regenwurm und wir kriegen die Antwort sechs.
Und jetzt nehmen wir zu dieser Theorie,
die wir hier oben haben, eine neue Klausel hinzu.
Regenbom hat null Beine, weil uns dieses Ergebnis nicht gefällt.
Und wir kriegen ab sofort zusätzliche Ergebnisse.
Also zu dem Ergebnis B gleich 6,
was wir bisher auch schon hatten,
kriegen wir jetzt noch das Ergebnis B gleich null hinzu
Das ist Monotonie. Wir haben unsere Datenbank erweitert,
von einer Klausel auf zwei Klauseln und die Menge der Ableitungen
geht von einer auf zwei hoch.
Das Monotonie, wenn das eine, das ist genau wie
in der Analysis auch, was ist eine monotone Funktion?
Eine monotone Funktion ist eine, die mit steigendem X-Wert zumindest nicht kleiner wird.
Also auch so etwas ist eine monotone Funktion,
aber sie darf auf keinen Fall nach unten gehen.
Dann wäre es eine nicht monotone Funktion. Und genau das
haben wir hier, wenn wir also neue Aktionen hinzunehmen,
dann steigt die Anzahl der Schlussfolgerungen,
die wir ziehen können,
an,
aber sie wird auf keinen Fall kleiner
Daher kommt der Name. Das ist also
das gleiche abstrakte Konzept, was dahinter steht.
Jetzt machen wir das nicht monoton. Wir haben also wieder
die einfache Datenbank mit einer Klausel drin und kriegen dieses eine Ergebnis.
Jetzt fügen wir eine Klausel mit einem Cut hinzu und deshalb
haben wir nicht monotonisch schließen und dann sehen wir plötzlich dieses Ergebnis,
was wir vorher hatten, das kriegen wir jetzt nicht mehr.
Hier ist also ein Ergebnis weggefallen, was vorher mal möglich war,
das steht jetzt nicht mehr zur Verfügung.
Also hier sind echt
Ergebnisse entfalten
Gut, soviel zur Frage der Suchraummanipulation. Spieler, was ist denn? Okay,
Datenbankmanipulation hatten wir schon mal ganz zu Anfang uns angeguckt.
Es ist in der Übung auch teilweise gern benutzt worden,
obwohl ich hälftig davor gewarnt habe. Also zumindest das Assertant,
das Assert A, das Assert Z, das sollte ihn bekannt vorkommen.
Das Consult sollte ihnen auch bekannt vorkommen,
auch wenn wir es meistens in dieser Kurzform mit Ecke,
Klammer auf, Ecke,
Klammer zu benutzt haben,
ist es nichts anderes als dieses Einlesen einer Datei
Es gibt immer noch die Spezialformen,
dass man sich den Pointer auf einen Therm mit ausgeben kann,
wenn man in die Datenbank einträgt.
Das ist aber für Leute, die ins Innere des Systems eingreifen wollen,
eher geeignet. Nur, dass es sich nicht wundern,
sowas taucht im Manual immer auf, dass man die Normale,
die einfache Variante für den normalen Nutzer zur Verfügung stellt.
Und dann gibt es noch die Power-Nutzer-Variante,
wo man jetzt tatsächlich auch den Speicherort mitgeteilt bekommt,
wo das Ding dann im Speicher steht
Man kann natürlich auch Klauseln aus der Datenbank wieder entfernen,
indem man etwa ganz gezielt vorgeht und funkte und Stelligkeit angibt,
was beseitigt werden soll. Oder aber,
dass man einen Retract angibt und hier ein Term angibt,
der mit dem Kopf udifiziert wird und wo immer diese Unifikation gelingt,
wird das entsprechende Klausel gelöscht, wobei Retract All alle Klauseln entfernt,
wo die Unifikation gelingt und Retract nur die erste entfernt,
wo die Unifikation gelingt.
Was wir auch dringend brauchen werden,
das ist jetzt der Teil, wo wir,
das ist nichts anderes als die Suche in der Datenbank
Die verwirbt sich hinter diesem simplen Prädikat. Claus,
gib mir, ich gebe dir einen Kopf und du verrätst
mir den Körper, der dazugehört.
Wenn wir also als nächstes die Abarbeitung von Prolo-Klauseln selber in
die Hand nehmen werden, werden wir diesen Aufruf brauchen.
Wir wollen ein Ziel ermitteln. Wir wollen aber nicht,
dass das Prolox-System etwas mit dem Körper macht.
Das wollen wir selber entscheiden. Zum Beispiel wollen wir sagen,
protokolliere, was du da machst.
Edos machst. Oder aber mache Breitensuche statt Tiefensuche.
Dann dürfen wir nicht standardmäßig die Klauseln abarbeiten,
sondern wie es fragen war,
gib mir alle Klauseln,
die auf diesen Kopf passen
Und wenn er eben fünf Ergebnisse liefert, wir machen ein Find All darüber,
dann wissen wir, jetzt verzweigt unser Suchraum fünffach.
Und wir können diese fünf Alternativen in die Warteschlange passen,
packen und dann eine Breitensuche darüber machen.
Also das werden wir brauchen. Gibt auch noch die Variante,
dass man dann sich eine bestimmte Klausel raussucht,
aber das werden wir nicht brauchen, den Klaus-Aufruf werden wir auf jeden Fall benötigen.
Es gibt auch so etwas, was man nicht denkt,
ja, es ist nicht nur,
dass wir über Asshört irgendwas in die Datenbank schreiben können,
die meisten Produktsysteme haben auch so etwas wie globale Variablen und
die werden als Flag bezeichnet und haben immer einen Namen,
einen eindeutigen und zwei systemweit eindeutigen Namen
Und erlauben dann so etwas wie Zähl, globales Zählen,
indem ich immer hier eine alte Intertatzahl angebe und eine neue Editor-Zahl eingebe.
Und dann ist eben so ein Aufruf hier,
wenn das N noch uninstanziert ist,
dann instanziert er es mit null oder initialisiert es mit null
und jeder weitere Aufruf von diesem gleichen Ziel, sorry,
hier sollte vielleicht noch ein Punkt hin,
wenn sie sich dem mitdenken können, das wäre nett.
Jeder weitere Aufruf dieses gleichen Ziels erhöht dann diese globale Variable
um eins
Und das kann man auch für nicht numerische Atome verwenden.
Also das ist hier nur die Verwendungsweise zum Zählen.
Das geht auch für, wenn ich irgendwie nicht numerische Informationen speichern möchte.
Aber es muss immer atomare Informationen sein. Das ist eine klassische Variable,
die keine Strukturen abspeichern kann, sondern nur einfache atomare Datentypen.
Gut, Prädikate höherer Ordnung, Prädikate höherer Ordnung, alle Freunde,
der funktionellen Programmierung wissen, dass Funktionen höhere Ordnung sind.
Das sind Funktionen, andere Funktionen als ein Argumentstellen entgegennehmen können,
die andere, die Funktionen als Berechnungsergebnis produzieren können.
Und ich habe dir diesen Namen gebeugt und obwohl in der Logikprogrammierung das
nicht in der gleichen Schönheit möglich ist wie in der funktionalen Programmierung.
Da hat die funktionale Programmierung tatsächlich ein großes Plus.
Was eben daran liegt, dass das Berechnungsergebnis immer eindeutig gegeben
ist und keine Betrachtung von Alternativen stattfindet
Prädikate höherer Ordnung sind also solche,
die zum Beispiel ein Ziel nehmen und dieses Ziel abarbeiten.
Oder aber ein Prädikat generieren können.
Und der schöne Effekt der funktionalen Programmierung ist ja,
dass sich ein Prädikat dynamisch bauen kann und sofort auswerten kann.
Dass ich dieses, habe ich Prädikat gesagt,
eine Funktion bauen kann und die sofort auswerten kann.
Und dass sich diese Funktion ja nicht mal einen Namen geben muss.
Sie kann anonym sein, sie kann Lamda,
irgendetwas heißen und ich schicke sofort diesen Lamdatherm wieder in
die Auswertung und ich kriege einen Funktionswert zurück
Das geht hier leider nicht so schön,
anonyme Prädikate haben wir nicht, die gibt es nicht.
Wir müssen jeder Prädikatsdefinition erst in die Datenbank eintragen, eversiv anwenden können.
Das ist der große Unterschied zur funktionalen Programmierung. In der funktionalen Programmierung
haben wir die Anonymen, also die Lamdad-Ausdrücke, unsere Pädikate,
hier müssen immer einen Namen haben und sie müssen in der Datenbank drinstehen.
Wenn ich denn ein Prädikat generieren will,
ich kann natürlich jederzeit Prädikatsaufrufe generieren und abarbeiten,
das geht, das ist überhaupt kein Problem,
aber Prädikatsdefinitionen müssen in die Datenbank eingetragen werden,
eh sie
verwendet werden können
Das ist also Prädikate, höherer Ordnung.
Die fangen also relativ simpel an. Allein die Tatsache,
ein Prädikat aufrufen zu können,
gezielt ist bereits ein Prädikat höherer Ordnung,
weil es ein anderes Prädikat hernimmt und in irgendeiner Weise in die Ableitung einbezieht.
Wir gucken uns dann Steuerstrukturen an, Steuerstrukturen,
sowas wie If and Ells und Not und dergleichen mehr.
Wir gucken uns die Resultatsaggregation an und dann auch noch sowas wie Konsistenzforderung.
Eva dann tatsächlich zu dem Punkt kommen, wo wir hinwollen,
nämlich die Metaprogrammierung. Das ist immer noch das große Ziel,
dass wir uns angucken,
wie können wir nicht nur die Syntax des Prologsystems verändern,
sondern wie können wir
seine Semantik verändern
Du also, Prädikate, höhere Ordnung, erlauben eben Prädikate,
Aufrufe als Argumentbelegungen und dann eben entsprechende Aktivierung dieser Aufrufe als Teilziel und können Prädikatsdefinitionen erzeugen,
die müssen aber in die Datenbank eingetragen werden.
Das habe ich schon diskutiert. Und das ist auch ein Problem,
während ich eine Funktionsdefinition,
ein lammnder Ausdruck in der funktionalen Programmierung immer als solchen erzeugen kann,
kann ich hier nur einzelne Klauseln erzeugen.
Klauseln sind aber immer nur Bausteine, aus denen sich ein Prädikat zusammensetzt.
Das heißt also hier die Prädikatsdefinition Passiert also auch Stück für
Stück durch Einzelklausel, nicht durch eine komplexe Definition.
Da unterscheiden sich die Paradigmen eben wesentlich. Gut,
dass da oben stand schon auf der vorhergehenden Folie,
das sind jetzt die Dinge, die wir im Einzelnen durchdeklinieren.
Also, der einfachste Fall, Prädikatsaufruf ist ein einstelliges Prädikat namens Cole.
Das nimmt eine Struktur und ruft diese Struktur als Teilziel auf.
Und Semantik ist dann eben, starte die Suche mit diesem,
mit dieser Struktur als Ziel. Und der interessante Fall ist natürlich,
dass die Struktur freie Variablen enthält, damit ich die Werdebindung
für die freien Variablen Ermittler und als Ergebnis präsentiert bekomme.
Und das brauche ich eben insbesondere dann, wenn ich
dynamisch Aufrufe erzeugt habe und diese dann tatsächlich ausführen will.
Oder aber eben auch zur verzögerten Aktivierung von Zeil zielen,
dass ich eben dem Prolog-System die unmittelbare Aktivierung eines Teils jetzt aus der Hand nehme und sage,
stopp, stopp, stopp, stopp, ich möchte erst mal gucken, was hier genau passiert.
Und entscheiden, ob ich diese Ausführung wirklich machen will.
Einfachster Fall, den wenn man es, oh nein,
ein interessanter Fall ist zum Beispiel,
möchte ich ein Teilziel überhaupt aufrufen,
wenn ich weiß,
dass es in einen Zyklus führt
Dann kann ich zum Beispiel dem System aus der Hand nehmen,
aktiviere dieses Ziel überhaupt. Mir fällt jetzt gerade was
ganz Schreckliches auf. Das ist, dass ich eigentlich,
das habe ich vorhin über der Kerze völlig vergessen,
dass ich eigentlich, wenn ich heute vorbereitet habe darauf,
dass wir die letzte Veranstaltung vor Weihnachten haben.
Und deshalb ein paar Sachen zur geistigen Nahrung,
machen Sie es mal auf und reifen Sie es durch,
bereitgehalten habe, sorgen Sie mal dafür,
dass das ein bisschen verteilt wird.
Das sollte eigentlich zur Stimmungsaufhellung beitragen.
Die Herren an der Technik will ich natürlich nicht vergessen
Das ist mir völlig entfallen, dass die Hüte noch da unten stand.
Gut, ein... Ja, was ich jetzt,
also, es gibt dann die sogenannte Meta-Call-Facility,
das haben wir schon auch beim Testen von Prädikatsaufrufen mal gesehen,
dass wenn ich ein Teilziel im Körper einer,
eine Variable im Körper einer Klausel habe,
wird der Wert dieser Variablen als neues Teilziel in die Suche gesteckt.
Also wenn ich eine Prädikatsdefinition dieser Art mache,
My Call, habe ich die jetzt genannt,
und im Körper der Klausel steht diese Variable,
dann wird der Wert dieser Variablen in die Suche geschickt.
Und auf die Art und Weise kann ich jetzt eben so etwas machen,
wie rufe mir bitte dieses Teilziel auf und alles andere ist
dann wieder ganz normale Prolog-Abarbeitung.
Auf diese Art und Weise haben wir den ersten Meterinterpreter geschrieben.
Jetzt rufen wir nicht mehr direkt das Prolog-System auf,
sondern erstmal dieses Prädikat My Call
Und jetzt können wir, ehe wir dieses P hier reinschreiben,
davor beliebige Manipulationen vornehmen. Wir können das Teilziel verändern,
wir können Protokollinformationen ausgeben oder was auch immer.
Also diese Struktur, so einfach, wie sie aussieht,
ist der erste Meta-Interpreter, den wir sehen,
und das werden wir dann weiter ausbauen, Schritt für Schritt.
Ja, und auf diese Art und Weise sind jetzt ganze Menge syntaktische Elemente definiert,
die man standardmäßig benutzen kann, zum Beispiel die Disjunktion.
Die Disjunktion ist nämlich nichts anderes als diese Definition, die hier steht.
Ich bin eine Struktur, wo der Funkteuer Pole und heißt,
mit einem linken Teil und einem rechten Teil.
Und die erste Alternative heißt, arbeitet den linken Teil ab.
Und wenn du ins Backtracking gezwungen wirst,
dann gehe ich zur zweiten Klausel und die sagt,
arbeite den rechten Teil ab. Und an der Stelle sehen wir,
dass die Dissuktion eigentlich gar kein echtes Sprachelement von Prolog ist,
sondern dass es etwas ist,
was sich auf der Basis der normalen Klauselstrukturen und der normalen
Abarbeitung und alternativen Betrachtung von Prolog ableiten kann.
Das ist eigentlich etwas, was schon definiert ist,
auf den Basiskonstrukten
Aber was wir hier sehen, ist eben genau diese Metacall Facility.
Wir haben hier nur immer die Bajable im Körper der Klausel.
Und die sagt uns, welches dieser Teilziele hier dann tatsächlich
abgearbeitet werden soll. Legation ist eigentlich auch nicht viel anderes.
Negation ist hier definiert. Also das hatten wir hier schon,
entweder dieses Not, dieses Backslash Plus oder dieses Not,
das Not ist die alte Variante, das Backslash Plus ist die neue Variante.
Beide sind auch als Prefix-Operator zu benutzen.
Und die Definition ist hier, arbeitet dieses,
teilt sie ab
Wenn du, wenn das gelingt, dann prozessiere den Cut und scheitere.
Das heißt also, wann immer das Abarbeiten dieses Zeitzieles gelingt,
kommt vor Orts heraus. Und gleichzeitig wird der Cut prozessiert
. Das heißt, diese Klausel schwindet aus der Betrachtung.
Wenn dieses, wenn dieses Ziel hier scheitert,
dann geht er zu dieser Klausel und sagt blindlings troh.
Das ist genau die Bedeutung des Notz. Vorrats der Seen,
das Not ist eigentlich auch nichts anderes als ein Mechanismus,
um diesen Cut zu verbergen
Ein Konstrukt, ein Prädikat,
was nichts anderes tut als dieses Cutform-Nutzer zu verbergen,
indem es eben genau diese Semantik realisiert,
wenn die Suche hier gelingt, dann scheitere,
wenn die Suche scheitere, dann gibt Truhe aus.
Und das kann man eben testen, das kennen wir schon,
da müssen wir nicht drauf eingehen.
Auf die Art und Weise kann man noch mehr,
ach so, halt natürlich, jetzt sehen wir auch,
was wir ganz zu Anfang mal beobachtet haben,
dass nämlich das Not seine Argumente nicht instanziert.
Also ich kann nicht fragen, warum scheitert irgendein Aufruf?
Das geht nicht. Ich kann nur feststellen,
scheitert er ja oder nein
Und das hat zur Folge, dass die doppelte Negation dann
das Systemverhalten ändert. Und das ist keine richtige Negation,
sondern eben nur ein Deep Closed World Assumption, die eine Part-Pseudonigation darstellt.
Also ich kann zum Beispiel fragen, gib mir das
letzte Element dieser Liste hier und ich werde C herausbekommen.
Und wenn ich eben frage, nicht das letzte Element dieser
Liste, dann kriege ich ein False heraus.
Denn natürlich gibt es ein letztes Element dieser Liste,
dieser Aufruf gelingt immer und weil er gelingt,
ist die Legation, liefert die Delegation des Ergebnis False.
Und wenn ich jetzt zwei dieser Negationen hintereinander baue,
wird dieser Aufruf in die Suche geschickt, dir gelingt.
Natürlich hat jede Liste ein letztes Element. Wenn ich das legiere,
kriege ich einen False heraus, wenn ich das wieder negiere,
kriege ich ein Truh heraus, aber wir sehen,
ich kriege eben keine variablen Belegungen mehr heraus,
die ich hier oben noch rausgekriegt habe.
Weil in der Zwischenzeit eben eine Integration bereits abgearbeitet worden ist
und das historische Gedächtnis nicht weitergeliefert wird.
Er testet wirklich nur, gelingt es oder gelingt es nicht
und mehr als Truh oder False kann ich davon nicht erwarten.
Bitte? Jetzt gibt es sogar noch Schutzsuche in dem Fall,
oder? Das war das, was wir am ersten Punkt
Das könnte sein, dass das noch eine alte Variante ist.
Wie gesagt, die Systemoberfläche hat sich geändert. Da muss
ich noch mal gucken, ob das noch aktuell ist.
Gut, also Negation prüft eben nur immer das Scheitern des Ziels.
Deshalb wird es als Integration erst failure bezeichnet.
Und die Voraussetzung ist immer die Closed World Assumption.
Ich habe eine abgeschlossene Welt. Nur das,
was ich aus der Datenbank ableiten kann, gilt,
alles andere ist falsch. Und auch hier wieder,
Nigration as failure ist der einfachste Fall einer nicht monotonen Logik.
Kein Wunder, denn wir haben in der Definition des Mods die den Cut benutzt da.
Gut, und wir sehen dann auch hier wieder,
dass wir eben durch die Verwendung des Notz Ergebnisse verlieren.
Hier erst wieder der monotone Fall. Wir haben eine zwei Klausel-Datenbank,
die uns die Ergebnisse für diesen Aufruf A von A2
liefert und A von B Falls liefert.
Und wenn wir jetzt ein weiteres Aktionen hinzunehmen,
dann sehen wir, dass wir aus dem,
statt dem einen der Einflussfolgerung jetzt zwei Schlussfolgerungen unterstützt bekommen
Und in dem Augenblick, wo wir einen Not einbauen und
das Not ist nichts anderes als ein versteckter Cut,
dann ändern sich hier natürlich die Polarität.
Das, was vorher tru war, ist jetzt fort,
das vorher vor uns war, ist jetzt true.
Aber wir sehen eben auch, wir verlieren hier eine Variante.
Das ist eben genau die Wirkung des Cuts. Er nimmt
immer nur Dinge weg, aber fügt keine neuen Ergebnisse hinzu.
Und nach diesem Schema kann man jetzt tatsächlich auch Steuerstrukturen aufbauen.
Wir haben ja schon eine ganze Reihe von Steuerstrukturen kennengelernt.
Der wichtigste, die wichtigste ist, die Rekursion,
die wir gebraucht haben,
um Zyklen zu realisieren
Guarded Clauses gehabt, die wir benutzen können, um Fallunterscheidungen zu machen.
Das hatten wir vorhin bei dem Maximum gerade, wo wir
eben getestet haben, ist A, GoSAB oder ist B-Größe A.
Der Cut ist auch eigentlich nichts anderes als eine Steuerstruktur und den werden wir jetzt benutzen,
um zusätzliche Konstrukte einzubauen, die wir aus anderen Programmiersprachen her gewöhnt sind,
die wir eigentlich nicht bräuchten, die aber manchmal sehr,
sehr nützlich sind.
Eins ist das If-Wen vor der Verwendung von If-Wen wird dringend gewarnt,
weil man da eben auch wieder leichte Effekte erzeugen kann,
die man nicht mehr überschaut. Hier ist die Definition,
ich habe ein In-Fix-Operator mit einem IF-Teil und einem Ven-Teil.
Der IF-Teil wird ausgewählt, er wird aufgerufen,
wenn das gelingt, werden die Alternativen abgeschlossen und das Ven prozessiert.
Wenn der IF-Teil scheitert, dann scheitert das gesamte Konditional.
Und das ist der große Unterschied,
wenn ich also in einer klassischen, imperativen Sprache habe,
if something Ven sowieso if something Ven sowieso.
Und das IF scheitert, macht er immer bei der nächsten Statement weiter.
In diesem Falle scheitert das IF und damit scheitert die
gesamte Klausel
Das ist der wesentliche Unterschied. Also wenn
das IF scheitert, dann ist das gesamte Konditionalforts.
Und wenn das konditional in einer Sequenz von Aufrufen steht,
bricht die Abarbeitung an dieser Stelle ab.
Deshalb wird dringend gewarnt, das IF zu verwenden, ja.
Kann man das sicherlich auch machen. Genau so,
was da steht, kann man das weiter verwenden,
so Bedingungen, Komma, Cups, sich selber zusammen.
Natürlich, ja, ja, jederzeit, ja, sicherlich.
Auch im Prinzip ist das nur ein Mittel,
um den Cut zu verstecken.
Das ist wie beim Not auch. Auch das Not kann
man sich jederzeit per Fuß zusammenbauen,
wenn man möchte
Also, für das IF werden lieber nicht verwenden,
wenn sie es verwenden wollen, machen sie immer ein IF-Venels,
dann sind sie auf Nummer sicher.
Dann können sie nämlich immer einen Els-Zweig angeben,
was ist zu tun, wenn das IF scheitert.
Und dann können sie eben im Tonnot im Erzzweig ein Truh reinschreiben,
der gelingt dann immer. Und dann ist gesichert,
dass die Verarbeitung auch mit der nächsten Klausel quatscht mit der nächsten Klausel,
mit dem nächsten Teilziel in einer Sequenz von Teilzielen weitermacht.
Also, das IF-Venels ist dann eben hier dieser IF-Ven-Operator und
hier nichts anderes als eine Disjunktion.
Und sagt eben, wenn das IF positiv abgeleitet ist,
dann führe das Wenn aus,
schneide aber vorher
diese alternative Klausel ab
Und wenn das IF scheitert,
dann kommt er automatisch zu der zweiten Klausel,
egal was im IF-Teil steht, er weiß,
er ist jetzt im Elszweig und er macht nichts anderes als default schließen.
Und genauso wie wir vorhin in der Default-Regel gesagt haben,
irgendetwas hat sechs Beine, sagt dieses Ding jetzt eben,
egal was die Bedingung war, ich muss abgearbeitet werden.
Und dann wird eben nur der Elszweig aufgerufen und die Verarbeitung
bricht ab. Vorher, das ist doch interessant,
wird hier noch ein Cut-Gesetz. Und dieser Cut bewirkt,
dass Alternativen, keine Ahnung, muss ich doch mal überlegen.
Nee, eigentlich nicht. Das Erst wird dahinter abgearbeitet.
Das ist das, deshalb stocke ich jetzt auch.
Ich habe im Augenblick vergessen, wofür der Cut da war.
Also so wie er da steht, bewirbt er so gut wie nix.
Nee, kann ich im Augenblick nicht sagen. Das ist,
wofür er da ist. Aber die Botschaft ist hier auch,
im Prinzip ist er nichts, ist das If-Van-Els,
nur wieder ein kompositionales Syntaxkonstrukt,
um irgendwo diese Cuts sinnvoll zu platzieren
Wenn man denn haben will, dass das Ziel,
das IF-Ziel, noch backtrackbar bleibt,
dann gibt es einen sogenannten Soft-Cut, der dann dazu führt,
dass auch wenn der IF-Teil mehrere Ergebnisse produziert,
diese mehreren Ergebnisse tatsächlich alle noch durchgerechnet werden.
Also die Cut-Definition, die wir hier, Quatsch,
die ich Ben Elst-Definition, die wir hier hatten,
ist ja, wenn das IF positiv war,
dann schneide Alternativen weg.
Also auch alle Backtracking-Alternativen, die
das IF möglicherweise produzieren würde
Und der Softcut, der erlaubt es jetzt weder noch Backtracking in,
die Alternativen, die hier gegebenenfalls vorhanden sind, zu machen.
Das ist der feine Unterschied dabei, das ist eben ein Beispiel,
wo man das mal an einem abstrakten Beispiel sehen kann.
Wir haben also diese beiden Fakten in der Datenbank und wenn ich jetzt diesen Ausdruck habe,
der IF-Zweig ist A von X und der produziert eben jetzt
zwei Ergebnisse mit 1 und 2. Und wenn das der Fall ist,
dann soll eben Y gleich A sein und anderenfalls soll Y gleich B sein.
Und dann wird eben im ersten Fall X an 1 gebunden,
Und deshalb kommt Y gleich A als Bindung heraus.
Aber dann ist der Cut prozessiert worden, sodass das
zweite Ergebnis mit A von 2 überhaupt nicht gesehen wird.
Wenn ich A von 4 aufrufe und auch ein normales FNLs habe,
dann ist eben, dann scheitert dieser IF-T-Test,
A von 4 gibt es nicht.
Und deshalb wird nur der Wendzweig, der Älz-Zweig abgearbeitet und
ich kriege die bei Ja-Win-Berung Y gleich B. Und jetzt das Alternativprogramm,
ich nehme den Softcut und dann sehen wir jetzt,
dass dieses Ding, was zwei Ergebnisse produziert,
noch dem Backtracking zugänglich ist.
Ich kriege zwei Ergebnisse,
nehme ich einerseits für X gleich 1,
andererseits für X gleich
Und in beiden Fällen ist der Kopf,
oder der IF, die IF-Bedingung erfüllt gewesen.
Das heißt, ich arbeite den Wendzweig ab, nicht den Erzzweig.
Wenn die Bedingung im IF-Teil nicht erfüllt ist,
dann wird natürlich der Erzzweig hier hinten abgearbeitet.
Das ist eine kleine Bariante, wer es braucht. Schön,
ich habe bisher in meinem Leben noch keine Verwendung dafür gefunden,
aber wie immer, um das vollständig zu haben,
ist es vielleicht gar nicht so schlecht.
Okay, ich da jetzt noch in das nächste Einsteige,
wollen wir mal lieber dran denken,
dass wir heute den letzten Vorlesungstag vor Weihnachten haben.
Ich wünsche Ihnen ein frohes Fest,
ich wünsche Ihnen einen guten Rutsch ins neue Jahr und dann
sehen wir uns hoffentlich wieder.