Ja, guten Morgen, meine Damen und Herren.
Lassen Sie uns mal loslegen. Wir haben ja angefangen,
uns mit der relationalen Programmierung zu befassen.
Ich möchte ganz kurz noch eine knappe Wiederholung machen,
damit wir den Einstiegpunkt finden für die Beispiele,
die ich Ihnen heute noch zeigen möchte.
Also was wir gesehen haben, sind zwei Dinge.
Wir haben in Reckett eine eigene Prolog-Version eingebaut und diese
eigene Prolog-Version
Ist eigentlich komplett in Rackett geschrieben, bis auf zwei Stellen
und an diesen zwei Stellen haben wir eigene Special Forms eingeführt,
als Sprachelement, um Pull-Op möglich zu machen.
Das eine ist die Zusicherung von Fakten. Gehen wir nochmal. Diese Stelle hin,
wir haben dieses Makro, das Sird-Makro als Special-Form geschrieben,
die dann in der Lage ist, eine Datenbasis anzulegen und dort verkauft zuzusichern.
Und wir haben es gesehen am Beispiel hier der Elternrelation, ja.
Damit können wir eine Datenbank aufsetzen, in der bekannt ist,
wer ist mit wem verwandt, wer hat welche Eltern und so weiter.
Und wir hatten zwei Arten von solchen Klauseln, die etwas zu sichern.
Das eine sind einfach Tatsachen, die wahr sind,
ohne jegliche Vorbedingungen, Klammeraufnahme einer Relation und dann zählen wir auf,
wer mit wem in dieser Relation dann so versippt ist.
Das andere, was wir hatten, waren Klauseln,
die von einer Vorbedingung abhängig sind, wo wir ausrechnen müssen,
ob die Vorbedingungen erfüllt sind.
Und da kommt dieser Opperator ins Spiel. Und wenn wir das so machen,
da können wir sagen, also ja, zwei Elemente,
X und Y, hier wird geschrieben als Variablen,
die sind verwandt, wenn sie dieselben Eltern haben.
Also hier hatten wir in der Elternrelation gesagt,
die Eltern von X,
Mutter, Vater sind dieselben wie die Eltern von Y.
Und wie funktioniert Pro-Look
Wie benutzen wir es? Zum einen benutzen wir es so,
dass wir, ja, es eigentlich benutzen, als eine Programmiersprache für Denkfaule.
Ohne es abwertend zu sagen. Es ist ein unglaublich mächtiges Werkzeug. Ja,
wir, statt alle Relationen,
Beziehungen selbst im Kopf auszuwerten oder dabei vielleicht Flüsterigkeitsfehler zu machen,
erknallen wir dem Prolog einfach alles für die Füße,
vor die Füße, was wir über dieses Problem wissen.
Ja, wir schreiben alle Fakten auf, die wir kennen.
Wir schreiben alle Zusammenhänge auf, die wir in Form
von Regeln
aufstellen können
Dann liegt Prolog für uns los und sucht Antworten,
wenn wir eine Frage haben. Also wenn wir jetzt wissen wollen,
ob Peter mit Petra verwandt ist, ob sie Geschwister sind,
dann werden wir einfach fragen, Sippling, Peter, Petra.
Und Prolog wird alle seine Fakten durchgehen. Unsere Anfrage per Unifikation vergleichen und feststellen,
ob es diese Anfrage erfüllen kann, indem man die Variablen,
die wir in der Anfrage haben, an Wertebilde bindet.
Und wenn das gelingt, dann erfahren wir als Antwort,
ja, es ist richtig, unter der Bedingung,
das Geschwisterteil A. Petra ist und das Geschwisterteil B. Peter.
Das ist die Art, wie wir programmieren. Also die,
wir sichern alles zu, was wir wissen,
gehen es aber nicht selbst durch und kombinieren und denken nach
und schreiben auch keine Suchfunktionen mehr.
Das ist im Kern vorhanden. Der Kern wird hingehen und
für uns ist durchforsten und versuchen durch Schlussfolgerungsregeln es herzuleiten.
Und dann zu sagen, ja, es geht oder nicht.
Und wir haben paar spezielle Prädikate eingeführt, die brauchen wir gleich.
Deshalb erwähne ich sie nochmal. Wenn wir feststellen wollen,
ob zwei Dinge gleich sind, dann verwenden wir keine Gleichfunktion,
sondern wir verwenden eine gleiche Relation, die einfach durch Unifikation vergleicht,
sind sie denn gleich.
Und die hatten wir das letzte Mal eingeführt, indem wir zugesichert haben.
Ein X ist gleich einem anderen X, ja.
Wenn das mit dem wir unifiziert. Dieses X ist gleich
dem, so können wir die Gleichrelation definieren.
Und wenn wir jetzt wissen wollen,
ob etwas gleich ist,
dann verwenden wir eben
diese Relation
Wenn wir etwas negieren wollen, haben wir noch eine spezielle Relation,
die einen speziellen Apparator, den wir davor setzen, das Not.
Und damit können wir auch einen Ungleich definieren. So, okay.
Also damit waren wir dann in der Lage zu sagen,
X und Y sind verschwistert, wenn sie dieselben Eltern haben,
aber X ist nicht sein eigenes Geschwisterkind,
sondern die sind X und Y müssen ungleich sein.
Die dürfen nicht unifizieren. Das haben wir
hier ausgedrückt
Und logisch, wenn wir unsere Datenbank aufsetzen,
dann wollen wir ja verschiedene Sachen erreichen.
Wir wollen erstens sagen, Dinge sind alternativ möglich.
Das machen wir einfach, indem wir die Fakten, unabhängig voneinander eintragen.
Das führt zu einem großen Urder zwischen all den Fakten.
Wenn wir wollen, dass etwas gleichzeitig erfüllt sein muss,
dann schreiben wir es auf die rechte Seite von diesem Pfeil.
Und alles, was hier steht, ist automatisch mit unverbunden.
Konjunktion drücken wir in der Liste der Prämissen
Für eine unserer Klausel, die Disjunktionen, indem wir Alternative Klauseln aufschreiben.
Und die Negation, indem wir ein Not davor schreiben.
So, und im Pro-Log ist es so, dass alles,
was nicht bewiesen werden kann, als wahr automatisch angenommen wird als falsch.
Weil das muss man wissen,
das kann einfach in manchen Anwendungen schwierig sein,
wenn wir es nicht modelliert haben, wird erstmal gesagt,
das gibt es nicht.
Und das gibt es nicht oder das ist falsch,
heißt nur, ich weiß nichts drüber.
Nicht, dass es in der realen Welt
wirklich nicht wahr wäre
So, an, gehen wir mal an die Stelle,
wo wir jetzt weitermachen wollen.
Okay. Genau. Wir hatten das Zipra-Ritzel besprochen. Als
eine Anwendung. Ich wollte Ihnen vorher noch mal zeigen,
wie wir mit pro Look in Recket umgehen.
Also zum einen, damit Sie Ihr Brüllock aufrufen können,
schreiben Sie in Ihren Pfeil rein.
Langswindel, ja. Weil die Special, die Makros,
die wir verwendet haben, das sind Windelsprachelemente.
Ich habe nicht die Makrodefinitionen von Recket genommen,
sondern wirklich die die aus dem Zwindel, aus dem Zählers-Implement importiert wird.
Deshalb brauchen wir Langswindel. Und das, wenn wir das volle Windeln nehmen,
dann sind eigentlich diese Sachen auch alle schon drin. So,
und damit wir Pro-Log ins Scream verwenden können oder Pro-Log in Recket,
je nachdem, welche Variante der SE3-PUPC sich runtergeholt haben,
schreiben Sie hier, Require, SE3, Pro-Log ins Skim rein.
Und wenn Sie das gemacht haben, drücken Sie den Startknopf,
hatte ich hier schon mal gemacht. Fürs Aufgabenblatt,
was Sie jetzt haben, da gucken wir mal,
da ist hier auf dem Aufgabenblatt Beispiel Datenbank drin und versuchen wir mal.
Sie hat ganz da. Klammerchen. Erst haben wir
alle Klammern gut. Also das sind die Relationen,
die sie auf unsere Aufgabenplatz stehen haben,
haben sie sicherlich alle jetzt schon angeschaut, auch in den Übungen durchgesprochen.
Und wenn wir in diesen Relationen jetzt suchen wollen,
Anfragen machen wollen, dann schreiben wir es einfach hier rein.
Und so. Also hier frage ich jetzt,
Ist das Buch P201 vorbestellt und wer hat es vorbestellt,
wer es vorbestellt hat, erfahren wir durch die Variable X. Ja,
da kriegen wir die Lesernummer zurück.
So ist diese Vorbestellungsrelation definiert. Hier sehen wir die Signaturvorbestellung,
Signatur, Lesernummer. Gut. Und was dann passiert,
ist, wir erfahren, es ist wahr,
wenn wir für X die Nummer 104 verwenden.
Und jetzt könnte es ja sein, dass noch jemand es haben will.
Nach alternativen Fragen wir, indem wir unsere Anfragen mit einem
oder verbinden. Deshalb geben wir einfach das Semikulan ein,
das ist das Logische
Ja, und dann fahren wir, aha, Leserin der
Zwei will es auch. Und sind das jetzt alle?
Geben wir nochmal ein Y ein und dann sagt uns,
das Programm einfach kurz und schmerzlos no.
Ja, das ist relativ unhöflich und kurz,
aber das können wir erfahren. So, das andere,
was wir auch schon geübt haben, war,
wie wir bei den Relationen, die wir definiert haben,
im Sinne von Datenbanktechnik an Joinen machen können.
Also wie wir danach fragen können,
ob Dinge aus zwei solchen Relationen erfüllt sind. Ja,
wenn ich jetzt wissen will, Ist ein Buch ausgeliehen und vorbestellt,
dann könnte ich die Anfrage so machen.
Moment. Ich habe hier jetzt nach der Vorbestellung gefragt,
aber ich will jetzt auch wissen, ob es ausgeliehen ist zurzeit?
Oder wenn wir jetzt wissen wollen, ja,
nehmen wir hier mal noch irgendeinen Y rein?
Dann erfahren wir ja, das ist ausgeliehen von Leser 100 und vorbestellt von Leser 104.
Und da gibt es noch andere Lösungen. Gut,
wenn wir jetzt wissen wollen, ob jemand so schusselig ist,
dass er das Buch, was er selbst ausgeliehen hat,
wieder vorbestellt hat, dann Moment.
Dann können wir mal Co-Referenz versuchen. So,
indem wir hier statt nach Y nach X fragen.
Moment, großes
Ja, was habe ich jetzt hier gefragt?
Ist das Buch P201 vorgestellt und ist es ausgeliehen?
Und zwar von derselben Person, weil das X hier zweimal auftaucht.
Wenn es in derselben Anfrage oder in derselben Klausel,
derselbe Variablenname auftaucht, meint es, denselben Wert.
Ja. Und da haben wir dann ein No,
in dem Fall, liegt nicht gefunden.
Also versuchen Sie für die Lösung dieser Aufgaben es hinzukriegen,
dass Sie langes Windel verwenden, dass Sie dieses Require reinnehmen und
dann können Sie hier die Relationen reinschreiben und damit testen.
Das Zweite, was wir noch auf dem Aufgabenblatt haben,
das wollte ich auch noch mal ganz kurz ansprechen.
Das ist, dass man es übt und trainiert,
Unifikation zu machen. Also dieses musste da so drei Relationen
zueinander in Beziehung setzen, mit Joyen, Anfragen zu beantworten.
Das sollten sie während der Klausur wirklich gut können.
Und was sie auch gut können sollten aus dem Aufgabenblatt,
falls ihre Übungs hält, weil es ihnen nicht gesagt haben,
das ist den ersten Teil von dem jetzigen Aufgabenblatt
Diffizieren wir Ausdrücke. Und dann, wie können wir
das machen? Sie haben zum Beispiel in der,
auf dem Aufgabenblatt stehen, in der kleinen Tabelle mit Dingen,
die zu unifizieren sind, Ausdruck 1 ist Laptop,
Marke schwarz, Ausdruck 2 ist Laptop, Macbook-Farbe.
Und das gefragt, ja, unifiziert ist, ja oder nein.
Und wenn ja, mit welchen Variablenbindungen. Wenn sie die
oft dabei lösen, versuchen sie es zuerst im Kopf
viel zu kriegen und festzustellen, ob es unifizieren kann.
Wir hatten besprochen, was dann Ausdrücke unifizieren,
wenn der Prädikatsname derselbe ist, wenn die Stelligkeit dieselbe ist.
Und wenn dieselben Variablen an dieselben Werte gefunden werden.
Also in diesem Fall Prädikatsnahme stimmt es in beiden Fällen
Laptop. Hier sollte MacBook, fällt zusammen mit Marke,
also würden wir die Variablenmarke an MacBook finden.
Marke als Variable kommt nirgendwo anders vor, das geht gut.
Farbe können wir an Schwarz binden, kommt auch nirgendwo anders
vor,
es geht auch gut
Also sollte rauskommen, nach allen menschlichen Ermessen. Ja,
es unifiziert und Marke ist Macbook und Farbe ist schwarz.
Also so füllen sie bitte die Tabelle aus. Wenn sie jetzt sicher sein wollen,
ob es wirklich so ist, fragen sie Brüllock.
Und das können wir eben machen, indem wir das Gleichheitsrelation nehmen.
Ja, die Gleichheitsrelation stellt fest, ob es unifiziert.
So ist es definiert. Deshalb können wir diese Aufgabe lösen,
indem wir einfach pro Lock fragen.
Vergleich mal die beiden Ausdrücke, von denen wir nicht wissen,
ob sie urifizieren, mit Hilfe der Gleichheitsrelation.
So, und die schreiben wir jetzt hier mal rein
Hoffentlich geht's gut. Ja, und dann erfahren wir genau
das, was wir uns auch gedacht haben. Marke,
die Variable, muss ein MacBook gebunden werden,
damit diese Ausdrucksstruktur redet gleich zu dem hier ist,
mit den gleichen Werten und Farbe muss ein Schwarz gefunden werden.
Und das machen Sie bitte für alle Ausdrücke in der Tabelle,
wie gesagt, als einfachste Pro-Log-Aufgabe werden Sie so eine Unifikationstabelle kriegen.
Ja, das kommt auf jeden Fall vor, also das üben Sie bitte.
Und das andere ist, Sie werden auch eine Aufgabe bekommen,
bei der im Wesentlichen drei Relationen steht und das wird gefragt,
Machen Sie Anfragen dazu.
Und das sollte also auch gehen. Deshalb üben Sie es gut,
dass Sie Anfragen stellen können,
wobei Sie Relationen in Zusammenhang bringen mit so einem Join.
Okay, dann machen wir das hier erstmal wieder zu und schauen nochmal auf das,
was wir das letzte Mal besprochen haben und machen da weiter mit einem ähnlichen Beispiel.
Nachdem wir Relationen eingeführt haben, werden einige nützliche Relationen eingeführt,
die wir immer brauchen. Und die haben wir im Zebra-Ritzel verwendet.
Eine wichtige Relation, die sich so von der Funktion unterscheidet,
wie wir sie in Recket haben, ist Member.
In Recket ist Member eine Funktion. Wir können fragen,
ist Element 1 oder so Element einer Menge? Wir können fragen,
oder eine andere Funktion schreiben, die testet,
erzähle mir alle Mengen auf, in denen dieses Element vorkommt.
In Prodoc ist es etwas, was Ungerichtet ist,
wir sagen einfach, das Element steht mit einer Menge in einem Zusammenhang,
dass sie ein Element ist, in dieser Relation steht es.
Und wir können diese Relation in verschiedenen Richtungen durchsuchen.
Und das machen wir rekursiv. Ja, schauen wir auch
das nochmal an, wie wir rekursiv eine Relation definieren.
Wir sagen Element eins ist Element der Menge,
die hier steht, wenn das Element das erste Element der
Liste ist
Im Pro-Lock, in der richtigen Brücke, Mirrsprache Pro-Lock wäre
hier ein senkrechter Strich für den Konsortberater. Der sagt,
ich habe eine Liste, die beginnt mit diesem ersten Element,
dann kommt der senkrechte Sprich als Konsapparator und dann kommt der Rest.
In Pro-Lock in Reckis habe ich das Kons genommen,
was wir schon als Infix-Apparator haben, das ist Licht weg, der Punkt.
Also das hier ist so zu lesen, das ist eine Liste,
die beginnt mit dem Element Item und dann kommt eine Restliste.
Und wir haben zwei Variablen, einmal fürs erste Element,
wo wir das erfahren können durch Unifikation und einmal den Rest.
Und das ist also unsere erste Zusicherung, in dem wir sagen,
Wenn das erste Element vielleicht dem Gesuchten ist, klar,
dann ist das Element der Liste.
Und außerdem muss man auch sagen, es ist Element der Liste,
wenn es im Rest vorkommt. Dann, das machen wir,
indem wir sagen, vorne darf irgendwas stehen.
Ja, diese 6 hier. Wir haben das Item, was versuchen.
Und wenn Pro-Locked beweisen kann, durchsuche in der Datenbasis,
dass dieses Item im Rest vorkommt, ist es auch Elementarliste.
Ja, okay. Also das Depreslember-Relation. Und
die können wir in verschiedenen Richtungen durchsuchen. Das haben wir
das letzte Mal auch besprochen. Aber ich möchte noch mal
daran erinnern, weil das der Punkt so wichtig ist
Wir können erst mal explizit fragen, kommt denn die Eins vor?
Ja, nehmen wir sagen, Member 1 in dieser Liste.
Und dann erfahren wir Yes. Ja, und dann haben wir Fragen,
kommst du noch mal vor, erfahren wir no.
Bei dem, wenn wir die etwas längere Liste haben,
muss es wann noch vorkommen, dann kriegen wir zweimal Yes.
Und wir können es als Konstruktor nehmen, indem wir fragen,
wie können wir eine dreielementige Liste bauen, die das Element Eins enthält?
Das war die Verwendung einer Relation in einer Anfrage als Konstruktor.
Also wenn wir Das habe ich hier gefragt,
wie sieht eine Lüfte aus, die ein Element 1 enthält?
Welche und die genau drei stellig ist. Und die Antworten,
die ich kriege, ist, na gut, eine dreistellige Liste,
drei elementige Liste mit dem Element 1. Könnte darin bestehen,
dass das erste Element gleich eins ist und die anderen beiden irgendwas.
Oder es könnte drinnen bestehen, das erste Element ist irgendwas,
der zweite muss eins sein, das dritte ist irgendwas und so weiter.
Wir kriegen drei Antworten, wie eine drei elementige Liste aussieht,
konstruiert einmal die Eins enthält. Ja? Kurze Frage. Und zwar,
ist das nicht da, als ich das eingangene wurde,
ein Mektor, weil wir legen jetzt vor, die Großplätze fest.
Es ist eine bestimmte Ausprägung von unendlich vielen Listen,
die es gibt. Also wir haben hier gesagt,
ich suche eine Liste, der genau drei Elemente.
Es ist kein Vektor, weil ich habe keinen Vektorzugriff.
Also ich kann nicht mit Vektorrecht drauf zugreifen. Also,
aber das soll, dieses Beispiel ist mir so wichtig,
weil es zeigt, dass Unifikation die Basis von allem ist,
wie wir mit Prolog programmieren.
Wir benutzen es, um auch bestimmte Positionen zuzugreifen,
ja, wenn ich das zweite Element haben will,
frage ich eben anschließend die Variable B ab.
Also die Akzessoren für Datenstrukturen bestehen darin, dass wir an den Positionen,
wo was sein soll, eine Variable einführen.
Und dann verwenden wir später diese Variable. Wenn wir
eine Struktur einer bestimmten Länge oder Größe haben wollen,
dann schreiben wir es einfach hin und zählen genauso viele Elemente auf,
wie drin sein soll.
Ja, und dann wird bei der Unifikation eben geguckt,
wie kommt es mit der Stelligkeit hin und ich kriege
alle Antworten,
die genau diese Größe haben
So, was wir auch noch angesprochen haben und wo eigentlich
die hohe Kunst im Prolog beginnt, das sind Prädikate zweiter Ordnung,
das entspricht so von der Mächtigkeit dem,
was wir an Funktionen höherer Ordnung im Regit hat,
einem funktionalen Programmierstil, ne?
Und ich habe nur eins davon angesprochen, das ist das
Prädikat Find All. Das ist ein Prädikat zweiter Ordnung,
mit dem wir uns Strukturen aufbauen können,
die alle Elemente enthalten, die eine Anfrage erfüllen.
Und die Struktur war von der Art, dass wir All sagen,
hier, schreiben wir einen Thermen hin.
Und das wären eben so Listen mit Variablen drin oder irgendetwas,
was so eine Struktur ist, die gefunden werden soll.
Und dann sagen wir hier ein Ziel,
das da schreiben wir alle Prämissen auf, die diese Strukturen erfüllen müssen.
Und hier führen wir eine Variable ein. An der Stelle
kriegen wir eine Liste zurück von allen Thermen, die dieses Prädikat erfüllen.
Und wenn wir jetzt unsere Vorfahrenrelation nehmen,
auch da nochmal kurz drauf gucken, dann könnten wir,
um jetzt alle vorfahren,
einer Person zu finden,
sagen,
find all
Der Therm besteht einfach nur drin in der Variablen.
Fraped. Predicessor, ja. Und der Ausdruck soll sein,
dass dieser Vorfahre für eine bestimmte,
dass diese Variable Brett in der Relegation Predicessor steht zu der Person,
die wir hier oben als Argument übergeben haben.
Und das Resultat wird eine Liste sein,
die an diese Variable Anthess Tests gebunden wird,
wo wir alle Vorfahren erfahren, ja.
Und auf diese Weise können wir sagen, okay,
wenn wir jetzt tragen, all and sisters von Walter,
also was sind bald das Vorfahren,
dann kriegen wir
diese Variable Anthess zurück
Steht also alles drin, was wir im Stammbaum gesehen haben,
wer in der Relation vorfahren ist. Ja, okay. Wir haben,
nachdem wir jetzt uns angeschaut hatten,
wie wir einfach so eine Datenbasis aufsetzen können mit Vorbedingungen und so weiter,
ein Beispiel durchgesprochen, da möchte ich kurz nochmal in die Struktur erinnern,
weil wir jetzt ein zweites Beispiel der gleichen Art machen wollen.
Also wir hatten eine folgende Frage aus dem Scientific American,
fünf Häuser in jedem Haus, wohnt jemand mit unterschiedlicher Nationalität, Häuser haben unterschiedliche Farbe
Und die Bewohner haben unterschiedliche Gewohnheiten. Also die Nationalität verschieden,
ihre Haustüre verschieben, ihre Rauch- und Trinkgewohnheiten verschieden.
Und dann haben wir gefragt, wo wohl in welchem Haus wohnt jemand,
der Wasser trinkt? Und wir hatten Zebra als Haustier.
Und die Einschränkungen waren eben von der Art,
was sie eigentlich deshalb spannend finde, weil sie zeigt,
wir können mit Prolog, weil es ja Symbole manipuliert,
gerade so ähnlich wie wir es auch in Record gesehen haben.
Wir können Einschränkungen die unterschiedlichsten Art kombinieren. Dasselbe Java zu machen,
wer eine endlose Fleißaufgabe mit Datenstrukturen und Bärtentypen,
weil wir eben für Haustüre für Trinkgewohnheiten,
für alles Mögliche müssten wir Datentypen einführen, ja und die Krise kriegen.
Hier schreiben wir die Regeln, die natürlich sprachlich gegeben sind,
praktisch als pro Locrelation hin mit den Symbolen und an
keiner Stelle haben wir einen Datentyp definiert.
Deshalb geht es so schnell und kurz. Wir hatten die einschränkten Regeln,
die wir hier hätten, jedes Source hat eine andere Farbe,
jeder besitzt der andere Nationalität,
Lieblingsgetränk und so weiter
Wir haben so verteilte Informationen gekriegt. Engländer im roten Haus,
Spanien hat einen Hund, im grünen Haus trinken einen Kaffee,
Ukraine trinkt Tee, das grüne Haus direkt rechts neben dem Elfenbeinfarbenen Haus und so weiter.
Mittleren Haus wird Milch getrunken im gelben Haus, cool geraucht.
Und der Kulsraucher ist ein direkter Nachbar vom Pferdebesitzer und so weiter.
Wenn wir das jetzt im Pro-Lock aufschreiben oder eben in Brüllock,
wie wir es eben hier gemacht haben, dann haben wir zunächst mal gesagt,
wir beschreiben ein Haus jetzt als eine Struktur, Relation,
Haus.
Und dann gibt es Nationalität, Torst der Zigarette,
Getränk und Farbe
Hatten noch zwei Hilfsprädikate eingeführt, die will ich jetzt nicht wiederholen.
Aber im Wesentlichen wird die ganze Arbeit für dieses Ritt
ritzelt durch die Member-Funktion als Konstrukteur gemacht. Also einfach,
weil es einen Engländer gibt, der im roten Haus wohnt,
sagen wir, für unser Haus, die Variable Haus,
die hier gebaut werden soll, muss gleich sein einer Struktur,
die enthält als ganz, als erstes ein Haus,
wo der Norweger wohnt, das wissen wir.
Sonst wirst du über den Norweger nichts, da stehen die ganzen Fragezeichen.
Im mittleren Haus wird Milch getrunken. Also wir können hier schon mal hinschreiben,
an der mittleren Stelle der Liste der von Fäusern,
haben wir die Hausrelation mit Milch und über die anderen wissen wir nichts.
Dann wissen wir, dass es einen Engländer gibt, der im roten Haus wohnt.
Und das haben wir einfach durch die Member-Funktion. Relation,
ja. Der Spanier hat einen Hund, ein Haus,
es gibt ein Haus mit einem Spanier und einem Hund.
Wir müssen sagen, das muss Member erfüllen.
Wir haben Memberhouse. Im grünen Haus wird Kaffee getrunken.
Wir haben Memberhouse, Ukrainer, der Trinité.
Und das rechts vom Elfenbein farbenen Haus ist ein grünes Haus.
Da wenden wir jetzt einfach mal eine andere Relation.
Diese ganze Liste des Nils, der jemand,
der Schmecken hat, raucht Winston und kurz und so weiter.
Einfach indem wir in der Member-Relation das Haus angeben,
mit den Werten in der richtigen Stelle belegt, ist das Richtige eingeschränkt.
Und ganz am Ende fragen wir noch, bringen wir unsere Anfragen unter?
Gefragt war doch, wer hat ein Zebra als Haustier?
Da fragen wir hier nach einem Z mit Zebra.
Und wir fragen, wir wollten auch noch wissen,
in welchem Haus wohnt der Wassertrinker.
Und da fragen wir eben Wasser und fertig
Und wenn wir jetzt diese Anfrage starten, Zipra-Relation für Hauses,
dann werden diese ganzen Zusicherungen des Member-Prädikats,
werden uns die Liste der Häuser so konstruieren,
wenn es möglich ist, dass alle die Einschränkungen erfüllt sind.
Und wir kriegen noch das Ergebnis,
wer den Wasser trinkt und wer es Zipra hat.
Der Norweger trinkt Wasser erstaunlich und der Zebra-Besitzer ist der Japaner.
Zebra gibt es in Japan auch nicht, außer im Zoo. Gut,
also die Liste der Häuser ist hier aufgestellt,
es sind genau fünf, warum sind es genau fünf,
weil wir in der Anfrage geschrieben haben,
bis zu der Häuser sind fünf,
Und das war's. So, also wir haben im Wesentlichen
die Relationen benutzt als Konstrukte für die Lösungsrelation. Und diese Liste Hauses,
die hier erzeugt wird, wird jetzt durch diese 15 Member-Aufrufe,
die wir alle hintereinander vermanscht haben, erzeugt.
Ich habe dasselbe Beispiel jetzt gemacht für das Rätsel aus dem Harry Potter,
Buch Harry Potter und der Stein der Weisen. Ja,
als ich das nämlich gelesen habe, fiel mir auf,
also Leute, ich glaube nicht daran,
dass das Rätsel Also entweder ist die Hermine wirklich super intelligent und kann auch noch hell sehen.
Oder aber die Autorin verschweckt uns, was jedenfalls mit den
gegebenen Einschränkungen ist, das Rätsel nicht lösbar.
Und das wollte ich überprüfen. Und klar,
da nimmt man pro Lock, wenn man Informatik studiert.
Dieses Rätsel, ja, wenn Sie sich nicht erinnern,
das war die Szene,
wo Harry Potter und Ham Joni in die Kammer der Schrecken kommen dort unten,
da stehen Sie vor dem Rätsel,
da stehen Reihe von Flaschen
Um weiterzukommen. Also auf der einen Seite ist Loda
das Feuer und auf der anderen ist es dicht,
da kommt man nicht durch.
Muss man einen Zaubertrank trinken, so.
Und was diese Flaschen enthalten ist in diesem Rätsel beschrieben.
Dingerlights before you wild safety lice behind,
two of assible help, you with every you would find.
Van the mong are 7, sensiblen Flaschen,
belet you move a head, another by transport the drinker back and statt.
Das ist auch nicht schlecht. Immerhin kommt man halt wieder raus,
ne? Two emong or numbers hold only netto vine.
Three of ass acculas, waiting hitten in line.
Truth, unless you wished to stay here
forever more
To help you and your choice, we give you this clues for first.
However sliyly the poison tries to high, do will always
find someone little wind slift side. Also das Gift ist immer links vom Levelbein
. Second, different are those sustand at eather end,
but if you would move onboard, nizer is your friend.
Also wir an den Enden stehen, die nützen einem nichts,
ja? Oder einfach ausfüllen. Aber sie sind unterschiedlich.
Third as you see clearly all our different size,
night that the warf no giant holds death and there inside.
Force. The second left und second on the right are twins,
you taste them, the different at first side.
Also die sind unterschiedlich groß, zweite von links und zweiter
von rechts. Und enthalten aber dasselbe. Ja,
Harry Potter und der Stein der Weisen so.
Ja. Ist das Rätsel lösbar? Wie gesagt,
als ich es gelesen habe, habe ich gedacht,
da fehlt noch was, weil die Autorin hat uns nämlich unverschämterweise nicht gesagt,
wie die Flaschen aussehen, ja?
Nur Harry Potter und Herr Myon, die sehen die Flaschen.
Wir wissen die Größe nicht. Und das sind also für uns
arme Leser, sind diese Regeln über Giant and Warf nutzlos.
Wir können das Rätsel nicht lösen. Also muss man einen Lösungsraum explorieren.
Ja, wir hatten jetzt einfach nach dem Muster-Zepra-Rätsel gedacht,
versuchen wir doch mal, das auch zu lösen.
Ja, Datenstruktur für eine Flasche. Die Flaschen haben einfach Namen,
damit sie eindeutig sind, Flasche 1, 2,
3 und sie haben einen Inhalt.
Und das sind sieben Flaschen, das wissen wir auch.
Also genau wie wir die Liste der fünf Häuser haben,
machen wir als Anfrage eine Liste mit fünf Flaschen.
Und konstruieren sie mittes Member, genau, wie es uns vorgemacht worden ist.
So, und wir wissen, es gibt genau drei Giftflaschen. Das heißt,
Für die Liste der Flaschen, die wir haben,
da gilt, es gibt ein Gift 1,
ein Gift 2 und ein Gift 3. Ja, die sind unterschiedlich.
Und kommen in der Liste der Flaschentour. Dann haben wir
zwei Flaschen mit Nether Design. Also es muss Flaschen geben,
die heißen Wein 1 und Wein 2 und deren Inhalt ist Wein.
Und die kommen in der Liste der Flaschen vor.
Und es gibt noch die Flaschen, die wir wirklich haben wollen,
nämlich den Schrank, der uns vorausbringt,
da wo wir endlich den bösen Zauberer erschlagen können und einen zurück,
wo wir heil nach Hause kommen und Hilfe holen
Gut, also trank voraus und trank zurückkamen wir noch.
Wir haben hier insgesamt, also bei sieben Flaschen,
das kann gut gehen, wir haben drei Gifte,
zweimal Wein und zwei nützliche.
Macht zusammen sieben. Und dann war gesagt worden,
weder der Riese noch der Zwerg,
für die Kleine und die Großkleinste und große Flasche ist der Inhalt kein Gift.
Und wir haben Regeln drüber, Twins, zwei Flaschen,
die dasselbe enthalten. Und wir haben noch eine Aussage,
dass eine Flasche uns nicht hilft mit Trank voraus
Diese Prädikat, da habe ich ja auch noch aufgeschrieben, als Hilfsprädikate.
Und dann können wir jetzt wieder das Zebra-Rätsel mal alles,
was wir über die Tränke wissen, hinschreiben und versuchen,
die Lösung zu finden.
Ja, Rätsel, Flaschen. So, als erstes,
wir brauchen sieben Flaschen und haben hier sieben Variable.
Und damit ist klar, das wird uns nicht,
wird uns genau eine Liste von genau sieben Flaschen bauen und zu überprüfen.
Und dann haben wir Aussagen darüber, dass es Giftflaschen gibt,
dass es Nesselwein gibt, dass es Tränke gibt.
Diese Relationen müssen auch erfüllt sein. Und dann wissen wir links von Wet Nesselwein steht Gift,
dann nehmen wir diese I-Right, rechts von Relation,
die wir schon fürs für die Häuser aufgebaut hatten.
Also, links vom Nesselbein steht, gibt.
Zuerst kommt hier das Gift und dann kommt der Wein.
Und so weiter. Auch der Wein zwei. Die beiden
haben wir. Die Flaschen an den Enden sind verschieden,
die müssen die verschiedenen Relationen erfüllen.
Und es gibt die Flaschen F1, die an den Enden stehen,
von denen wissen wir auch, dass sie uns nicht vorausbringen.
Zweiter von links und rechts sind Zwillinge, also die müssen gleich sein.
Ja, und dann fragen wir mal und lassen
das Rätsel lösen und wir kriegen eine Auskunft, ja,
Gift eins
In der Reihenfolge stehen die Flaschen Gift ein.
Es gibt zwei Beine, ein Skift 3 und so weiter.
So, Eman, jetzt sagt, okay, haben wir eine Lösung?
Und wir nehmen mal einen Schluck. Lieber gucken,
gibt es noch eine? Also hier würde man jetzt entweder
Flasche F-6 und F-7 trinken und sehen, was passiert,
wenn wir jetzt weiterfragen. Dann stellen wir fest,
okay, der Bein, hier, die Flasche 6,
von der wir geglaubt haben, die bringt uns voraus.
Das war harmloser Wein. Und wenn wir da weiterlaufen,
kann es sein, dass es uns das Leben kostet.
Ja,
nächste Lösung
An der Stelle F7, wo der Zurücktrank in der Lösung 1 steht.
Da könnt ihr aber genauso gut das Gift stehen, ja.
Also so wie wir es bisher beschrieben haben,
ist das Rätsel gar nicht eindeutig bestimmt.
Es gibt eine Lösung, aber man kann dir ja nicht trauen.
Also die Mehrdeutigkeiten sind lebensgefährlich. Irgendwo müssen wir die Größen
der Flaschen herhaben. Ja, was habe ich jetzt gemacht
an der Stelle? Ich habe einfach mal geguckt,
wo denn die Hamjoni in dem Buch,
auf welche Flaschen sie zeigt, die es sind,
sodass man dann weiß, welches die Größen sind.
Ja, da wird eine Little Bottle erwähnt.
Und wenn wir das noch, das, was die Helden
da sehen können, mit einbauen, bekommen war.
Mit Größe der Flaschen bekannt, auch zu einer eindeutigen Lösung.
Also wenn wir das Wissen fest vertraten, obwohl es im Rätsel nicht steht,
dass die Flasche F7 ein Riese ist und die Flasche F3 ein Zwerg,
dann können wir das Rätsel lösen.
Und dann gibt es nur eine Lösung und das
ist auch die erste, die angezeigt worden ist.
Okay, die ist dann sicher. Also wir können Prolog
auch zur Literaturkritik nehmen und einfach mal zu gucken,
ob das, was die Autoren uns verkaufen,
schlüssig ist
Also ein ganz ernsthaft hatte ich diese Regeln angewendet in einem Forschungsprojekt,
jetzt nicht mit den Steinen der Waisenregeln,
aber da ging es um die Verifikation von Abrüstungsverträgen und unsere Aufgabe war,
das, was an Regeln bekannt ist,
wenn man auf ein Satellitenbild schaut und versucht rauszukriegen,
ist da in der Gegend ein Minenfeld oder nicht?
Ja, sieht man Zäune,
gibt es Pfade und so weiter, die da hinführen?
Also das mit Wissensrepräsentation zu beschreiben,
um dann sicher zu sagen, ja,
mit hoher Wahrscheinlichkeit ist an der und der Stelle
ein Minenfeld
Dann, da kann man solche Sachen eben dann auch verbinden.
Okay. Die hatten dieses Projekt in den 80er Jahren gemacht,
wurde von der europäischen Union gefördert,
weil in Afrika nach den vielen Bürgerkriegen,
in Angola und den Nachbarländern Mosambik und so weiter,
praktisch überall durch den Bürgerkriegreste von Minenfeldern da sind und die Zivilbevölkerung leidet natürlich drunter,
weil wenn man nicht weiß,
wo man Räumtrupps hinschicken soll,
um das Land wieder bewohnbar zu machen,
hat man
schlechte Karten
Die Art ist, wie es gemacht wird, manuell,
indem man Räumtrupps hinschickt, die eben Roboter reinschieben.
Und dann geht man hinterher an den Topf,
dass die Roboter vor allem alles weggeräumt haben.
Das ist auch ganz schön gefährlich. Also die... Das Einschränken und
gezielt dort zu suchen, wo es eventuell solche Felder sind,
das ist dann von der EU gefördert worden,
indem man das mit Fernerkundung macht.
Nachteil bei Minen ist, die sind mit Absicht verbuddelt.
Also man sieht es ja nicht, wenn man mit Bildverarbeitung
unsichtbare Dinge sucht, dann braucht man eben genau solche Constraints.
Also was zum Beispiel hilft, ist, dass man feststellt,
Durch Vergleich mit älteren Aufnahmen, dass Felder,
wo früher Ackerbau betrieben wurde, mit einmal brach liegen.
Und wenn dann die Umgebung zusätzliche Hinweise hat,
dann konnte mit solchen Regeln es gesagt werden.
Ja, vermutlich Minenfeld. Eher. Das feiere ich mir
immer bei Minenfeld. Sie sind aber nicht dafür gebraucht,
dass wir deplodieren für einen Einfandsteller. Ja,
aber die explodieren auch, wenn kleine Kinder dabei spielen.
Oder... Wieso macht ein Trainer jemand dazu,
der im Prinzip den Traktor, Geheimdienst, das so umdreht,
was uns vorentschiebt und dann vorne einen Sack der Mannsgröße kocht,
muss man das einfach vorentscheiden
Weil es zu lange dauert, damit ganz Afrika zu durchsuchen.
Das ist unwegsam, ja. Und diese Minen werden auch vertrieben.
Also man hat die zum Besten in den teuersten Touristenorten entlang
de Sambisi gefunden, ja. Nicht weit von den Victoria-Fällen,
also da, wo die Touristen abloss spazieren gehen.
Und das ist nicht mehr so schön. Also die Frage,
also ganz generell die Frage, wie kann ich Unsichtbares finden,
indem ich die Indikatoren, die darauf hinweisen, kombiniere?
Für solche Zwecke ist es, wo ich prolog eingesetzt habe
und solche Regeln, Wissensrepräsentation.
Okay, ja
Ein anderer Punkt, über den wir auch unbedingt sprechen müssen.
Das ist, wie können wir von Pro-Log aus zum Beispiel
Racket verwenden? In Racket haben wir es uns erlaubt,
dass wir Ausflüge in die Imperative Ausfährteumgebung machen,
indem wir sowas wie Setbang haben, Modifikatoren und Vorschleifen und so weiter.
Und in der Pro-Log-Welt möchte man gelegentlich auch mal richtig rechnen,
so mit Zahlen und das nicht nur über Relationen machen,
die die Pianischen Axyume implementieren,
dass man sich die Zahlen so nach und nach konstruiert,
dauert ein bisschen.
Also er möchte richtig rechnen können. Und dafür brauchen wir
dann aber Funktionen. So das Gefährliche ist,
wenn wir die Funktionen verwenden, ist, dass eines der schönsten,
schicksten Sachen im Pro-Log weg ist, dass wir die Richtungsunabhängigkeit haben.
Wir können, wenn wir funktionale Teile haben,
kommt es auf die Reihenfolge der Ausflattung an,
eine Zahl dividieren kann, muss die Variable gebunden sein.
Ja, ich kann nicht erst mal nach allen möglichen anderen suchen.
Und deshalb müssen wir also an den Stellen,
wo wir es machen, davon ausgehen,
dass wir unsere Relation in der richtigen Reihenfolge definieren und auch die Prämissen in der richtigen Reihenfolge.
Also was wir da haben, um zum Beispiel numerisch auswerten zu können,
ist der, Apparate, ja, da können wir arathmetische Ausdrücke funktional auswerten.
Und Aprilock in Rected oder Prolog in Skim,
habe ich das so gemacht, dass ich an der Stelle einfach zulasse,
sobald ein Ist kommt, kann jeder beliebige Rected-Ausdruck kommen.
Also sie können an den Stellen ihre Racket-Funktionen aufrufen und mit Racket rechnen,
wie ihnen Lust und lieb ist, nur der Typ,
der zurückkommt, muss dann zu der Variable passen,
die sie binden wollen.
Wertet arathmetische Ausdrücke aus und jetzt für uns in Prolog in Racket
, wertet Racket-Ausdrücke aus und bindet das Ergebnis an die Variable
Denn wir haben eine Testoperate, noch als Zusätzliches Sprache-Element,
der wertet Bullshit-Ausdrücke, funktional aus.
Und wenn solche Testoperatoren angewendet werden, ist die Unifikation erfolgreich.
Ich finde Testoperator wahrsagt. So alle Variablen des Ausdrucks müssen
instanziert sein und die richtungsabhängigkeit ist Fuji.
Gucken wir uns mal ein Beispiel an,
wie man mit so einem Iss rechnen kann.
Ich habe hier eine Phrase und wir fragen,
bis geben eine Variable vor. Und hier einen Wert,
der ausgerechnet werden soll, das ist also hier schlichtes
Trecket,
2 plus
Und das wird ausgewertet, wird an die Variable X gebunden und wir erfahren als Resultat
diese Anfrage X gleich 6. Und über Fragen gibt es weitere Lösungen, nein.
Es gibt nur eine. Wenn wir jetzt nach Variablen suchen,
bist du hier kompliziertere Ausdrücke haben wollen, zum Beispiel XS3,
Y ist 5 und wir machen jetzt einen Test,
ob Y größer ist als X. Ja,
dann erfahren wir, der ist wahr,
wenn Y gleich 5 ist und X vielleicht drei und weitere Lösungen gibt es auch nicht.
Also hier wird Test verwendet und damit können wir das,
was wir hier an Werte gebunden haben,
Verknüpfen mit irgendwelchen funktionalen Ausdrücken und der Test soll dann sagen,
ja oder nein.
Wir können auf diese Stelle Funktionen höherer Ordnung reinbringen. Sagen wir mal,
wir wollen eine Liste haben, die hat die Werte 1,
2, 3 und der soll überall eins drauf addiert werden.
Na gut, dann nehmen wir unser Recket und schicken mit Map, ab wann drüber.
Es wird an die Variable Xes gebunden und wir erfahren,
das Resultat ist, X ist 2, 3,
4. Wir können es nicht rückwärts verwenden und Subwannen nehmen und das Ganze wieder subtrahieren.
Also rückwärts wird es nicht gerechnet,
geht nur in eine Richtung
Wenn wir jetzt wissen wollen, ob was alles die Member-Relation erfüllt,
in der Liste X ist,
die wir hier so erzeugt haben, könnten wir sagen,
ist X ist,
wir erzeugen sie hier und fragen dann nach allen X ist die Elemente,
nach allen X die Elemente dieser Liste Access sind und kriegen dann die möglichen Lösungen.
Und jetzt, wo wir rechnen können,
können wir auch endlich mal danach fragen,
wie lange so eine Liste eigentlich ist.
Und wir sind in der Lage,
eine Längenrelation zu definieren
Und das sieht jetzt schon wieder,
weil wir die funktionale Ausfährteumgebung benutzen müssen, ziemlich funktional aus.
Wir sagen als erstes, hier die Längenrelation,
ich setze die leere Liste in Relation zu Null.
Die leere Liste steht in der Relation längst zur Zahl Null. So,
und wenn wir eine Liste haben,
die ein Element X hat und einen Rest,
dann steht sie in Relation zu einer Variablen Länge,
die wir bekommen, wenn wir die Länge des Rängenrestes ermitteln.
Das machen wir hier. Nennen Rest. Und dann
die Variable Länge
Hier oben, das wohl nach der Frage,
um eins erhöhen gegenüber der Länge des Restes.
Hier addieren wir, ob die Länge des Restes eins drauf.
Und diese Reihenfolge ist wichtig. Wir müssen zuerst,
nennen er, eine Meere spenden an eine Zahl mit Unifikation.
Und erst dann können wir es in der IS verwenden.
Also als Überlebensregel, wenn sie sich dabei ertappen,
dass sie das ist, irgendwo ganz am Anfang,
denn dass sie der Klaude entstehen haben, dann denken sie nochmal nach.
Das Sicherste, weil eben alles gebunden sein muss,
ist die typische Situation, das steht als letztes.
Gut, ja, nun können wir hier fragen,
was die Länge der Liste ABC und dann kriegen wir
drei raus
Weil wir hier genau diese Rekursionen haben,
erst die Länge des Restes feststellen, dann rein drauf addieren.
So, die hohe Kunst in Pro-Log, aber darüber will ich nicht reden,
ist, dass man sowas auch endrikorsiv hinkriegt.
Aber das wird, weiß gar nicht, Menzeln ihnen beibringen
in der anderen Vorlesung. Uns soll es erstmal genügen,
dass wir auch die Numerik in einer Rekursion sicher machen können,
wenn wir dafür sorgen,
dass wir in der Rekursion immer alles schon gebunden haben,
wie wir damit rechnen wollen.
Okay. Ja, das nächste Thema. Das Pro-Log in Recket-System Das hat verschiedene Teile, ja,
dass einmal der Code, der uns die Datenbasis gibt,
das ist der Code, der die Unifikation implementiert,
also bei neugierig, es kann sich all das anschauen.
Wir hatten das Pro-Log selbst,
dieses Modul macht für uns Interaktion ein Back-Tracking und Blue-Log in Skim,
das ist das Bündel, was alles zusammenbringt und was wir einbinden müssen,
wenn wir damit rechnen wollen.
Also da haben wir die Sachen, ja. Okay,
das machen wir hier über nächstes, das übernächste Mal.
Gehen wir mal ein Stück zurück
Ich wollte jetzt nämlich noch in den Teil übergehen,
wo wir uns anschauen, wie wir eigentlich diese Spracherweiterung machen.
Mit der Programmierung. Gibt es zum Teil pro Log noch
Fragen, über den wir lassen? Ja. Eines,
was ich mich fragen habe, bis jetzt haben wir eine Zeit,
wo wir noch Fragen stellt,
wenn wir Relationen zu einer Heiligkeit haben,
könnte ich auch zum Beispiel sagen,
ich habe A und B und ich gebe mir eine Relation zwischen diesen beiden aus.
Nach den Relationen Selbstsuchen, da müsste man dann
mit Feind All und anderen Predikaten
höhere Ordnung das implementieren
Also das wäre der Meta-Programmierung auf der Datenbasis,
das geht aber nicht in meiner Spielzeugvariante hier.
Machen Sie es als Bachelorarbeit, dann können wir es erweitern.
Ich möchte nicht sagen, ich habe hier eine Lösung kaum
ein Problem und ich hoffe mal, es geht hier zu meiner Verratung.
Ja, also das Meta-Programmierung ist möglich, natürlich,
aber das wollte ich nicht in dieser Variante nicht eingebaut.
Gucken wir mal überhaupt, wie man Meta-Programmierung macht.
Also wir haben jetzt schon zwei Spracharbeiterinnen kennengelernt für Reckett,
das eine ist eben, das Pro-Log in Reckett, was für implementiert haben.
Das andere ist, das Zählers, was gerade ist,
wenn sie in Dreckett reinschauen, werden sie feststellen,
dass sie sehr viele unterschiedliche Sprachen dort realisiert haben.
Und typischerweise macht man es im Reckett vernünftig, indem man
die Feinden sonntags verwendet. Damit kann man beliebige Sprachelemente implementieren,
dafür eine Semantik definieren und dann ist man fertig.
Ich habe für die Sprachen, die ich selbst implementiert habe,
eine ziemlich brutale Abkürzung genommen und habe da einfach ins Windel das Dev Macro genommen.
Wir hören uns jetzt erstmal an, wie man man
solche Erweiterungen machen kann und dann gucken wir mal rein.
Das Makro, was ich gemacht,
was ich verwendet habe, ist eigentlich ein Klassiker,
das ist ein kommendes eingeführt worden in den 80er Jahren mit DevMakro
und ist ziemlich mächtig und relativ einfach zu benutzen.
Es gibt viele Programmiersprachen, in denen Sie mal groß finden.
Also zum Beispiel in der anderen Vorlesung, die in diesem Semester,
habe IVC, verwenden wir ständig Makros in der Sprache Popframe,
mit dem mein Retreser aufsetzen.
Ja, es gibt Makros in allen C-Dialekten und Makros sind sehr mächtig und nützlich,
haben aber ziemlich viele fallen und über diese Fallen möchte ich sprechen.
Dass die feinen Syntax, was sie in Bracket haben,
ist gestillt, dass es diese Fallen nicht hat.
Eigentlich müsste man es benutzen oder hätte ich dann wahrscheinlich vier vorlesechs Stunden extra gebraucht,
um nur, dass ich diese Art von differen Syntax eingehe.
Da habe ich dann doch lieber schnelle Quick und Dirt
die Makro genommen. Und außerdem ist es für einen Informatikerinnen
und Informatiker wichtig zu wissen, wo Makros gefährlich sind,
weil wie gesagt, praktisch jede höhere Programmiersprache hat sie.
Und da kann man leicht reinfallen. Also die Idee befür
man Makros nimmt, das sind zwei Sachen,
das eine ist einführen neuer Sprachelemente.
Man erweitert die Sprache
Oder man möchte die Programme effizienter machen, indem man zur
Übersetzungszeitprogramme Werte auswertet, sodass sie zur Laufzeit nichts kosten.
Das ist die Motivation in der ganzen Zielwelt, warum Makros genommen werden.
Gut. Also wir, ein Satz ist, wir können neue Sprachelemente einführen.
Und die wir eben mit Funktionen nicht können. Wir können
in der ganzen Lizwelt, können wir über Makrospecial Forms machen.
Und wir können Berechnungen für konstante Ausdrücke schon zu Übersetzungszeiten machen.
In der Zielwelt gibt es einen weiteren Grund,
wenn wir ein Makro schreiben, dann ersparen wir uns
Die Definition einer Funktion und die Funktionsaktivierung entfällt,
weil es eben auch zur Übersetzungszeit gemacht wird.
So, weil jetzt Makros aber eine Erweiterung der Sprache ist,
sollten wir damit wirklich frogal sein.
Also ganz, ganz, ganz, ganz sparsam verwenden.
Peter Norwick sagt dazu, erstmal überlegen, ob wir es wirklich brauchen.
Und wenn wir uns sicher sind, ja,
wir brauchen es, wir brauchen ein neues Sprachelement,
dann entwerfen wir die Syntax, dann legen wir fest,
zu welchem Code diese Syntax expandiert werden soll und dann definieren
wir es mit der Makro.
Ich fand es richtig schick, wie Norweg diese Warnung
formuliert hat, möchte ich ihn auch nochmal vorlesen
The first step in a writing the macro is to recognize
that every time your right one. You are defining in new language,
that just like list, except for your new macro.
The program are who sings that vaable rightfully be extremely
frugal on defining macros. Obviously. Ben someone us,
what did you do today? It sounds more impressive to say,
I defined in new language and roll to compiler for it,
then to say, I just tech that a couple of macros.
Das ist das, was wir tun,
wenn wir Makros einführen, um eine Sprache zu erweitern.
Wir definieren eine neue eigene Programmiersprache
Was ist da zu bedenken? Introducing, Emercroputz,
much more, memory strain on the reader of your program.
Denn das Introducing affunction, variable or data type,
so it should not budget a lightlener.
Introduce, macros, only vendarys a clear need,
and then the macro fits and voil with your existing systems.
I see you are horror put it. But thing the language
designer should that do is to include, untry ideas of his own.
Also was immer man in eine Sprache einfügt,
muss man in der Gemeinschaft ausdiskutieren und sehen,
dass es gut passt.
Jetzt machen, lösen wir einmal unser My-If-Problem.
Das hatten wir ja schon gesehen bei Elessappe.
Hacker, gell? Wir wollen mal If jetzt wirklich mal
als eine neue Special Form einführen und eben nicht als Funktion.
So, das My-If soll die folgende Syntags haben.
My-If. Und dann kommt die Bedingung und dann kommt der Wendenfall
und dann ist dann ein Fall. Und das ist also die Syntags,
die wir uns wünschen und der neue Sprachelement MyF. Ja,
das ist völlig überflüssig, das gibt es ja schon.
Aber trotzdem. Und wenn immer das auftaucht im Code,
soll in Wahrheit ausgeführt werden, Bedingung, wenn er als dann, ja.
So, und das können wir in Zwinde, ZF locker und lässig schreiben,
indem wir einfach schreiben, Death Macro, ja,
jetzt hier kommt der Name des Macros, dann kommen die Argumente des Macros.
Und dann kommt der Kot und den Kot erzeugen wir durch Lisp,
ja, oder eben Racket oder was immer wir für ein Dialekt zur Verfügung kamen,
wir schreiben hin, Liz, dies List erzeugt die Liste des Kodes so.
Und das erste, mit der Kot beginnt, ist,
dass das Symbol kommt und dann kommt eine Unterliste, Bedingung,
wenn, ja, und der andere Ausdruck ist er als sonst,
ja, damit, genau dann mit dieser Code hier erzeugt wird,
also diese Schablone muss da stehen, Klammer auf Kont,
Klammer auf, dann eine neue Liste, wenn und eine neue Liste dann,
ja, so ist die Sintats eines Kont, zu dem soll es expandieren und damit haben wir unser Makro.
Wenn jemand jetzt dieses Makro aufruft,
dann wird genau dieser Kurs ausgeführt, ja,
die Schreibweise ist allerdings ein bisschen gewöhnungsbedürftig,
weil wir eben die List-List-Ausdrücke hinschreiben müssen oder direkt Ausdrücke hinschreiben müssen,
die uns diesen Code erzeugen und die Liste errechnen,
die den Code darstellt, wurden diese Backquots und quasi kurz erfunden, ja.
Anstelle des normalen Quotes haben wir hier das Backquot, also das Portierungszeichen,
was so Schritt legt, nach hinten weggeht, ja.
Wenn wir das schreiben, haben wir eine Liste,
die ist quotiert und wir können den ganzen Code,
wie er hier erzeugt werden soll, einfach reinschreiben.
Also dieses Makro soll uns ein Konditional geben, Klammer auf,
Klammer zu, mit Bedingungen und Wenn und Erz.
Und dieses feste Quotieren hier, das muss aufgehoben werden,
wo wir unsere Variablen einsetzen wollen, ja. Da,
wo wir den Wert der Bedingung einsetzen wollen,
noch in einem Komma davor, wo wir den Wert des Ausdrucks Sven reinsetzen,
noch einen Komma vor, vom Arzt nicht,
weil das soll als Symbol genommen werden.
Aber hier, weil sonst kommt auch wieder ein Komma vor.
Und diese drei Kommata heben die Quotierung im Backquot auf.
Und damit können wir Makros gut leserlich schreiben. Da sieht man
auf dem ersten Blick, was dieses Makromai-Eff tun wird.
Und wir können auf diese Weise eben auch die anderen Formen,
die es früher in Racket gar nicht gab, schreiben,
wie zum Beispiel Wenn und Anläss, unser eigenes Mai,
Wenn, Bedingung, Restbody oder Mai Anläss, Test,
Also, so ist es zu benutzen.
Wir führen neue Sprachelemente ein. Das sind Special Forms.
Und durch die Reihenfolge, in die wir es hier schreiben,
ist auch klar, in welcher Reihenfolge es ausgewertet wird.
Wir können hier beim Konstruieren des Codes die Auswert der Reihenfolge festlegen hier.
Du möchtest das so beschreiben, wenn man von Silvester ist.
Sonder, das ist die Orange. Wir schreiben damit Special Forms
, ja. Wir erweitern die Sprache. Wir erweitern damit
die Syntags von Reggae, wenn wir diese Makros schreiben.
Hier haben wir zum Beispiel ein Makro für die Waldschleife.
Wenn wir hier Wir wollen sowas schreiben können, weil,
solange wir einen Test nicht erfüllt ist, führe diese Folge der Anweisungen aus.
Und das soll expandieren zu einem Let's Track,
wo wir hier eine Look-Funktion haben. Die macht Landerausdruck,
wenn für den Test aus, wenn er erfüllt ist,
führte all die Ausdrücke aus und dann ruft sich rekursiv selbst wieder auf.
Und zu Anfang ruft Luke einmal auf.
Und das können wir hier mit dem Quasi-Quot so machen.
Schreiben genau diese ganzen Let's Track-Kram da rein. Und überall,
wo das Komma steht, da werden die Argumente test und vor die reingesetzt.
So, was ich so taktisch noch erklären muss, ist,
Dieser komische Klammer-Affe hier, der kommt auch nur im Quasi-Quote vor.
Und das ist ein Splice. Also wenn ich hier eine Liste habe,
die heißt Body, und ich möchte alle Elemente der Liste
nacheinander hier einfach reinschreiben, nicht als Liste, sondern einzeln.
Dann nehme ich dieses Blice-Symbol. Also damit kann ich hier
eine lange Folge von Anweisungen übergeben.
Macht das Komma davon, kriegt die Anweisung hier alle reingeschrieben.
Und ein Zwindel sind diese Sachen alle schon fertig, ja?
Also das geht ganz schön vorbei, das hat man auch,
was ich nicht ganz verstehe, ist,
wie es geht, Pressures sind da noch rausgefährt.
Ich meine, das sind ja
auch Funktionen
Die Frankfurt draußen liegt. Ja, geh noch mal zurück
an... Also die Frage hier war, wie dieses Makro
dazu führt, dass diese Expressions ausgewertet werden, ne?
Die werden ausgewertet durch das Komma davor, ne?
Weil in dem Moment, wo ich das quotierungszeichen hier aufhebe,
steht hier diese Variablen mit ihrem Wert.
Und er wird in dem Moment ausgewertet,
wo man dann beim Ausführen des Kodes an diese Stelle trifft.
Da wird es eingesetzt, ja. Es wird verzögert bis zu der Stelle,
wo für unten eine Makrokurz zum ersten Mal auftaucht.
Es wird nicht beim Aufruf von Dev Makro
Sondern was passiert, wenn wir jetzt unser Makro aufführen,
aufrufen, dann wird die Syntax expandiert, der Kurt erzeugt.
Und an der Stelle, wo die Variable dann zum
ersten Mal auftaucht im Rumpf, da wird sie ausgewertet.
Also wir verzögern die Auswertung an die Stelle,
wo wir es im expandierten Kurz zum ersten Mal erwähnen.
Das heißt, wir verwenden uns zum Kurtentest.
Ja, kann man so sagen. Und dieses Schema hat
allerdings ein paar Fallen. Und ob diese Fallen möchte ich
jetzt eingehen,
Wenn Sie mal in das Windeln Missk reinschauen,
das sind Schablonen für all diese Makros da.
Ein Weill, ein Till, ein Dood-Times, sind herrlich
einfach zu benutzen. Du timest zum Beispiel die Laufvariable-I,
die führt den Rumpf und der Verwendung der Barriable-I-N mal aus.
Und damit können wir gut iterieren. Oder ein Dool-List,
das bindet X nacheinander in alle Elemente der Liste und mit
jedem Wert den X-Crick, führt es dem Body aus.
Und solche Sachen sind als imperative Sprachelemente alle über Makros eingeführt
und sind in Zwindeln da.
Verwende dich gerne. Wir können es mal als Beispiel Jetzt
schreiben wir uns sehen, wo wir vielleicht Probleme kriegen könnten.
Wir sahen hier außen mal die feinen IS7. Ja,
jetzt haben wir eine Variable I. So, und jetzt machen
wir einen Valine. Und sind so schusselig,
dass wir hier die Variable I als Wert übergeben.
Und wollen, solange wir die kleiner als Szenen ist,
wollen wir I immer um eins erhöhen.
Und dann Writeline E machen. Ja, das kriegen wir
raus. Na gut, zuerst wird acht gedruckt.
Wir beginnen mit einem I gleich eh gleich
Sieben, ja, erwöhnen ihn. Ach, habe ich noch nicht erwähnt,
dieses Sprachelement Inki, das ist ein Inkrementieren einer gegebenen Variableger.
Voll imperativ so. Also im ersten Wert,
dass ihr gleich achtet und dann wird das ihr geschrieben und
es ist neun, dann ist es zehn.
Das geht nochmal ganz gut, aber es wird,
wir werden das sehen, wo die Problembleme herkommen.
Der erste Grund, den ich gesagt habe,
wir wollen Makros haben, ist immer,
wenn wir Dinge zur Übersetzungszeit ausführen wollen und das
im kompilierten Coach schon ausrufen können
Wenn wir zum Beispiel häufig einen Ausdruck wie längs haben und
das eine konstante Liste übergeben, aber jedes Mal,
wo man auf diese selbe Liste mit längs kommt,
wird es neu gerechnet.
Das will man einmal zur Übersetzungszeit machen. Oder eine Multiplikation,
wenn irgendwo Pi mal 2 steht, ja,
das sollte vielleicht zur Übersetzungszeit schon gemacht werden.
Okay. Gucken wir uns da mal, Rechenzeitersparnis durch Makros
an, ja? Ein kleiner Wettbewerb. Ich schreibe hier
ein Makro, das den Mittelwert von vielen Argumenten errechnen soll.
Ja, der Makro, das heißt Average und dann mit
einem M dahinter
Makro. Und jetzt kommen die Argumente, von denen ich den Mittelwert haben will.
Das Makro bekommt diese Argumente alle als Liste übergeben.
Und wir können das ausrechnen, indem wir sagen,
wir dividieren die Summe aller Elemente dieser Liste,
hier machen wir wieder das Angstbleist, die werden die einfach reingesetzt,
sind das Plus.
Und dann werden sie addiert. Und wir teilen es durch
die Länge der Argumente. So,
wenn wir das mal aufrufen als Beispiel, Average,
Van Band Two 3, kriegen wir Mittelwert von zwei raus.
Wir haben das Ganze als Funktionen machen
Applied Plus auf die Argumente und schaltet es durch die Länge
der Argumente, dann funktioniert es auch.
So, und jetzt machen wir mal einen kleinen Test.
Wir lassen es laufen. Eine Million Mal, Do-Times,
die läuft von eins bis eine Million.
Ja, und rechnen dir Average von diesen zehn Zahlen aus.
Da kriegen wir für das Makro eine CPU-Zeit von 402,
Ryaltime 472, 472 keine Garbage Collection.
Wenn wir es als Funktion machen, ja,
kriegen wir
eine CPU-Zeit
Von über 2000, also fast 3000,
eine Triotime von 3000 und ziemlich viel Garbage Collection.
Warum? Das längst ist hier zur Übersetzungszeit ausgeführt worden,
in dem Moment ist das Makro expandiert, wird im Code eingesetzt,
gerechnet, während die Funktion es jedes Mal gemacht hat.
Da kommt der Unterschied her. Ja, aber wenn man etwas,
das Unbesehen anwendet, kann es sein,
dass man sich ein bisschen verheddert bei dem Ganzen, ja.
Also zum einen können Makros den Code total ineffizient machen,
wenn man bestimmte Dinge übersieht und zum zweiten Können wir falschen Code erzeugen?
Wenn wir zum Beispiel hier jetzt ein Death Macro machen für
Power 8 und haben die verzögerte Auswertung und schreiben das so,
dann wird das X, wenn wir hier hier achtmal hinschreiben,
im Komma vor, wird es achtmal ausgewertet.
Weil es hier nicht auf Parameterpositionen ausgewertet wird bei einer Funktion,
sondern erst, wenn wir es hier unten hinterm Komma einsetzen.
Also um das zu vermeiden, würde man das X einmal
ausschwerten an eine lokale Variable binden und dann diese lopokale Variablecheachten da reinsetzen.
Also das ist Wird achtmal berechnet in dem Fall.
Als Funktion würde es hier oben ausgewertet werden und nicht erst im Rumpf.
Und wenn wir da jetzt mal wieder vergleichen,
also die Variante, die es als Makro macht,
die hat hier jetzt ziemlich lange gebraucht und die Funktionsvariante nur die Hälfte.
Gut. Schlimmer ist es, wenn es nicht nur länger dauert,
sondern wenn auch das Falsche rauskommt,
weil wir uns beim Makro vertan haben.
Gut, falsche Programme geben immer falsche Ergebnisse,
weil beim Makro schreibt uns dann das leicht falsche Programme
Gucken wir uns das mal an. Wir implementieren mal unser
eigenes F. Incrementform, ja? Das soll eine Variable nehmen,
den Wert erhöhen und dann den Wert zurückgeben. Also wenn wir,
können wir so schreiben, beginnen,
in Form ist Ink nix und dann gibt es den Wert noch zurück,
weil wenn wir solche Bang-Ausdrücke in Recket verwenden,
dann wird das Ergebnis und der Druck kommt immer Void zurück.
Also wir wollen den Wert aber haben, also schreiben wir es als Makroso. Wenn wir,
es funktioniert auch, dieses Ink F, kein Problem,
wir definieren hier eine globale Variable X auf eins und rufen Ingust,
increment form of, das ist eine typische Commonlist-Pfu, Makoya,
dann kriegen wir zwei raus, weil eben eins drauf addiert wird und wir kriegen das Ergebnis zurück.
So, wenn wir jetzt die vierte Potenz von M berechnen,
ja, können wir als Makro auch so schreiben,
mal XXXXX wird hier viermal übergeben, das X, ja.
Und jetzt, wir wollen da als Argument übergeben,
Inkrementformen von X, ja,
also wir haben eine Variable X und wollen deren Wert nach
dem Inkrementieren nehmen und davon wollen wir die vierte Potenz
Super, schreibt man so. Was kriegen wir da als Ergebnis
zurück? 360 und wir stellen von der Überraschung fest,
unser X hat inzwischen den Wert 6 und ist nicht 2. Wir haben,
X war vorher 2, bevor wir das Ganze gemacht haben.
Das haben wir übergeben, wollten es erhöht haben auf 3.
Aber nachdem unser Power 4 fertig war,
hat X den Wert 6. Warum?
Weil der Ausdruck wörtlich, Increment FX, hier viermal nacheinander reingeschrieben wird.
Und damit beim ersten Auswerten wird es einmal inkrementiert,
beim nächsten nochmal, beim nächsten nochmal,
beim nächsten nochmal.
Weil eben wörtlich
Die Argumente eingesetzt werden. Ja. Und zwar so oft
, wie wir es dahin schreiben. Also wenn das,
was wir an ein Makro übergeben, etwas ist,
was modifiziert, dann werden die Makros gemein.
Das ist, also Makros sind gut und schön, aber nicht,
wenn wir sie auch noch kombinieren in den Argumenten mit modifizierenden Operatoren, ne?
Dann ist die Bezugstransparenz zweck, dann bedeutet x Firme
dasselbe Schreiben, nicht immer denselben Wert.
Ja? Okay. Was passiert ist ja,
Beginnen wir mal wieder mit unserem X gleich eins, ja?
Rufen PowerM auf und haben noch eine einhörende Funktion.
Show, die ein Display machen soll. Ein Rightline und uns
das Ergebnis anzeigt. Und dann sehen wir hier genau,
was passiert beim ersten oft Zugriff auf X.
Wird zweigedruckt beim zweiten Zugriff 3, beim dritten 4,
beim vierten 5. Und wir haben jetzt vierte Potenz von Inkrementierer eins
um eins, haben da als vierte Potenz 120 rausgekriegt.
Noch lustiger wird es, wenn der Aufruf hier random ist,
auf Parameterposition, ja?
Dann haben wir bei jedem Zugriff
eine andere Zahl
Okay. Wenn wir, und um sowas zu vermeiden,
führt man in Makros lokale Variable ein, ja?
Wenn wir so ein Endfime sichern machen wollen, ja,
ein paar verwenden hier eine Variable ein und haben auch Parameterpositionen,
solche Sachen, das ist eben riskant.
Sicherer wird es, wenn wir lokale Variable einführen.
Und weil so etwas einfach schief geht,
wenn wir auf Parameterposition modifizierende Ausdrücke haben,
ein vorsichtiger Makroprogrammierer wird immer das Argument als erstes nehmen an eine lokale Variable binden,
die festhalten, das nur einmal auswerten und das dann reinsetzen.
Die hohe Kunst ist, wo kriegen wir lokale Variable her?
Ja. Der sichere Weg, lokale Variable zu bekommen,
besteht darin, dass wir ganz sicher gehen und
nur solche Namen für lokale Variable verwenden,
die im Programm nirgendwo verwendet wurden.
Also wir können nicht einfach als lokale Variable im Makronin nehmen.
Irgendeinen Schlüssel kann eine Variable auf Argumentposition übergeben haben,
dann landet dieses I in einem anderen Kontext
Dann unser Aufruf hier zum Beispiel, Show,
Inkrementform und für übergebende Variable, führt dazu,
dass diese Show-Inkrement-I, in das Doot Times reingeschoben wird,
wo wir als lokale Variable uns ein I ausgedacht hatten.
Und das heißt, mit einmal ist das I,
was hier steht, dasselbe wie hier.
Nicht, dass I, was für global übergeben haben.
Also Makro ist verschieben Variable in einen anderen Bindungskontext und
wir müssen dafür sorgen, dass das,
was wir uns an Variablen ausdenken,
eine Variable ist,
die niemand verwendet hat
So, jetzt, das erfordert aber hellseherische Künste.
Wir schreiben einen Satz von Makros, wo wir gerne mal
eine Laufvariable haben wollen. Und dann sagen wir uns,
ach Gott, wenn wir die die 9099 nennen,
wird keiner meiner späteren Kunden, wird je seine Variable 199 nennen.
Pustekuchen, kann man sich darauf verlassen. Die Benutzer sind
kreativ. Also was uns als variablen Namen eingefallen ist,
wird 50 Jahre später, wenn jemand denselben Code verwendet,
vielleicht auch verwendet werden. Und deshalb ist eingeführt worden,
Glenn Zimm. Generate Zimble. Also was das macht,
Denn Zimm ist eine Wahnsinnsfunktion, die geht sämtliche Symboltabellen durch,
die wir in Reke diveniert haben.
Ja, wir wissen es ja aus den Closures.
Es gibt Symboltabellen, in denen steht drin,
welche Variable ist zurzeit bekannt und hat einen Wert.
Jen Zimm geht sämtliche Tabellen durch und sucht einen neuen Variablennamen,
der in keiner der zurzeit gebundenen Variablentabellen vorkommt.
Also da können wir uns sicher sein,
wir kriegen eine taufrische Variable, die zurzeit im Programm nicht verwendet wird.
Die Namen sind ein bisschen langweilig,
also die heißen laut P 681
oder so
Aber man muss ja auch menschlich lesen können. Wir kriegen auf
jeden Fall einen Namen, die noch nie verwendet wurde.
In unserem Programm zur Laufzeit in den Tabellen nicht vorkommt.
Und wenn wir das so machen, dann können wir unseren Code so schreiben,
zuerst brauchen wir eine Variablennamen für unseren Laufindex.
Den kriegen wir mit Jens, einem Aufruf von Jensin.
Dann brauchen wir einen neuen Variablennamen dafür,
wie oft das Ganze gemacht werden soll.
Der soll Times heißen. Und den erzeugen wir auch mit Sensum.
Und jetzt schreiben wir unser Letzt so, dass wir hier das,
was bei Times zurückgekommen ist, einsetzen, ja.
Und bei Index setzen wir das ein, was hier zurückgekommen worden ist.
Und damit haben wir ein neues Makro, das etwas entfach ausführt,
sich eine neue Laufvariable erzeugt hat mit einem Namen,
der garantiert nicht auf Parameterposition übergeben wurde.
Und auch die Laufvariable ist eine andere, die von außen
nicht beeinflusst ist. Und damit können wir das sicher machen.
Also dieses Gensum ist sehr, sehr nützlich,
um uns vernünftig neue Variablennamen zu geben,
die wir
sicher benutzen können
Okay. Viertelstunde haben wir noch. Dann können wir jetzt
auch noch mal auf die anderen Programmierstile eingehen.
Und uns anschauen, wie wir verzögerte Auswertungen machen.
Also eigentlich kennen wir das schon. Ich habe ganz zu Anfang
des Semesters habe ich neben der Sprache Recket auch die Sprache Lesi erwähnt.
Lesi verwendet verzögerte Auswertung. Was ich jetzt gern zeigen möchte,
ist, wie wir sowas wie Lesi selbst implementieren könnten,
mithilfe von unseren Makros.
Und dann damit die stromorientierte Programmierung hinkriegen
Haben ja ganz zu Anfang überhaupt über
Vor- und Nachteile von Verzögerter Auswertung und vorgezogene Auswertung gesprochen.
Bracket macht bei Funktionen immer vorgezogene Auswertung. Steht ein Therm da,
stehen Argumente da, erst die Argumente auswerten, ja,
man davon ausgeht, kein Mensch übergibt Argumente, die nicht gebraucht werden.
Und dann haben wir sie alle einmal berechnet,
der Fall ist klar, weil Lesi, oder verzögert der Auswertung,
verschieben wir es bis zu dem Zeitpunkt, wo klar ist,
wird es gebraucht oder nicht, ne?
Gut, und wir brauchen verzögerte Auswertung,
aber auch für das Definieren eigener Kontrollstrukturen und wir brauchen es,
potenziell unendliche Datenstrukturen zu erzeugen.
Und das am Beispiel des Stromorientierten Modells. Stromorientierte Programmierung nutzt
voll die verzögerte Auswertung und wir haben eine neue Datenstrukturströme.
Diese, was wir in diesem als Grundelemente in diesem Programmierstil
verwenden, das sind Generatoren. Wir haben Bausteine aktiv,
die erzeugen immer wieder neue Werte. Da kommt ein endloser
Strom von möglichen Werten heraus. Stellen sich das vor,
wir wollen etwas über eine Menge der natürlichen Zahlen machen.
Wir schreiben einen Generator, der produziert eine natürliche Zahl nach der anderen.
Und auf das, was da rauskommt, wenden wir unsere Mapping-Funktion an,
um für jede natürliche Zahl irgendwas zu machen.
Da brauchen wir verzögerte Auswertung, weil wir werden nie fertig,
wenn wir die gesamte Menge der natürlichen Zahlen berechnen wollen.
Und aus diesen möglichen Werten wollen wir die Rosinen rauspicken.
Wir brauchen Filterbausteine. Das sind Transformatoren, Bilder- oder Abbildungsfunktionen
wie Map. Und dann Kombinatoren, die viele Ströme nehmen und
sie zu einem verbinden und wir kriegen neuen Strom raus.
Wir müssen aus dem Strom Werte rauskriegen. Wir brauchen Selektoren
und wir brauchen Konstrukturen, um uns Ströme zu erzeugen.
Also ich habe das mal hier am Beispiel Familienplanung gemacht.
Wir haben einen endlosen Strom von Frauen und einen endlosen Strom von Männern
. Da werden immer ein Männlein oder ein Weiblein generiert.
Und dann haben wir einen Kombinator aus der Klasse der Transformatoren.
Der erzeugt daraus, Moment, der Bauchstein will nicht.
Moment, der erzeugt daraus Ehepaare. Und dann haben wir
einen Transformator, der erzeugt aus diesen Ehepaaren einen Strom von Kindlein
Also so etwa kann stromorientiert der Programmierstil aussehen.
Man macht es gern grafisch, indem man die Quellen zeichnet.
Und dann die Ströme durch Pfeile darstellt und zeigt,
wie da neue Werte rauskommen. In der Bildverarbeitung zum Beispiel,
da könnte so ein Strom mit einer Kamera beginnen,
die einem ein Video erzeugt.
Und dann haben wir Filter, die in den Bildern
den Kontrast erhöhen oder so etwas und so weiter.
Also wir ströme in der Welt der funktionalen Programmierung,
da haben wir die Listen eine rekursive Struktur.
Sie haben einmal einen Kopfteil, der ist endlich und
wir haben einen potenziell unendlichen Reststrom.
Oder sie sind leer
Und wir können sie von vornherein als beliebig lang definieren.
Und entnehmen tun wir aus einem Strom immer nur ein Element zur Zeit.
Das ist endlich und deshalb können wir damit auch rechnen.
Der Rest eines Stroms ist ein Versprechen, dass wenn jemand
interessiert ist an den Werten, dann wird sich drum gekümmert.
Also die Stromfunktion, das sind jetzt Funktionen,
die gut für Ströme geeignet sind, die so aufgesetzt sind,
dass sie sich immer dem Kopf angucken und den Rest wieder verzögern und zurückstellen,
bis jemand den Rest haben will.
Und eine Funktion ist in Kopfform, Wenn wir sie so geschrieben haben,
dass wir immer schon ein bisschen vom Resultat erfahren können,
weil der Kopf des Stroms ausgewertet wird.
Ja, und wenn das Kopfstück des Ausgabestroms nur vom Kopfstück des Eingabestroms abhängt.
Damit fällt eine Klasse von rekursiven Funktionen schon mal komplett flach,
wenn es um die Welt der Funktionalen Programmierung mit Rekursionen geht.
Das sind Funktionen in Entrekursionen, wo man die Ergebnisse
erst kriegt, wenn der Akkumulator fertig ist.
Also die sind total ungeeignet dafür. Und das ist der Grund,
weshalb wir in der Welt wie Miranda und Teskel,
wo jede Liste automatisch ein Strom ist
Gar nicht erst über Entrykursion reden, weil das für den Programmierstil
völlig ungeeignet ist. So, ein Beispiel hier, ja,
wenn wir Map schreiben würden über eine Liste existieren, Strom sein soll.
Nicht da ein Mapping drüber macht, dann ist es prima,
wenn die Liste leer ist, erfahre ich was.
In allen anderen Fällen habe ich einen Konz,
wo ich die Funktion anwende aufs erste Element und kann das schon in den Oscarbestrom geben.
Und dann wird Rico Süchte Rest verarbeitet. Dagegen diese Entrekursive Form,
wo ich in die Rekursion absteige,
Und das Ergebnis mit dem Akkumulator-Zusammenbau, da erfahre ich erst,
wenn ich die endlose Liste abgearbeitet habe und bei der Lehrenliste angekommen bin, irgendwas.
Dann kommt der Akkumulator zurück. Und muss auch noch umgedreht werden. Gut,
in Recket haben wir, um das zu machen,
natürlich erstmal die Programmiersprache Lesie, der alle schon fertig ist.
Wenn wir es zu Fuß machen wollen, haben wir zwei Special Forms.
Das ist Delay and Force. Die Le liegt zu berechneten
Ausdruck in der Datenstruktur ab. Und damit kommen wir den
Rest der Liste einhüllen und Force sorgt dafür, dass das abgelegt ist,
geöffnet wird und wir wieder ein bisschen mehr erfahren.
Wer hatte die Datenstruktur aus? Meistens macht man die Auswertung dann aber so,
dass nur das erste Stück genommen wird und der Rest hat wieder gleich mit Dilet verzögert.
Gut, also Strom ist wie eine Liste,
eine lineare, rekursive Datenstruktur, ja?
Kann leer sein, aus einem Kopf bestehen und einen Schwanz,
so der Restteil, der wieder vom Typ Strom ist.
Und der Schwanz kann zu jedem Zeitpunkt nur unendlich
weit ausgewertet werden, kann aber viele,
viele Elemente enthalten
Wir überlegen uns jetzt mal so einen abstrakten Datentyp Sturm,
was das Minimum ist, was wir haben wollen.
Das eine wäre eine Funktion Empty Stream, ja,
die soll erstmal einen leeren Strom erzeugen, wo noch nichts strömt.
Dann haben wir einen Cons, wo wir einen Kopf und einen Restteil,
den Tail zusammensetzen zu einem neuen Strom, einen Kombinator,
die Akzessorenhead gibt uns als erste Element,
Tail gibt uns den Rest und Stream
Ref gibt uns das Ente-Element. Und damit können wir loslegen
. Machen wir jetzt mal alles als Makros
Weil dir Ströme wie eine Liste aussehen und in Lee,
sie Sinslisten. Ja, da sieht man keinen Unterschied.
Wir machen einen Dev-Makro-Constreme, A und B.
Zwei Elemente sollen verbunden werden und das macht einfach das Folgende,
ein Cons erzeugt uns eine Liste,
das Element A eingesetzt wird und B wird mit Dilet reingesetzt.
Es soll eben nicht ausgewertet werden. Ja, erstmal.
Headstream gibt uns das erste Element, Tailstream,
guckt jetzt ein bisschen vorsichtig nach, ist der Strom leer,
dann kriegen wir einen Leer.
Wenn der Rest leer ist, kriegen wir auch einen Leonstrom zurück.
Wenn es ein Paar ist, dann wird dann Er ist zurückgegeben, ja,
und in allen anderen Fällen wird ein Fonds gemacht für den Rest vom Strom.
Ja, da wird es mal die Auswertung erzwungen. Feststellen,
ob der Strom leer ist, fragen wir einfach nur,
ist er null. Und The Empty Stream liefert uns zu
Anfang die leere Liste, damit können wir loslegen.
Und jetzt versuchen wir mal damit wirklich was zu programmieren.
Zum Beispiel den Strom der natürlichen Zahlen.
Strom der Zahlen ab N. Wie machen wir das?
Wir machen einen Cons von der Zahl N.
Und dann verwenden wir das praktische Delay und übergeben
In der Rekursion alle natürlichen Zahlen ab N plus 1.
Also das ist wieder diese Rekursion, die einen Angst und Bange
machen kann, weil da nirgendwo eine Abbruchbedingung drin ist.
Wir gehen einfach von null bis unendlich. Aber als vorsichtiger Mensch
gibt es vor jedem Ausführen, dieser ist Endlosrekursionsschritt eingelesen,
sodass man immer Gewalt braucht, um das nächste Element zu kriegen.
Und wenn wir das jetzt aufrufen,
ab drei zum Beispiel an den natürlichen Zahlen ab drei,
dann kriegen wir, wenn wir es auswerten,
die Zahl drei zurück und hier hinten nach dem Kunstoperator kommt Struct Fromise.
Da steht, wenn ihr wirklich wissen wollt, was die weiteren Zahlen sind,
dann bewendet Force und wertet es aus, ja?
Ich habe es hier mal angewendet fürs siebte, das ihrer
Thorsten ist für Primzahlen. Erinnern Sie sich an Ihre Schulzeit?
Primzahlen können wir sieben, indem wir einfach uns vorstellen,
wir haben einen endlosen Strom der natürlichen Zahlen und streichen nacheinander
die Vielfachen aller Bekannten gefunden in Primzahlen aus.
Ja, jedes Mal, wenn wir eine Primzahl finden,
streichen wir die Vielfachen der Primzahlen aus,
nehmen Sie mit einem Filter raus.
Und die erste Zahl,
die nicht ausgestrichen ist,
ist wieder
eine Primzahl
Wenn wir das so als stromorientierte Programmierung machen,
folge der natürlichen Zahlen außer Eins, ist ein Strom der Kandidaten verbringt zu zahlen.
Kopf des aktuellen Kandidatenstroms ist eine Primzahl, dass wir in unserem Fall die Zwei,
wir nehmen den Kopf, wir haben die Prinzahl 2 gefunden,
spreichen alle Vielfachen von 2 aus. Wir sieben den Rechtsstrom,
dann nehmen wir den gesiebten Rechtsstrom und nehmen wieder den Kopf von den Kandidaten und so weiter,
wir nehmen als nächstes die 3, dann die 5,
dann die 7. Okay, also ungefähr so,
zuerst die Zwei wegnehmen, dann bleibt der Strom ab 3,
dann nehmen wir die 3,
dann haben wir schon 2,
3 gefunden,
dann nehmen wir die 5 und so weiter und gesiebt immer
die Vielfachen rausgestrichen
Hilfsfunktion nicht teilbar von, da gucken wir uns den Divisionsrest an.
Und dann kommt das Sieben, ja?
Wir haben hier einen Strom, wir machen einen Kunst,
der Kandidatenstrom, das erste ist eine Primzeit per Definition.
Und das setzen wir mit Dilet vorm gefüllterten Strom,
wo wir testen, ob es nicht teilbar ist,
der Kopf des Stroms mit den Elementen.
Ja, damit filtern wir das. Also alle Vielfachen von Kopf
des Stroms werden rausgesiebt. Und dann sieben wir den Reststrom,
ja? Und dann können wir sagen, unsere Primzipzahlen besteht darin,
wir sieben, die Inteachers, beginnt ab der Zahl zwei.
So, und wenn wir jetzt wissen wollen,
was sind denn nur die ersten zehn Primzahlen,
dann sagen wir Takestream, die Funktion müssen wir noch schreiben,
nimmt die ersten zehn Elemente und druckt sie uns aus und die haben wir.
In der Sprache Lesi ist alles schon fertig,
der braucht manche Ströme nicht selbst machen,
aber ich wollte immer zeigen,
wie man solche Dinge eben mit Delay und Force einfach tatsächlich machen kann.
Lesi ist auch das Tick schon fertig,
da können wir einfach sagen, Tick kennen den natürlichen Zeilen,
von eins haben wir die ersten zehn natürlichen Zahlen.
Oder beim sieben in Lesi könnten dann auch sagen,
Prime Chief Nets up to und nehme die ersten zehn Bremenzahlen und haben es dort gelöst.
Okay, dieses Beispiel zeige ich das nächste Mal für heute
erstmal Schluss.
Danke für die Aufmerksamkeit