Okay, einen schönen guten Tag. Wir waren dabei,
uns mit dem Thema aktive Datenstrukturen zu befassen. Aktive Datenstrukturen sind also solche,
die noch Variablen enthalten und wann immer ich eine solche Datenstruktur als Argument in einem Prädikataufruf verwende,
dann wird sie der Unifikation unterworfen und die Variablen,
die in der Datenstruktur enthalten sind,
die ungebundenen Variablen,
die schreien
danach instanziert zu werden
Und dies kann man sich zunutze machen, dass Datenstrukturen tatsächlich
nicht mehr nur noch eine passive Rolle spielen im Berechnungsprozess,
sondern eine aktive Rolle übernehmen können.
Und die prominenteste Variante davon ist, sind die Differenzlisten,
die also darauf beruhen, dass ich eine zweistellige Struktur vereinbare.
Und diese zweistellige Struktur benutze, um Differenzlisten zu generalisieren,
indem ich sage, eine Liste enthält einen bestimmten Inhalt,
der gefolgt sein kann von weiteren Elementen,
über die ich zurzeit noch nichts weiß,
aber ich rede nur über die Elemente,
die hier vor dem Konstott vor dem Kopfseparator spezifiziert sind.
Alles, was hier hinten noch folgen möchte, möge, wird sofort wieder abgezogen.
Und auf die Art und Weise bin ich in der Lage,
hier hinten in die Liste neue Elemente hinein zu unifizieren und dadurch längere Listen zu bilden,
ohne dass ich dafür die Liste kopieren muss.
Das ist der Wesentliche Vorteil davon. Wir hatten uns das
angeguckt für die verschiedenen Anwendungsfälle, also beispielsweise für Warteschlangen.
Wir hatten dann den interessanten Fall, dass man auf die
Art und Weise aus einer leeren Liste noch Elemente entfernen kann,
sodass Listen entstehen, die eine negative Länge haben.
Ach so, ja,
und was überhaupt wie unbedingt noch hinweisen wollte,
weil das bei mir in der Übung
auch aufgetaucht ist
Ich kann es nicht deutlich genug sagen,
dieser Backslash hier ist nichts anderes als Syntax.
Er hat keinerleise Manndeck. Er bewirkt nichts. Ich könnte
ihn ersetzen durch ein Komma, ich könnte ihn ersetzen durch ein Plus.
Ich könnte ihn ersetzen durch eine Straßenkreuzung, ein Dollarzeichen,
was immer sie wollen. Ich könnte dort reinschreiben, ja,
was weiß ich, Differenzliste und Differenzliste als Operator deklarieren,
so wie ich das hier gemacht habe.
Wenn ich irgendeinen existierenden Operator nehme, wie das Plus oder
die Multiplikation oder so etwas, das funktioniert genauso.
Ich könnte eben auch ein Komma dahin schreiben,
denn das Ganze wird ja immer in irgendwelchen Prädikaten benutzt,
sodass also dieses Prädikat dann hier beispielsweise fünfstellig würde
Jetzt ist es dreistellig, das Element, die alte Warteschlange,
die neue Warteschlange, wenn ich das durch Kommas trenne,
was diese Mantik überhaupt nicht verändern würde,
dann wäre es eben fünfstellig.
Dann wäre das der vordere Teil der ersten Differenzliste,
der hintritt Teil der ersten Differenzliste,
der vordere Teil der zweiten Differenzliste und der hintere Teil der zweiten Differenzliste.
Das könnte man machen an der Semantik des Programms ändert sich nichts.
Und das ist einzig und allein, dass man das Ganze
etwas markiert, als zusammengehörig und deshalb ein Operator verwendet.
Man könnte auch jede beliebige zweistellige Struktur nehmen.
Man könnte also irgendwie eine Struktur erfinden,
die heißt
Ja, das würde den gleichen Zweck erfüllen.
Dann habe ich eben auch eine Differenzliste mit zwei Komponenten.
Der erste Teil und das, was sich von der Subtrahent,
den ich von der Liste wieder abziehe.
Gut, insofern auch ich, das habe ich,
glaube ich, hier noch nicht gemacht.
Ich werde in der Prüfung keine Fragen zur Differenzlisten stellen.
Ich habe aber mal in irgendeiner Aufgabe,
wo es darum geht, was unifiziert mit wem.
Es gewagt einen Slash oder Backslash über Listen zu verwenden.
Und sofort kriegte ich Protest, die haben versprochen,
dass Differenzlisten nicht in der Klausur drankommen
Ich möchte darauf hinweisen, Differenzlisten sind nicht dran gekommen.
Ich habe nur gewagt, einen Operator zu verwenden.
Nichts anderes habe ich gemacht. Ein Infix-Operator, einen zweistelligen,
der ist dort drin aufgetaucht und ich überprüfe damit, genau dieses Wissen,
dass dieser Operator nichts anderes ist als eine syntaktische Konvention.
Und wie ich mit solchen Operationen und der Unifikation umgehe.
Das könnte also jeder beliebige andere Operator auch sein. So,
die wichtigste Anwendung, also diese Differenzlisten,
immer mit der Interpretation,
ich rede über einen Teil einer Liste und spezifiziere,
welcher Teil nicht mehr berücksichtigt wird oder mit anderen Worten,
ich habe einen Pointer auf den Anfang
einer Teilliste und das Ende einer Teilliste
Und ich bin dann eben in der Lage zu sagen,
das verketten zweier Listen ist einfach nur,
das Unifizieren dieses Ende-Indikators hier mit dem Anfangs-Indikator dort.
Und dann entsteht einem eine Liste,
die hier an dieser Stelle beginnt und an dieser Stelle endet.
Und genau das, sagt die Appent-Definition, ich habe eine Liste,
die hier beginnt und die Erbegebnisliste fängt an der gleichen Stelle an.
Ich habe hier eine zweite Liste, die hört an der Position Z
auf und die Ergebnisliste hört auch an der Position Z auf.
Und dort, wo die erste Liste endet, da beginnt die zweite Liste.
Das ist die Bedeutung von der Pent. Gut, das
hatten wir uns in Testaufrufen angeguckt. Ich kann also einfach
zwei Listen miteinander verketten und erhalte dann die, so,
das habe ich nicht wieder, das hätte ich noch mal kompilieren müssen.
Okay, hier ist der Aufruf, ich verkette die Liste,
die nur A enthält, denn der Teil X wird sofort wieder abgezogen.
Ich verkette mit der Liste die AB enthält und erhalte als Ergebnisliste
die Liste, die zweimal A, gefolgt von B enthält.
Und wir sehen hier, dieses Ergebnis ist erzielt worden,
indem wir die erste Liste instanziert haben,
die hier nur ein Element enthielt,
da haben wir hier hinten in der Restliste die zweite Liste
hinein unifiziert
Und wir haben das nicht nur hier getan,
sondern wir haben es auch hier getan. Und deshalb wird
diese zwei Elemente ihr zweite Liste sofort wieder abgezogen davon.
Sodass also die intendierte Bedeutung ist, dies ist eine,
war mal eine Liste, die nur ein Element enthält.
Und durch die Subtraktion haben wir eben erreicht,
dass auch die Ergebnis, die Differenzliste, nur dieses eine Element enthält.
Und wir waren in der Lage, eine Liste zu zerlegen,
hier gegeben, in zwei Teillisten.
Und wir kriegen dann eine unterspezifizierte Antwort. Im Gegensatz zu den drei ausspezifizierten Antworten,
die wir für das traditionelle Impact kriegen würden, die aber,
wenn man die Co-Referenzen verfolgt,
genau so sind,
dass ich die drei Ergebnisse daraus generieren kann
Moment, L2 ist die Ergebnisse. Okay, ja.
Ich habe hier also eine erste Liste. Die ist,
enthält möglicherweise die Elemente A, B, gefolgt noch von
irgendetwas anderen. Und ich ziehe dort aber das ab,
was die zweite Ergebnisliste ist. Das heißt also,
wenn ich das hier instanziere mit der Liste A,
B, gefolgt von irgendetwas,
dann ist diese Liste leer
Und wenn ich das inszenziiere mit der Liste B,
gefolgt von irgendetwas, dann enthält diese Liste nur noch ein A.
Und wenn ich das inszenziiere mit der leeren Liste,
dann enthält diese Liste noch A und B.
Das sind die drei Varianten, die dabei herauskommen. So,
jetzt nehmen wir uns andere Anwendungen vor. Wir schreiben,
ich weiß nicht, ob ich es dies Jahr gestrichen habe,
ein beliebtes Listenverarbeitungsprädikat, das heißt Flatten.
Ich habe rekursiv eingebettete Listen und ich möchte alle rekursiven Einbettungen aus
der Liste entfernen. Also wann immer ich eine Teil des,
ein Element der Liste vorfinde, was wieder eine Liste ist,
dann muss ich das rekursiv in das Flatten stecken,
um sich Also um die Elemente zu extrahieren und zum Schluss nur noch eine Liste erzeugen,
wo die Elemente alle auf dem Top-Level stehen.
Wenn wir das traditionell implementieren, haben wir eben, wenn die leere Liste,
die enthält keine Elemente, ergibt wieder die leere Liste.
Als Ergebnis, wenn wir denn ein Atom irgendwo vorfinden oder
etwas, was nicht die leere Liste ist.
Achso, Jan, okay, das ist rein technisch an Natur.
Also wenn dort ein Atom noch in der Liste drinsteht,
dann soll das in die Ergebnisliste übernommen werden. Ansonsten müssen wir gucken,
ob der Kopf Oder ansonsten haben wir den Kopf noch,
platt zu drücken, indem wir ihn rekursiv in dieses Prädikat schicken.
Und wir müssen das Gleiche mit der Restliste machen,
die wir auch rekursiv platt drücken müssen.
Und zum Schluss müssen wir das,
was hier herauskommt für den Kopf und was hier herauskommt für die Restliste,
wieder mit Appent verknüpfen, um dieses Ergebnis zu erzielen.
Jetzt übersetzen wir das Indifferenzlisten. In Differenzlisten heißt das,
gucken uns erstmal das Eingebaute, das Eingebaute an und anschließend den Frepper außen drumherum.
Also, wenn wir eine leere Liste,
wir haben hier vorne auf der ersten Argumente immer eine klassische Liste.
Und wir schreiben das Prädikat so, dass wir eine Differenzliste als Ergebnis erhalten.
Also, ich habe hier vorne, ich schicke eine leere Liste rein,
dann soll auch eine leere Liste wieder rauskommen.
Das ist eine leere Differenzliste. Zweiter Fall ist,
wenn da ein Atom ist, dann soll ich eine einer Liste erzeugen,
in der genau dieses Atom drinsteht.
Das mache ich hier. Dies ist eine Einerliste,
in dem genau das Atom dass an die Variable X gebunden ist,
drinsteht, gefolgt von irgendetwas, was sofort wieder abgezogen wird.
Und der dritte Fall ist, flatte den Kopf,
flatte die Restliste und verkette das Ganze.
Also, nimm den Kopf, das ist das X hier,
und übersetze es in eine Differenzliste.
Und nimm die Restliste, das sind die Xs hier,
und übersetze es in eine andere Differenzliste.
Und anschließend verkette diese beiden Differenzlisten miteinander, sodass es das Ergebnis bekommst.
Jetzt kommt der Trick, Wir können das natürlich so aufrufen,
wie es ist, also dieser Aufruf bewirken,
dass wir unsere Appent-Definition, dieses Teil hier aktivieren.
Es wird über die Unifikation diese drei Co-Referenzen,
die hier drinstehen, hergestellt und wir kriegen eine Vayabi-Bindung als Ergebnis.
Wir machen mal hier was anderes. Wir nehmen jetzt diese drei Co-Referenzforderungen,
die dieses Appent realisiert und bauen sie in die Aufrufe hier oben ein.
Das nennt man Entfalten oder Unfolding. Wir haben also
eine Appent-Definition und wir unifizieren sie sozusagen in den Programmcode hinein.
Wir unifizieren jetzt also nicht mehr zur Laufzeit, sondern zur Entwicklungszeit.
Der Programmierer nimmt bereits vorweg, was bei der Unifikation passieren würde.
Und bei der Unifikation wird eben nur gefordert,
dass was immer hier vorn auftaucht, auch hier hinten auch auftauchen muss.
Und was hier auftaucht, muss dort auftauchen und was ihr auftaucht, muss dort auftauchen.
Das ist alles, was das erpennt fordert. Und das können wir natürlich machen,
indem wir sagen, okay, wo immer,
wir haben ja hier die, by way,
das ist nur das dritte, die dritte Klausel dieser Definition,
für die anderen müssen wir nichts machen,
denn da kommt keine Pentren vor.
Also wir gucken uns jetzt Sorry, wir gucken uns
nur die dritte Klausel dieser Definition an.
Und wir können jetzt also sagen,
wo immer ein YS auftaucht oder wo immer ein AS auftaucht,
können wir das durch ein YS ersetzen.
Und das machen wir, indem wir dieses AS hier,
das auftauschen gegen dieses YS. Und wo immer ein BS auftaucht,
können wir das ersetzen durch ein CS.
Ich habe da eine Umbenennung vorgenommen. Ich habe beide,
sowohl BS als auch CS in Y1S, umgewandelt.
Also wo BS auftaucht, das ist hier,
Steht jetzt ein Y1S drin und wo CS auftaucht,
steht auch ein Y1S drin.
Hauptsache es ist die gleiche Variable. Ist ja egal,
wie das Ding heißt. Und die dritte Forderung ist,
wo immer ein DS auftaucht, darf ich es durch ein ZS ersetzen.
Da ist das ein CDS, was auftaucht, das ersetzt sich durch ZS.
Und damit habe ich alle Ersetzungen vorgenommen und das Erpennen ist verschwunden.
Was kein Wunder ist, denn das erpennt selber,
hat ja keine prozedurale Bedeutung mehr.
Es ist keine rekursive Prozedur, sondern einfach nur ein Fakt,
der sagt, so ist es,
wenn ich Differenzlisten miteinander verkette
Und damit kriege ich also eine Definition für das Flatten,
in der gar kein Appent mehr drinsteht. Also Differenzlisten als Mittel,
um Appent-Aufrufe, die ja von der Effizienz sehr problematisch sind,
weil sie immer die erste Liste kopieren müssen,
um das Ergebnis zu erzeugen.
Das entfällt jetzt hier, weil ich eine Differenzliste habe,
in die ich hier hinten etwas hineinunifizieren kann.
Und wenn man sich das Ergebnis anguckt, ist das eben sehr erklärlich.
Ich habe hier eine Liste mit einem,
ich interessiere mich für das erste Element,
ich schicke dieses erste Element in das Flatten hinein und kriege
eine Differenzliste,
die bei YS beginnt und bei Y1S endet
Und ich schicke gleichmaßen die Restlichen Elemente,
die Restliste hier in dieses Flatten hinein und kriege auch eine Liste,
die hier beginnt und da endet.
Und zwar beginnt sie genau dort, wo dies Erste endet.
Und das ist die Bedeutung des Epens. Verkette,
was immer du hier bekommst mit dem,
was du hier bekommst und das Ergebnis fängt eben dort an,
wo die erste Liste anfängt und hört dort auf,
wo die zweite Liste aufhört.
Das ist alles, was passiert ist.
Das Flatten ist verschwunden. Wir können das jetzt testen,
das spare ich mir mal. Wir können das Gleiche für
das Reverse machen.
Da wird es
noch interessanter
Wir haben also ein naives Reverse, was sagt, ja,
nimm immer ein Element, schicke es die Restliste in das naive Reverse,
sodass du die Restliste umgedreht bekommst.
Und dann verkette, dass die umgedrehte Liste mit einer einer Liste,
die genau dieses Element enthält, was sie hier abgetrennt habe.
Also ich trenne immer vorn etwas ab und hänge es dann an das Ergebnis hinten wieder dran.
Und das Problem des naiven Reverse war ja,
dass dieses erpennt immer die erste Liste kopiert,
das heißt kopiert und dadurch unnützen Aufwand erzeugt.
Deshalb hatten wir ja das nicht naive Reverse,
was mithilfe eines Akkumulators es uns erlaubt,
die Liste auf einer dritten Argumentposition aufzubauen und erst am Rekursionsabschluss dann nach draußen zu übergeben,
sodass alles bereits beim Reco-7-Abstieg passiert und das ständige Kopieren der ersten Liste entfällt.
So, jetzt machen wir das wieder mit Differenzlisten.
Wir haben eine Liste gegeben, das ist eine klassische Liste
und wir kriegen wieder eine Differenzliste zurück. Ach so,
das habe ich vorhin nicht doch dann vergessen zu sagen,
hier außen drumherum braucht man natürlich immer einen Rapper,
der uns jetzt diese Differenzliste,
die wir ja als Ergebnis hier bekommen,
wieder übersetzt in unsere klassische Listennotation
Und neben mir einfach sagen,
wir kriegen hier eine Differenzliste und wir wollen eine solche haben,
wo der Subtrahent die leere Liste ist.
Auf die Art und Weise ist das Ding hier vorn,
der Minuend, genau die gesuchte Liste, die wir haben wollen.
Wir zwingen sozusagen das Ganze dann aus der Welt der Differenzlisten
wieder in die Welt der klassischen Listen zu projizieren.
Und genau das machen wir hier auch. Wir schicken hier vorn
eine normale Liste in die Berechnung hinein, die wird einfach durchgereicht.
Wir kriegen aus der Berechnung eine Differenzliste heraus und
wir projizieren die wieder auf die Welt der klassischen Listen,
indem wir sagen,
Rest lässt es leer und wir interessieren uns nur für den
vorderen Teil
Das hat so ein bisschen was von Rechnen mit imaginären Zahlen.
Ich gehe immer in den Bereich der imaginären Zahlen,
berechne dort irgendwas und zum Schluss projiziere ich alles wieder auf
die reale Achse und habe wieder Werte,
die ich physikalisch interpretieren kann.
So, das machen wir jetzt also,
indem wir einfach diese Definition wieder eins zu eins übertragen.
Wir machen überhaupt nichts Neues oder Innovatives hier. Wir haben
eine klassische Lehreliste und wir wollen dann eine Differenzliste haben,
die auch leer ist.
Das steht hier. Zweite Klausel ist,
wir trennen das erste Element einer klassischen Liste ab.
Das steht also
genauso wieder hier
Und wir kriegen eine Differenzliste zurück, das steht hier.
Das sind alles andere Variablen, also das Ganze ist
noch uninstanziert, wir um ein Ergebnis berechnen zu können.
Wir rufen das Reverse auf mit der Restliste,
das sind die, die hier hinten geliefert worden sind,
und wir kriegen eine Differenzliste zurück als Ergebnis.
Und jetzt müssen wir diese Differenz, diese Liste,
die wir da zurückkriegen, mit der einer Liste,
in der das Element drinsteht, pennt, miteinander verbinden.
Also wir nehmen diese Differenzliste, die steht hier,
wir bauen eine einer Liste mit diesem X auf,
das ist diese hier, die enthält nur das X,
gefolgt von irgendetwas,
was wieder abgezogen wird
Und wir kriegen natürlich hoffnungsvoll hoffentlich ein Ergebnis,
was wieder eine Differenzliste ist, die wir dann durch instanziieren mit
der leeren Liste oder aus der wir die reale Liste extrahieren können,
indem wir hier hinten die leere Liste rein instaziieren.
Jetzt kommt wieder das Entfalten. Wir tragen also wo immer
ein A steht, dürfen wir ein YS eintragen.
Ein A steht hier, da tragen wir ein YS ein.
Wo immer ein B oder C steht,
das muss auf die gleiche Variable verweisen
Ein B steht hier, es steht hier und es steht hier.
Achso, nee, ist Quatsch. Es steht hier,
das ist das Einzige, was es ist und ein C,
jetzt muss ich selber erst mal ein bisschen gucken hier.
Machen wir andersrum, wo immer ein ZS steht,
darf ich ein CS reinschreiben und wo immer ein CS steht,
nee, nee, Quatsch.
Wo immer ein ZS steht, darf ich ein,
oder ein CS steht, darf ich ein ZS reinschreiben?
Nein, so kommen wir der Sache nicht bei.
Ich habe mich verrannt
Neuer Anlauf. Was ich machen muss, sind diese drei Cho-Referenzforderungen.
Ich muss sicherstellen, dass das, was hier steht,
gleich dem ist, was hier hinten steht.
Also ich muss das AS und das Y erst miteinander unifizieren.
Das heißt, das, was hier steht,
im vorderen Teil muss das Gleiche sein wie das, was hier steht.
Die beiden müssen miteinander unifiziert werden. Und das habe ich hier gemacht,
dann nehme ich das hier zu YS umbenannt habe.
Das ist die Cho-Referenz. Zweitchoreferenz zwischen BS und C,
ja, genau das ist es. Ja, danke.
Da habe ich mich vertödelt, ja. Der Gesamtausdruck ist
das, was an Y gebunden wird. Das heißt also,
wo BS steht, das steht hier, darf ich X,
slash slash slash CS eintragen. BS steht also hier,
da kommt ein solcher Ausdruck hinein und die dritte Co-Referenzforderung ist,
dass wo immer CS steht, darf auch ZS stehen.
Das ist der Inhalt, der dann hier Also dieses,
die Variable hier drin, das CS wird nochmal durch ZS ersetzt.
Das heißt, wir kriegen wieder eine Reverse-Definition, die kein Appent mehr enthält.
Wir haben eine rein formale Transformation des Programms vorgenommen.
Wir haben nichts irgendwie Gehirnschmalz hineingesteckt,
sondern es ist ein total formaler Prozess, der dort abläuft.
Den können wir automatisieren. Und wenn wir das jetzt gegenüberhalten,
unser klassisches Reverse, was wir hier haben,
und unser neues Reverse,
was wir haben,
dann sollte ihnen
eins auffallen
Das Dumme ist, ich habe die falschen Folien hier.
Ich habe sie nicht nochmal übersetzt. Die Antwort steht bereits hier.
Wir haben das, ja, das ist genau der Punkt,
wenn man das Backstage durch den Komma als jetzt.
Genau. Ja, das ist unser nicht naives Reverse mit Akkumulator-Verwendung.
Das einzige, was hier noch den Unterschied ausmacht,
der Rapper ist genauso, der Unterschied besteht einzig und allein,
dass hier der Akkumulator das Ding hinter dem Slash ist,
hinter dem Backslash ist.
Und hier ist der Akkumulator das zweite Argument.
Aber die Argumente kann man ja konsistent vertauschen,
das macht ja nichts aus.
Ansonsten ist es genau
das Gleiche
Wir haben also, hier wird der Wert des Akkumulators an
die Ausgabe Vagable übergeben. Hier wird ein Element aus
der Liste immer entnommen und in den Akkumulator übergeben.
Das passiert hier, denn hier hinten steht der Akkumulator.
Es ist genau das Gleiche, wenn wir diese beiden Argumentpositionen vertauschen,
haben wir eben hier den Aufbau der Ergebnisliste und hier das Ergebnis,
die Ergebnis Vagable, das ist hier die zweite.
Und wir sehen, die zweite Vagable wird einfach eins
zu eins per Co-Referenz nach oben weitergereicht oder die Bindung wird
nach oben weitergereicht,
genauso wie das hier passiert
Und sie instanziert wird, das Ganze im Rekursionsabschluss,
wo die dritte Variable, den Wert der zweiten Variablen enthält und
das ist eben hier in der umgekehrten Richtung der Fall.
Also wir haben jetzt aus dem naiven Reverse durch Einführen der Differenzlisten durch Entfalten des Appens
genau die Definition des nicht-naiven Reverse automatisch, also formal abgeleitet.
Auf die Art und Weise sieht man also,
dass eine wesentliche Eigenschaft der Logikprogrammierung,
dass sich in dem Augenblick,
wo ich sicher sein kann,
dass die Richtungsunabhängigkeit
wirklich durchgängig funktioniert
Solche formalen Programmtransformationen anwenden kann, um kurz zu optimieren.
Denn das nicht naive Reverse, was jetzt hier rausgekommen ist,
das ist natürlich erheblich effizienter als das naive Reverse,
von dem wir ursprünglich mal ausgegangen sind.
Und wir haben das Ganze nur durch einen formalen Transformationsprozess des Programms erreicht.
Also da ist großer Raum für die Optimierung von Programmen gegeben.
Hier nur noch als letztes Beispiel Quicksort, das läuft eben genauso.
Wir haben das klassische Quicksort, ich muss irgendein Element nehmen,
ich nehme das erste Element der zu sortierenden Liste hier und unterteile entsprechend
Die restlichen Elemente, ob sie kleiner sind,
als dieses Split-Element, dann kommen sie in diese Ergebnisliste.
Ansonsten kommen sie in diese Ergebnisliste. Dann sortiere ich die kleineren Elemente,
ich sortiere die größeren Elemente und ich baue die Ergebnisliste wieder zusammen,
indem ich die sortierten kleineren Elemente,
die sortierten größeren Elemente miteinander verkette und dazwischen kommt noch das Split-Element,
was ich vorher abgetrennt habe, hinein.
Gut, und wenn ich leere Listen habe, dann sind die bereits sortiert.
Da muss ich nichts weitermachen und wir können jetzt wieder hingehen und können
das Ganze mit Differenzlisten aufschreiben und dann durch Entfalten dieses Appent wieder beseitigen und das,
was dann herauskommt, sieht eben so aus.
Ich schicke eine ganz normale Liste in das Quicksort hinein.
Ich habe wieder einen Rapper, der mir genau das macht,
der hier hinten eine Differenzliste entgegennimmt,
die er durch Instanziieren mit der leeren Liste wieder in die
Welt der realen Listen überträgt oder der normalen Listen überträgt.
Ich habe den Rekursionsabschluss, der sagt,
eine leere Liste ergibt wieder eine sortierte leere Liste als Differenzliste.
Ansonsten, der Split ist genau das Gleiche.
Ich nehme die Listen-Elemente, die Restlisten-Elemente und sortiere sie,
ob sich kleiner oder größer als das erste Element sind.
Und ansonsten wird der vordere Teil sortiert,
der hintere Teil sortiert. Ich kriege jeweils eine Restliste,
eine Differenzliste und wir sehen hier wieder dieses Erpenntmuster.
Die Liste fängt hier irgendwo an,
dann gefolgt von dem Split-Element und dann wird es mit den
restlichen Elementen der hinteren Liste verkettet.
Das ist wieder
das gleiche Ergebnis
Gut, das Hauptanwendungsgebiet der Differenzlistentechnik ist allerdings nicht die Programmoptimierung,
sondern das Schreiben von Grammatiken.
Das gucken wir uns jetzt noch mal ganz kurz an.
Es gibt noch weitere Anwendungen, wo die gleiche Idee,
weil sie so mächtig ist, ebenfalls benutzt wird.
Im Mercury hat die schon mal angesprochen, eine Sprache,
die versucht, die Brücke zwischen funktionaler und Logikprogrammierung zu schließen,
indem sie klauseln als ungeordnet im Körper betrachtet.
Also die Teilziele im Körper werden per se erst nicht einer
Ordnung unterworfen. Und wenn ich eine Ordnung haben will,
dann muss ich
dies festlegen
Und das passiert genau durch diese Differenzlisten-Technik, dass ich dann sage,
in welcher Reihenfolge werden die Ergebnisse von einem Teilziel in das nächste Teilziel weitergereicht.
Wir gucken uns das gleich noch mal an,
wenn wir den Marco-Operator für die Grammatiken uns angeschaut haben.
Eine zweite Idee ist auch noch,
dass ich die Suchraumverwaltung in Theorinbeweisern auf die Art und Weise erledigen kann,
oh, hier steht noch der Pointer auf GWV drauf,
das haben wir gar nicht mehr,
weil wir inzwischen das Buch gewechselt haben.
Das ist da gar nicht mehr drin.
Gut, also kontextfreie Grammatiken, der Begriff ist hoffentlich
jedem einigermaßen geläufig
Ein Regelformalismus, der durch Regelersetzungen es erlaubt,
Sprachen zu definieren. Das ist üblicherweise die Interpretation,
die wir in der theoretischen Informatik haben,
dass eine Sprache als Menge von Zeichen,
oder Symbolen heißt es da, oder?
Ja, da ist die Terminologie immer ein bisschen unterschiedlich.
Es sind also Symbole, die zu Wörtern zusammengefügt werden.
Der Link Wist sagt eher,
es sind Wörter, die zu setzen,
zusammengefügt werden
Und eine berühmte klassische Regel, die auf Trumpsky selbst zurückgeht,
ist diese Regel für das Bilden eines Satzes im Englischen,
der also besteht aus einer Nominalphrase,
das ist immer das Subjekt,
was im Englischen ja vorn stehen muss, gefolgt von einer Verbphrase,
die das Prädikat und mögliche Objekte enthält.
Weil eben die Objekte, die dort auftreten können,
sind abhängig von dem da, was ich verwende.
Mal darf ich Objekte verwenden, mal darf ich keine Objekte verwenden,
manchmal sind es sogar zwei Objekte, die ich brauche.
Deshalb wird all dies
in dieser Verbgruppe zusammengefasst
Und wir können jetzt eine solche Regel hernehmen,
die sagt ja nur, ich habe ein Symbol, das nennt sich S.
Und dass ein solcher Satz setzt sich aus einer Nominalphrase gefolgt von einer Verbalphrase zusammen.
Also so ein Regelkodiert zwei Typen von Informationen. Erstens,
ich brauche zwei Dinge, die NP heißen und VP heißen.
Und ich brauche etwas, um die beiden zu verketten.
Denn sie müssen hintereinander im Satz auftreten. Wenn ich das also in ein Logikprogramm übersetze,
heißt das, ich habe ein Ding, das ist,
das nenne ich S, ich habe ein anderes Ding,
das nenne ich NP. Ich habe ein drittes Ding,
das nenne ich VP und es gilt die Beschränkung,
Das X, was ich hier vorhin habe,
durch Verketten von Y und Z entsteht.
Das ist diese Mandik einer solchen kontextfreien Regel. Und wir sehen,
dass es da eine ganz hübsche Analogie zwischen solchen kontextfreien Regeln
und unseren Klauseln, die wir in der Logikbramierung verwenden.
Wir haben also die linke Seite einer Regel, das ist
die linke Seite unserer Klauselnotation, mit diesem umgekehrten Implikationsfall.
Wir haben eine rechte Seite der Regel und wir haben hier
Teilziele auf der rechten Seite.
Alles, was rausfällt, ist dieses Append.
Aber wir haben ja eben einen Weg gesehen,
wie wir das erpennt
Reinbekommen, ohne dass wir es sehen können. Auf die Art und Weise
war ja unser Flatten und unser Quicksort und so weiter entstanden.
Und genau das wollen wir hier auch wieder anwenden.
Wir brauchen natürlich auch noch so etwas,
was der Link wisst, als Preter minale Regeln bezeichnet.
Also das, was nicht Terminalsymbole der Grammatik in Terminalsymbole der Sprache übersetzt.
Das sieht eben normalerweise in einer kontextfreien Grammatik so aus.
Ich habe ein nicht-terminales Symbol, das ist hier ein Nomen,
ein Substantiv. Und ich habe ein Terminalsymbol,
das ist zum Beispiel Frau,
das taucht dann tatsächlich in einem Satz auf
Und wir werden das dann so repräsentieren,
dass das eben eine Liste ist, die genau ein Element enthält,
weil wir dann nämlich generalisieren können, zu Listen,
die mehr Elemente enthalten, die wir beispielsweise V-Binnen oder Entpinnen oder Satz nennen.
Also diese Vajablen hier werden in Zukunft immer an Listen gebunden,
denn nur dann können wir sie mit erpennt überhaupt miteinander kombinieren,
sonst würde das nicht gehen.
So, so sieht also das Wörterbuch aus,
dann das sagt, wenn ich etwas finde,
was eine Liste ist, die nur das Wort Frau enthält,
dann nenne ich das Teil
ein Nomen
Das steht dort. So, und jetzt machen wir eine ganz,
ganz simple Grammatik, warum die ganz simpel ist.
Da kommen wir gleich noch dazu. Und wir sagen,
ein Satz ist also eine NP und eine VP miteinander verkettet.
Eine NP erkläre ich als ein Artikel mit einem Nomen verkettet.
Ein Artikel ist also die beispielsweise. Ein Nomen ist Frau,
sodass ich, wenn ich die miteinander verkette,
die Frau herauskommt, und das nenne ich eine NP.
Und eine VP soll nur etwas sein, was ein Verb ist.
Das sind sogenannte intransitive Werben in der Sprache,
die kein Objekt mehr erfordern. Also davon gibt es eine
ganze Reihe, schlafen zum Beispiel gehört da auch dazu.
Aber in diesem Fall, in dem wir liest,
Und wir haben jetzt diese Liste als die vaiable Belegung für X hier,
sodass wir das ganze Ding auch eine VP nennen können.
Damit haben wir eine NP, die heißt die Frau und eine VP,
die heißt liest und wir können per Appent die beiden miteinander verknüpfen,
sodass wir einen Satz herausbekommen, der heißt die Frau liest.
Was auffallen sollte, ist, dass dies der einzige Satz
ist, den die Grammatiker zeugen kann.
Die Grammatik kann diesen einen Satz und sonst keinen weiteren.
Den Fall, wenn wir weitere Sätze erzeugen wollen,
den gucken wir uns gleich noch an.
Jetzt benutzen wir diese Grammatik zum Generieren. Wir rufen das Ganze
mit einer uninstanzierten Variablen auf und wir die einzige Regel,
die auf dieses Ziel passt, ist eben unsere NPV-P-Regel.
Und die hat drei Teilziele und die müssen wir jetzt der Reihe nach abarbeiten.
Erster Auftrag ist, finde eine NP. Die einzige Regel,
die wir dafür haben, ist die Regel, die sagt,
eine NP setzt sich aus Artikel und Nomen zusammen und wir verketten sie miteinander.
Also nächster Auftrag, wir machen ja Top-Down-Analyse, finde einen Artikel.
Da findet sich jetzt im Wörterbuch dieser Eintrag.
Damit wird Y2 an die gebunden und ich kann mit diesem
Wissen das nächste Teilziel eingehen.
Das sagt, finde mir
ein Substantiv
Ein Substantiv steht auch im Wörterbuch drin, es heißt Frau.
Damit hat jetzt Y2 den Wertliste, die. Und Z2
den Wertliste Frau. Und ich soll die beiden miteinander verketten
, sodass die DNP herauskommt. Und das passiert hier,
das Appent wird aufgerufen mit die und Frau und als Ergebnis
kriegen wir die Liste die Frau zurück.
Dann, damit haben wir diesen ersten Auftrag,
gib mir eine NP abgearbeitet, wir müssen noch eine VP suchen,
eine VP kann eben ein Verb sein und ein Verb ist liest zum Beispiel.
Und wir verketten jetzt Ach nee, da ist nichts zu verketten.
Da ist ja kein Erpennt drin. Das ist ja die
Regel ohne Erpennt. Was sie aber als drittes noch brauchen,
wir müssen jetzt den Wert,
den wir hier für die NP ermittelt haben und den Wert,
den wir hier für die VP ermittelt haben,
die müssen wir miteinander verketten und dann kommt eben ein Satz heraus.
Und das passiert hier. Die Frau als NP liest als VP,
gib mir bitte das Verkettungsergebnis und das kriegen wir nach außen mitgeteilt.
Die Frau ist ein Satz dieser Sprache,
die durch die Grammatik erzeugt wird
Und wenn wir nach Alternativen fragen, werden jetzt diese Entscheidungspunkte alle abgeklappert,
aber wir sehen immer, es gibt keine andere Regel für einen Werbung.
Es gibt keine andere Regel für einen Nomen,
es gibt keine andere Regel für einen Artikel,
es gibt keine andere Regel für eine Nominalphrase und es gibt auch keine andere Regel für einen Satz.
Das heißt, das führt dazu, dass wir keine weiteren
Alternativen finden in den Entscheidungspunkten und wir kriegen insgesamt False,
also weitere Ergebnisse gibt es nicht.
Das sieht einigermaßen vernünftig aus, wir gucken uns das Gegenstück an,
das ist jetzt, wir schicken einen Satz hinein,
wir instanzieren die Variable hier Und wir wollen wissen,
gehört er zur Sprache ja oder nein.
Und dann wird der Prozess umgekehrt angeworfen, dass man wiederfragt,
gib mir bitte eine NP. Und wenn wir die NP gefunden haben,
das ist zum Beispiel, ist ja die einzige NP,
die wir bilden können, ist ja die Frau,
dann sagt er, ja,
dann gib mir jetzt bitte noch eine VP und die einzige VP,
die wir bilden können, ist liest.
Und dann sagt er, ja, jetzt verkette bitte mal
das Ergebnis für NP mit dem Ergebnis für VP und gucke,
ob dabei zufällig die Frau liest,
Das, was wir hier machen, ist also wieder ein reines Generate- und Testverfahren.
Wir generieren einen Satz und hoffen, dass der Satz rauskommt,
den wir haben wollen. Was hier trivial ist,
weil wir nur einen einzigen Satz generieren können und wir nur testen können,
ist es dieser Satz oder ist es dieser Satz nicht?
Das Ganze wird natürlich erst spannend in dem Augenblick,
wo wir anspruchsvollere Grammatiken haben. Also was wir hier haben,
ist unsere klassische Analysestrategie von Prolog, wir machen Top-Down-Suche,
wir fangen mit dem Satzsymbol an und propagieren, unsere Erwartungen über das,
was wir in einem Satz finden, von oben nach unten.
Und wir machen in den Klauseln wieder links, rechts Analyse und Tiefe zuerst.
Wir wollen erst ein Ergebnis haben, ehe wir nach Alternativen gucken. Und das Ganze,
was hier rauskommt, dadurch ist eine blinde Suche,
die also blind links sagt, generiere mir bitte eine NP,
generiere mir bitte eine VP, ich werde schon noch überprüfen,
ob mir die gefallen oder nicht.
Das ist dann das, was hier im letzten Teil Zielpapier passiert.
Also der Vergleich erfolgt anschließend mit dem Eingabesatz.
Und wir haben also sozusagen einen Generate- und Testverfahren,
ohne dass wir Zwischenergebnisse uns angucken können.
Und damit haben wir auch keinerlei Steuerung, die auch durch die Eingabedaten.
Es könnte jemand ja eine Idee haben, wie man das erreichen könnte.
Wie könnten wir durch die Analyse, durch Eingabedaten steuern?
Ohne dass wir jetzt auf Differenzlisten zurückgreifen.
Das ist eine Alternative dazu
Hat hier mal eine Idee? Wie erreichen wir es,
dass wir hier in der Generierung sagen, wir wissen ja schon,
dass der Satz, die Frau liest, rauskommen soll?
Also warum suchen wir hier blindlings nach einer NP?
Wir suchen natürlich nach einer NP, die in diesem Satz enthalten ist.
Wir wollen wissen, ob das da drin vorkommt.
Wie könnte man es machen? Eine Idee, wenn wir das ausdrücken,
bevor wir in die und auch die Idee ausdrücken.
Und die haben die Platzung geebnet und würde ja.
Und das hätte so möglich auf diese Phase gemeinsam.
Genau, ja. Das wäre eine Möglichkeit,
wir schreiben, dass er pennt als erstes rein,
dank Richtungsunabhängigkeit können wir das ja machen.
Wir instanzieren das mit dem gesamten,
mit dem gesamten Satz und dann zerlegt es uns das praktisch
in die Bestandteile und wir müssen hinterher nur noch überprüfen,
ob der vordere Bestandteil eine NP ist und ob der hintere Bestandteil eine VP ist.
Geht sehr gut für solche binärverzweigenden Sachen,
die man auch als Trumpsky-Grammatik bezeichnet.
Allgemein kontextfreie Grammatiken erlauben aber mehr Elemente auf der rechten Seite
und dann wird es komplizierter.
Dann ist es
nicht mehr ganz so einfach
De facto macht man dann eine Zerlegung in Trumpsky Normalformen.
Aber es wäre eine, es ist ein Lösungsansatz, den man verfolgen kann.
Wir folgen einem anderen Lösungsansatz, der noch effizienter ist.
Und um zu zeigen, dass er tatsächlich etwas bringt,
müssen wir uns die Grammatik etwas erweitern. Und zwar,
wir erweitern nicht den Regelsatz, das ist immer noch der gleiche.
Wir erweitern das Lexikon. Indem wir sagen, wir haben
jetzt nicht nur einen Artikel, sondern wir haben zwei.
In der Realität haben wir noch viel, viel mehr.
Wir haben nicht nur ein Substantiv, sondern zwei.
Und wir haben auch zwei Verben
So, und wenn wir jetzt das Ganze im Analyse-Modus betreiben,
wir schicken den Satz rein, dann sehen wir, was passiert.
Er generiert jetzt hier Blindlings eine NP und um eine NP zu generieren,
braucht er einen Artikel und er geht in das Wörterbuch hinein und findet dort der.
Wir bemerken bereits, das Ziel war, die Frau liest,
böswilligerweise habe ich natürlich das Wörterbuch so angeordnet,
dass er das als allerletztes findet.
Dann braucht er einen Nomen und im Wörterbuch findet er als erstes Nomen Mann.
Das heißt also, er generiert daraus der Mann muss jetzt eine Werbgruppe generieren.
Und das erste, was er im Wörterbuch findet,
es lacht, sodass der Satz rauskommt, der Mann lacht,
die beiden miteinander verkettet, ist aber leider nicht das,
was er da rauskommen sollen.
Das heißt, er scheitert. Backtracking gib mir ein anderes
Verb, denn das Verb ist als letztes gewählt worden.
Also gib mir ein anderes Verb, da ist eins,
es ist liest, also kommt heraus, der Mann liest,
aber es soll noch die Frau sein, die liest, also wieder nichts.
Wieder Backtracking, jetzt gib mir ein anderes Nomen statt der Frau,
nimm jetzt statt der Mann, nimm jetzt Frau.
Dann kommt eben raus, der Frau lacht als nächstes.
Wieder nichts. Dann kommt der Frau liest und so weiter,
bis dann irgendwann mal, ach so, dann kommen die die Mann-Varianten,
bis man alles durch ist und zum allerletzten Dings dann die Frau liest, tatsächlich rauskommt.
Und das ist der Satz, den ich haben wollte.
Also der klassische Fall, den ich am dringendsten brauche,
wie kann ich eine gegebene Struktur, also einen gegebenen Satz,
analysieren, ist der, der die meisten Probleme hervorruft.
Das ist ja auch für die Informatik genauso,
wenn ich mithilfe eines Compilers ausdrücke der Programmiersprache analysieren will,
ist ja die Analyse das Wesentliche.
Ich will ja nicht Programme generieren dadurch und zwar auch noch
sinnlose Programme generieren, sondern ich möchte gegebene Programme verarbeiten.
Und das ist in der mit der natürlichen Sprache nicht anders.
So, und das ist jetzt die Tatsache, der Punkt,
wo wir unsere Differenzlisten zum Einsatz bringen können, wir haben noch ein weiteres Problem.
Das sind nämlich Terminierungsprobleme, das habe ich jetzt hier nicht drin,
weil wir mit diesen rekursiven Regeln wird es noch komplizierter.
Wenn ich eine Regel habe und die würde der Linkwist gern so sehen,
dass eine Nominalphrase nach rechts ergänzt werden kann durch eine Preppe,
soziale Halbphrase, also so etwas wie der Projektor auf dem Tisch,
der Projektor ist die Nominalphrase auf dem Tisch,
ist sie per Positionalphrase, dann sieht man,
dass man bei der Top-Down-Struktur-Analyse-Richtung hier eine, Die Aufforderung,
suche mir eine NP, ersetzt durch die Aufforderung,
suche im Janna NP.
Das ist also eine klassische Zykluskonstruktion, die ich da einbaue und dann kriege ich ein Terminierungsproblem.
Es gibt auch noch den umgekehrten Fall,
der nicht ganz so kritisch ist, aber auch Probleme bereitet,
wenn ich nach links eine Modifikation vornehme, also so etwas wie,
das ist jetzt, das ist ein schlechtes Beispiel.
Das überspringen wir mal lieber, das ist nicht gut. So,
und jetzt nehmen wir unsere Differenzlisten und sagen, okay,
wir nehmen jetzt nicht mehr einstellige Symbole,
wie wir das bisher hatten, die wir an Listen binden,
sondern wir nehmen zweistellige Symbole,
nämlich Differenzlisten
Also eine Differenzliste, eine Konstituente,
ein Satz fängt immer irgendwo an und hört irgendwo auf.
Und das ist genau hier, als die Argumente der Differenzliste kodiert.
Und die NP fängt irgendwo an und hört irgendwo auf.
Und wir können das interpretieren, wieder, wie wir das
bisher auch interpretiert haben. Wir haben den vorderen Teil,
das ist die zu analysierende Liste. Das ist die Top-Down-Hypothese,
die überprüft werden, also für die, die,
ich glaube, Downhyper so Hypothese überprüft werden soll.
Und wir haben eine zweite Liste, das ist das,
was übrig bleibt, nachdem wir zum Beispiel eine NP gefunden haben.
Dann sagen wir, jawohl, wir haben eine NP,
die ist hier zu Ende und an dieser Stelle
musst du weitermachen,
um dann
eine VP zu analysieren
Und damit sehen die Regeln jetzt so aus,
dass immer hier Differenzlisten drin sind,
ein Satz fängt also hier an und hört bei E auf
und dann muss bei B einen NP anfangen und bei M muss die NPO hören.
Bei M muss dann gleich die nächste Konstituente beginnen,
die dann bei E zu Ende geht.
Das ist wieder das typische Appent-Pattern,
was wir per Entfalten direkt in die Regel hineingeschrieben haben.
Und das Gleiche müssen wir auch im Wörterbuch machen,
dass wir sagen, diese einer Liste ist jetzt eben eine Differenzliste,
wo ich das,
was hier hinten kommt,
sofort wieder abgezogen habe
Ja, und das macht man über die gesamte Grammatik hindurch.
Und wenn man jetzt die Analyse macht, dann sieht man,
was passiert, wer im Gegensatz zu den drei Folien vorher,
haben wir jetzt tatsächlich nur noch eine einzige Folie.
Wir geben diesen Satz hinein,
wir wollen ihn analysiert haben mit einer leeren Restliste.
Es soll also vollständig konsumiert werden. Und dann wird jetzt
diese Variablenbindung hier sofort an die Suche nach der NP übergeben.
Ich soll also eine NP finden,
die irgendwie ein Anfangsstück dieser Zeichenkette ist und ich soll
eine Restliste zurückgeben,
an der dann
die Verbalphrase beginnt
Und dann muss ich das eben durchreichen. Ja,
wenn ich das als NP interpretieren soll,
über diese Regel hier, weiß ich,
dass ich dann als erstes einen Artikel finden muss.
Und dann gehe ich direkt ins Wörterbuch und gucke und kriege wieder
den falschen Artikel der. Aber ich stelle unmittelbar fest,
dass es der falsche Artikel war. Das scheitert sofort,
weil es eben nicht unifiziert mit dem Eingabesatz.
Dann gucke ich mir eine Alternative an und die unifiziert tatsächlich.
Und die lässt mir einen Restsatz übrig,
den ich aber sofort wieder abgezogen habe.
Deshalb unifiziert das Ganze
Damit kriege ich eben die Aufforderung, dann jetzt an der Stelle,
wo der Artikel zu Ende ist, zu gucken,
ob ich denn nicht einen Nomen finden kann.
Und so weiter. Auch hier kriege ich als erstes wieder die falsche Hypothese,
dann die richtige, die odifiziert und das Gleiche fürs Verb,
erst wieder das falsche Verb, dann das Richtgewerb.
Und damit bin ich fertig. Im Gegensatz zu dem exponentiellen Aufwand,
den ich vorher hatte, wo ich also alle Alternativen durchgemustert habe,
ehe ich bei der Richtigen angekommen bin,
bin ich hier also ziemlich straightforward durch die Grammatik durchgekommen und kann gleich sagen,
jawohl, dieser Satz, die Frau liest, ist Element der Grammatik.
Gut, also was wir jetzt haben,
im Gegensatz zu dieser Blindensuche, ist eine gesteuerte Suche,
dass wir die Prädikatsaufrufe immer mit dem aktuellen Input bereits instanzieren
und deshalb nicht wild in der Gegend herum suchen müssen,
sondern ganz zielgerichtet auf die Input-Zeichenkette hinarbeiten können.
Ja, es gibt immer noch Terminierungsprobleme bei linksrekursiven Regeln.
Das sind die, die ich vorhin angedeutet habe,
wo also hier vorne eine Ja, wo ein Symbol in
das gleiche Symbol überführt wird durch Ergänzung nach rechts mit weiterem Material.
Also so etwas wie mit dem roten Dach oder das Haus mit
dem roten Dach hinter der Straße am Bach, solche Konstruktionen.
Und da läuft man eben in eine Endlosschleife hinein,
weil er eben immer wieder eine Nominalgruppe generieren soll.
Ach so, Moment
Genau, er wendet also immer wieder diese NP-Regel an und sagt,
ja, dann findet mir doch bitteschön eine NP,
die am Anfang dieser Liste steht.
Und das führt sofort wieder darauf rückgeführt, dass ich dann,
um die NP zu finden, muss ich eine NP finden.
Und um die NP zu finden, muss ich wieder eine NP finden.
Und damit sind wir in diesem endlosen Regress drin.
Der ist mit einfachen Mitteln nicht zu vermeiden. Ich kann
allerdings die Grammatik umformulieren. Es gibt zu jeder Grammatik,
die linksrekusive Regeln enthält, eine äquivalente Regel,
die rechts, eine äquivalente Grammatik,
die rechtsrekusive Regeln nur enthält und die terminieren dann tatsächlich
Was dabei passiert ist,
allerdings, und das ist ein Problem in der Programmiersprachen,
genauso wie in der natürlichen Sprache,
ich mache das ja nicht, um nur rauszukriegen,
das ist ein richtiger Satz, sondern ich will ja wissen,
was ist die Struktur des Satzes.
Also die Struktur, die durch die Regeln angegeben wird,
die brauche ich ja, um die Eingabe semantisch zu interpretieren.
Ich muss ja wissen, in der Programmiersprache,
was ist der Operator, was sind die Operanten beispielsweise?
Was sind die Öffnen, was sind die schließenden Klammern,
die zueinander passen? Und das Gleiche gilt für die natürlichen
Sprachen genauso. Und wenn ich jetzt die Grammatik verändere,
verändere ich die Strukturbeschreibung,
die durch die Grammatik erzeugt wird
Und das ist ein Problem, aber das kann man
auch wieder korrigieren, aber dann wird es richtig kompliziert.
Das ist dein Hirn verdrehend. Ich bin in der Lage,
da ja, das Ganze ist ja im Endeffekt ein Unifikationsformalismus.
Der ist Turing-Äquivalent. Ich kann jede beliebige Output-Struktur erzeugen,
die ich erzeugen kann prinzipiell.
Das heißt, ich kann jetzt per Unifikation wieder,
obwohl meine Grammatikregeln falsch sind, kann ich die richtige Struktur zusammenunifizieren.
Indem ich in den Regeln spezifiziere, wie denn das Ergebnis zusammengebaut werden soll.
Das geht es aber einigermaßen unüberschaubar, wer sich dafür interessiert.
Nächstes Semester gibt es die Einführung in die Computerlingvestic.
Da mache ich sowas
So, damit das Ganze handhabbar ist,
gibt es einen Makromechanismus, der also diese etwas unhandliche,
interne Regelstruktur erzeugt, indem ich diesen Doppel-Pial-File verbände.
Dann wird sofort beim Einlesen der Ausdrücke das umgewandelt in diese interne Struktur.
Das kann man also am Prompt sehr schön übersehen.
Man macht ein Assert für diese Regel. Und wenn man hinterher das Listing macht,
dann sieht man, dass es intern plötzlich so aussieht.
Das wird also beim Einlesen bereits erledigt,
das ist ein Operator, eine Makro-Anwendung, die da passiert.
Und das Gleiche eben auch für die Für die Wörterbucheintragungen,
die werden eben auch gleich in die gewünschte interne Differenzlistennotation transformiert.
Und auf die Art und Weise kann ich eben jetzt Grammatiken
auf diese Art und Weise schreiben und das ist einigermaßen überschaubar.
Das sieht fast so aus, wie es in der theoretischen Informatik
auch benutzt wird. Nur, dass das Komma jetzt und
der Punkt noch hinzugekommen ist. Aber der sagt eben,
ein Satz setzt sich aus NP und VP zusammen,
eine NP aus Artikel und Nomen und so weiter und so fort.
Und das ist das Wörterbuch dazu. Und ich muss natürlich,
da ich jetzt im Endeffekt Differenzlisten habe,
wenn ich das alte Pädikat,
bist du ein Satz oder generiere mir einen Satz,
beibehalten will,
muss ich das natürlich auf die Differenzlistennotation umwandeln
Indem ich sage, der hintere Teil ist wieder die leere Liste.
Das ist genau wieder der gleiche Trick. Ich möchte eine reale Liste,
eine klassische Liste als Ergebnis bekommen und reinstecken,
dann muss ich eben dafür sorgen, dass der hintere Teil darauf instanziert wird.
Im Übrigen, hier sehen Sie es,
hier habe ich ein Komma genommen und nicht im Backslash.
Das ist vollkommen äquivalent dazu. Ich hätte auch den Backslash
hinschreiben können. Muss es dann natürlich durch alle Grammatikregeln und
so weiter konsistent durchhalten
Gut, dann haben wir die Syntax für solche Definite Claw-Scramer-Regeln,
weil wir definite Klauseln verwendet haben. Das ist genau das,
was wir im Prolog die ganze Zeit benutzt haben.
Ist noch ein bisschen erweitert. Wir können zum Beispiel auf
der linken Seite hier noch Terminalketten angeben. Wir können wieder
Disjunktionen in die Regeln hineinschreiben und das ist das Interessanteste.
Wir können beliebige Ziele noch mit hinzufügen.
Wir können also an eine kontextfreie Regel noch reinschreiben,
welche zusätzlichen Bedingungen erfüllt sein müssen, damit diese Regel angewendet werden kann.
Und damit sind wir wieder bei einem Formalismus,
die erheblich ausdrucksstärker ist, als das,
was ursprünglich mal als kontextfreie Grammatik der Ausgangspunkt war.
Ach, wie ja, by the way, wir können auch Cuts da reinschreiben.
Also alles das, was uns Pianologikprogrammierung so zur Verfügung kam,
kann auch in solche Regeln mit hineingenommen werden.
Ist ja kein Wunder. Denn es wird ja nur per
Marco-Expansion in eine ganz stinknormale Klausel übersetzt. So,
und damit haben wir eben beliebige Listen als Terminalsymbole, nicht nur Einzelwörter.
Wir können auch Liste von Termen als Terminalsymbole verwenden und so weiter.
Wir können Steuerelemente einbauen und das, was das Ergebnis ist,
ist, sind im Prinzip kontextsensitive Regeln.
Also das ist der nächst höhere Grammatiktyp, der über die kontextfreien Grammatiken bereits hinausgeht.
Aber es ist weder noch eine Subklasse davon, nur die, die rechtzeitig kontextsensitiv sind. Gut,
und das kann man jetzt in der verschiedenster Hinsicht benutzen,
man kann diese Grammatiken beispielsweise verfeinern,
dass es hier nun mal angedeutet, wie gesagt,
das ist nur im Hinblick auf die Sprachverarbeitung jetzt,
wer sich dafür interessiert, ist herzlich eingeladen für das integrierte Anwendungsfach Computerlingvestic.
Ich kann eben Strukturbeschreibungen erzeugen, indem ich sage,
die NP liefert mir eine Strukturbeschreibung, die VP liefert mir
eine Strukturbeschreibung und ich baue sie zusammen in eine Struktur,
die ich es von irgendetwas nenne
Und genau hier so, der Artikel liefert mir eine Strukturbeschreibung,
das Nomen liefert mir eine Strukturbeschreibung und ich baue sie zusammen in eine Strukturbeschreibung.
Und dann kriege ich eben im Endeffekt Strukturbeschreibungen für einen Satz
heraus, die dann so aussehen.
Zum Beispiel, das ist jetzt nur eine Variante,
das kann man nach Gusto machen,
wie man das möchte
Müsste genug sein, ja. Dass ich eben eine Baumstruktur habe,
die eben so aussieht, ich habe einen S-Knoten,
der verzweigt in einen N-Pick-Knoten und einen VP-Knoten, der NP-Knoten,
verzweigt in einen D-Knoten und einen Endknoten und der VP-Knoten
in einen V-Knoten und das ist das Wort der,
das ist das Wort Mann und das ist das Wort lacht.
Ja und dann habe ich eine Strukturbeschreibung,
die ich in die Semantik-Interpretation hineinschicken kann, um rauszukriegen,
was sagt dieser Satz eigentlich über die Welt
Das ist der einfachste Fall, dass die Strukturbeschreibung genau
dem Regelmechanismus folgt. Ich hatte es vorhin schon angedeutet,
wir können das vom Regelmechanismus oder von der Regelstruktur lösen und dann können wir beliebige Ausgabestrukturbeschreibungen erzeugen,
die also überhaupt nicht mehr an die Regeln gebunden sind.
Gut, ach so, hier habe ich es ja auch aufgeschrieben, ja.
Also dann kriege ich eben einen solchen Satz hinein und raus
kommt eine solche geschachtelte Struktur, die ich als Baum interpretieren kann.
Gut, das ist das,
die Unabhängigkeit von der Regelstruktur, letzte Anwendung ist,
dass ich irgendwann mal vollkommen vor den Kopf gestoßen war,
als ich Programme gesehen habe, Prolog-Programme gesehen habe,
die mitten im Prolog-Programm plötzlich definiert, ja, definitiv Claws-Germer-Regeln enthalten haben.
Und da das ja ein ganz normaler Mechanismus des Prolog-Systems ist,
dass ich diesen Operator mit dem Doppelfeil verwenden kann,
mit der Interpretation, ich bin eine kontextfreie Regel über Differenzlisten,
kann ich das in jedem ganz normalen Programm verwenden.
Also beispielsweise, wenn ich hinschreibe, einen solchen Ausdruck,
eine solche Klausel,
wird das ja beim Einlesen sofort übersetzt in eine solche Klausel
Also, wenn du A von A,
B berechnen willst, dann berechne zuerst B,
was A bekommt und ein X daraus berechnet,
dann berechne aus dem X, was hier erzeugt wird,
ein Y und dann berechne aus dem Y, was hier erzeugt wird,
hier ein B und das kriege ich hier wieder zurück.
Das ist also eine Programmstruktur,
die des Durchschleifens von Informationen,
wenn ich eine Art Pipe-Line aufbauen will,
diese drei Teilziele müssen in der Reihenfolge abgearbeitet werden,
sodass das Ergebnis des ersten Teilziels als Input für das
zweite Teilziel dient und der Output des zweiten Teilziels als Input
für das dritte Teilziel dient
Ja, dann kann ich das einfach so hinschreiben und
dann ist das genau ein Programm, was dieses tut.
Und an der Stelle sieht man die Verbindung zu Mercury wieder.
Das ist genau das, was Mercury macht.
Das nimmt unabhängige Teilziele, so wie sie hier stehen.
Mit der Bedeutung, die sind erstmal per se Reihenfolge unabhängig.
Wenn ich eine Reihenfolge der Berechnung erzwingen will,
kann ich das über die Differenzlisten machen.
Indem ich also sage, ja, okay,
der Output hier muss als Input hier hineingehen.
Und damit habe ich festgelegt, dass die Reihenfolge von B und C
nicht mehr beliebig ist, sondern genau so realisiert werden muss.
Und damit ist die Parallelität natürlich infällig. Ich brauche dann
den Input hier möglicherweise, um das überhaupt aktivieren zu können.
Dann sind wir bei der klassischen Links-Nach-Rechtsverarbeitung von Prolog angekommen,
solange das nicht spezifiziert ist, gelten die Teilzähler als beliebig durchführbar.
Aber das ist Mercury, das ist nicht Prolog.
In Prolog wäre es eben so,
dass auf die Art und Weise immer diese sequenzielle Struktur induziert wird,
dass das linke Teilziel Input für das nächste produziert und
das produziert Input für das nächste Teilziel
Also das kann man in jedem ganz normalen Prolog-Programm dann
auch als stilistisches Mittel zum, ja, Codeschreiben benutzen.
Ich war nur das erste Mal ein bisschen vom Kopf gestoßen und dachte mir,
mein Gott, was passiert jetzt hier,
bis ich gesehen habe, da passiert überhaupt nichts Spannendes.
Das ist einfach nur die Ausdrucksmittel von Prolog bis zum letzten ausgereizt.
Das Ganze kann man dann erweitern, was wir jetzt gemacht haben,
ist ja nur ein String zu Baumtransducer.
Wir geben eine Zeichenkette hinein, wir kriegen einen Baum heraus
Zeichenketten zu Zeichenketten, Transdios,
da kann man natürlich auf die gleiche Art und Weise realisieren,
die sind sogar noch einfacher, weil nur endliche Automaten dahinter stecken.
Ich kann das aber eben auch erweitern oder ich kann das
in umgekehrter Richtung benutzen und kriege dann allgemeine Baum zu Baumtransformatoren.
Ich schicke einen Baum hinein und kriege einen anderen übersetzten Baum dabei heraus.
Das ist zum Beispiel die Basis für die maschinelle Übersetzung,
dass ich eine Baumstruktur aus dem Englischen in eine Baumstruktur im russischen beispielsweise übertragen muss.
Da sind die Konstituenten anders angeordnet, da sind die Strukturen
völlig anders aufgebaut. Also nehmen Sie nur immer,
dass mein Lieblingsbeispiel, wie übersetzen Sie I like to swim
Im Englischen ist es eine Struktur, wo das Verb oben steht.
Da steht das Ei drin und hier steht das Verb,
like. Und hier ist eine Komplimentstruktur. Fremden wir es
mal so, das ist hier die To-Swim.
Ja, da ist eine solche Struktur.
Wenn sie das Gleiche im Deutschen machen, sieht es
völlig anders aus
Dann ist es eben, ich schwimme gerne.
Das sieht hier oben also noch genauso aus.
Bis dahin hat sich nichts geändert. Ach so,
das muss ja dann ich heißen, nicht mehr Ei,
okay. Und hier steht jetzt das Verbschwimmen.
Und hier steht ein Adverb dazu. Das soll ein Fow
sein und das ist ein Adverb und das ist gern.
Da sehen Sie eben, das ist eine völlig andere Struktur.
Das, was das Like wird als gern übersetzt,
steht, aber nicht mehr in der Verbposition.
Da steht jetzt
das Schwimme drin
Und solche Transformationen kann ich machen,
indem ich allen Baum reinstecke und ich kriege den anderen Baum raus,
den ich dann in der anderen Sprache wieder verbalisieren kann.
Das ist also die Basis für maschinelle Übersetzungssysteme. Okay,
ja, ich kann sogar eins machen und das ist dann das Ende der Fahnenstange,
dass ich die definitorische Basis der Grammatiken verlasse.
Alle Grammatik in der Trumpsky-Hirarchie gehen immer davon aus,
dass sie einen endlichen Symbolvorrat haben.
Und wir haben ja gesehen, dass wir sowas wie Pianozahlen
zur Verfügung haben
Und wenn wir in den Symbolvorrat noch Peano-Zahlen mit einbauen,
dann haben wir eine indizierte Grammatik, die jetzt ein Symbol,
nennen wir es A, mit beliebig vielen NECs versehen kann.
Denn es gibt für die PA-Notaren keine obere Schranke.
Das sind natürliche Zahlen. Es gibt keine größte PA-No-Zahl.
Und damit haben wir nicht mehr endlich viele Symbole A,
sondern wir haben unendlich viele Symbole A.
Und dann können wir Grammatiken schreiben, die so aussehen,
als ob sie reguläre Grammatiken wären, aber in Wirklichkeit kontextfreie Sprachen beschreiben.
Oder wir können kontextfreie Regeln schreiben und da drin Symbole zählen
und können auf diese Art und Weise
Kontextsensitive oder Genauer die Klasse der indizierbaren Sprachen generieren.
Auf die Art und Weise können wir die Grenzen zwischen den
verschiedenen Ebenen der Trumpsky Hierarchie durchaus verschwimmen lassen.
Und das ist ja gar kein Wunder. Wenn Sie sich
über erinnern, was ist die einfachste Kontext sensitive Sprache,
die nicht mehr kontextfrei ist.
Hat es noch jemand in Erinnerung? Müsste irgendwo mal
in FDI 1 passiert sein? Noch passiert es.
In Bälde wird es keine Automaten und Grammatiken mehr geben in
der Theorie
Weiß noch jemand, wie sieht eine Indizier...
Nehmen wir einfacher. Was ist die einfachste kontextfreie Sprache,
die nicht mehr regulär ist? Machen wir es an der
Grenze, dann können wir es eher überschauen, bitte?
Ja, das wird auch. Ja, genau,
natürlich, das ist sie, die Klammersprachen, ja,
also öffnende Klammern müssen immer genauso viel da sein wie schließende Klammern.
Das ist eine kontextfreie Sprache und deshalb brauche ich einen Keller,
Speicher, um festzustellen, ich pushe eine Klammer auf den Kellerspeicher,
ich pushe noch eine Klammer auf den Kellerspeicher,
ich poppe eine Klammer wieder,
ich poppe noch eine Klammer und dann weiß ich,
dass Klammergleichgewicht stimmt
Das kann ich für mit endlichen Automaten nicht machen.
Jedenfalls nicht dann, wenn die Einbettungstiefe der Ausdrücke beliebig ist.
Weil ich in einem endlichen Automaten immer nur endlich viele Zustände
habe, um das zu modellieren. Ich kann sagen,
hier ist der Zustand, ich habe eine Klammer gesehen.
Das ist der Zustand, ich habe zwei Klammern gesehen,
das ist der Zustand. Ich habe noch eine Klammer offen
und das ist der Zustand, ich habe das Gleichgewicht wiederhergestellt.
Dafür brauche ich unendlich viele Zustände und die liefert mir ein
endlicher Automat nicht. Da steckt im Begriff des endlichen Automaten
drin. Und ich bin jetzt aber in der Lage,
durch eingebettete Strukturen, genau unendlich viele Symbole zu erzeugen.
Das ist das, was es mir ermöglicht. Ich bin
also in der Lage, jetzt ein Ding zu schreiben,
was Grammatikregeln verwendet, die wie reguläre Grammatikdinge aussehen,
aber eben da drin ein Zählmechanismus realisieren.
Und dann können sie eben mitzählen und sagen, okay,
ich habe jetzt wieder einen Klammerausdruck, aber der tiefe Eins am Weckel.
Ich habe einen Klammerausdruck, der tiefe Zwei am Wickel.
Ein Klammerausdruck der tiefe Drei der 4-4-
5 und so weiter bis unendlich
Es gibt keine, natürlich ist es immer endlich,
aber es gibt keine obere Grenze dafür.
Ich kann immer noch mal eine Ebene weiter nach unten gehen.
Und da habe ich eben mit endlich vielen Zuständen,
aber jeder Zustand enthält einen Index, der keine obere Schranke hat,
habe ich eine kontextfreie Sprache modelliert.
An der Stelle kann man also durch solche Zählmechanismen die Grenzen aufweichen.
Die Regeln sehen dann eben aus, als seien sie regulär.
In Wirklichkeit sind es kontextfreie Regeln wegen dieses Zelapparates.
Oder aber die Dinger sehen aus, als seien es kontextfreie Regeln,
aber in Wirklichkeit sind es kontextsensitive Regeln
Gut, das ist dann der Extremfall,
der geht zurück auf Alain Kolmerauer,
das ist der Erfinder von Prolog überhaupt,
der das erste Prolog-System geschrieben hatte,
der hat sich für solche Sachen interessiert, also der Extremfall,
den er hat, ist dann eine sogenannte Metamorphosis-Grammatik,
die also volle Touring-Mächtigkeit hat.
Und das ist aber, das ist etwas,
was man als Normalsterblicher nur noch in einfachsten Beispielen überhaupt übersehen kann,
wie das ist.
Das ist genauso, als wenn man versucht,
praktische Probleme auf der Ebene
Tatouring-Maschine zu implementieren
Auch das Problem, dass man irgendwann mal nicht mehr versteht,
was man da eigentlich macht.
Die einfacheren Formalismen sind ja eben immer genau deshalb gut geeignet,
weil wir noch verstehen und nachvollziehen können, was dort tatsächlich passiert.
Gut, das sind Beispiele dafür, die müssen wir jetzt nicht hier reingehen.
Ja, das ist im Prinzip das Programm, was ich
hier abwickeln wollte. Wir haben noch 20 Minuten,
ich könnte nochmal auf die Logikgrundlagen eingehen,
des Ganzen also den Bezug zur formalen Logik herstellen,
aber das werde ich mir verkneifen.
Nur also so viel die Hornlogik, die wir hier verwenden,
also diese Klauseln, ist der einfachste Fall eines Prädikatenkaltgüls erster Stufe.
Das, was wir hier vorliegen haben, sind implizit allquantifizierte Ausdrücke.
Wir haben also nur Altquanturen und weil alles allquantifiziert ist,
schreiben wir die gar nicht erst hin.
Es gibt also keine Existenzexistenzquantoren. Und ja,
das ist der Preis, den wir dafür zahlen,
dass wir das Ganze überhaupt als Programmiersprache verwenden können.
In dem Augenblick, wo wir beispielsweise volle Prädikatenlogik erster Stufe verwenden würden,
ist das Ganze nicht mehr effizient zu benutzen.
Und das ist aber immer das Schöne daran,
ich kann es so weit vereinfachen,
dass es als praktisches Hilfsmittel
tatsächlich funktioniert
Und ich erbe dann eben die ganzen Vorteile des Prädikatenkalküls.
Das haben wir ja zur Genüge diskutiert. Also insbesondere so etwas wie die Richtungsunabhängigkeit,
weil es eben ein relationaler Formalismus ist, in den ich
nicht von vornherein eine Verarbeitungsrichtung hineinstecke wie in der funktionalen Programmierung.
Da ist von vornherein in der Funktion festgelegt,
wie sie zu benutzen geht,
bei der Relation spezifiziere ich ja nur statische Zusammenhänge,
die zwischen Objekten meiner Programmierwelt bestehen
Gut, ich glaube, das soll es auch dann genügen für heute.
Es sei denn, sie haben noch irgendwelche Fragen,
bin ich gerne bereit, die zu beantworten?
Dann sollten wir aber, glaube ich, sortieren,
ob das allgemein interessierende Fragen sind oder Dinge,
die wir zu zweit klären können.
Okay, gut, dann wollen wir das mal nicht länger ausverzögern hier.
Ich bedanke Ihnen für die Aufmerksamkeit und wünsche Ihnen alles Gute.
Alles Gute auch für die Klausur