Ja, guten Morgen, alle zusammen. Lassen Sie uns
mal loslegen. Was hier gerade als Screenshaver im Hintergrund läuft,
sind die Lösungen für das nächste Aufgabeblatt, was heute verteilt wird,
was in den Vorjahren dafür geschaffen wurde,
als sie ihre Opfer bewirbtes sein, alles,
was man über das Erzeugen von Bildern in Recket Preis zu benutzen und mir eine Weihnachtskarte zu schicken.
Und diese Weihnachtskarte sollte Baumrekursionen enthalten. Ja,
also ihre Fantasie sind da keine Grenzen gesetzt, Baumrekursionen heißt nicht notwendig,
dass ein Baum im Bild ist, aber zumindest eine baumrekursive Struktur.
Also er trennt in den Vorjahren, ging immer mehr zu baumrekursiven Geschenkestapeln.
Aber vielleicht finden sie eine wenige kommerzierende Art, daran zu gehen.
Guck mal mal. Wir waren das letzte Mal an der Stelle angekommen,
wo wir mit dem zentralen Thema der funktionalen Programmierung begonnen haben.
Funktionen, höhere Ordnung, Funktionen, die Funktionen als Argument
bekommen, Funktionen, die Funktionen als Resultat erzeugen.
Heute möchte ich vor allem ganz kurz wiederholen,
was wir bei denen, die Funktionen als Argument benehmen,
schon besprochen haben und dann weitermachen, bei denen die Funktionen als Resultat erzeugen.
Also was wir das letzte Mal schon gesehen haben,
waren Funktionen wie Map, ja,
Map nimmt eine Funktion als Argument und dann eine oder mehrere Listen und je nachdem,
wie viele Listen wir hier haben als Argumente,
so viele Argumente sollte diese Funktion nehmen,
die wir übergeben und hier haben wir es genutzt,
um so einen Reißverschluss zu machen.
Einfach diese Listen nehmen und jeweils das erste Element der ersten mit dem ersten,
der zweiten verknüpfen, zu einer Liste und das über die ganze Liste,
also Map bildet eine Liste ab.
Das haben wir das letzte Mal schon gesehen,
ist in vielen Fällen nützlich, wenn wir eine lineare Rekursion einfacher ausdrücken wollen,
statt in die Rekursion zu steigen, sagen wir Map,
gib die Liste rein und verarbeiten jedes Element systematisch.
Dann hatten wir als nächstes das Gesehen Filtern,
beim Filtern haben wir auch eine Liste als Argument,
aber wir übergeben ein Prädikat,
das Elemente auswählt,
die wir behalten wollen
Und wir kriegen als Resultat dann eben eine Liste aller Argumente zurück,
die das Prädikat erfüllen. Zum Beispiel hier Filter, Ott,
wir wollen alles, nicht was seltsam ist und was ungerade ist,
und kriegen dann als Resultat 1, 3,
5 bis ungerade, 4 nicht, 3, 5,
das wieder nicht auslassen, eine kürzere Liste zurück.
Wir hatten bei der Gelegenheit Zusicherung für die Kombination von diesen Funktionen höherer Ordnung
besprochen, aber das wollen wir erst mal übergehen.
Dann waren auch wichtig, die Faltungsoperationen falten,
nimmt eine Funktion
Ein Startelement, das ist typischerweise das neutrale Element für den Operator,
den wir da übergeben und verknüpft jeweils das bisherige Ergebnis mit dem ersten,
mit dem nächsten Element der Liste.
Also wir nehmen das Stadtelement hier,
addieren mit Plus zu eins, das Resultat,
was dann rauskommt zu zwei addiert zu drei und so weiter.
Und auf diese Weise können wir Listen kollabieren zu einem Element
von einem anderen Typ. All diese Funktionen höherer Ordnung,
die wir angesprochen haben, kann man schachteln,
sollte man schachteln.
Also das werden wir
ziemlich intensiv machen
Gut, das hatten wir auch schon gesehen,
wirst selbst programmieren könnten und dass es Falten, was wir selbst geschrieben haben,
dass es allgemein linear rekursiv ist und nicht entrekursiv, ne?
Es wird alles beim Hochkommen aus dem Stack werden, diese Faltungsoperationen erst gemacht.
Beim Falten ist eine von den Funktionen höherer Ordnung,
wo wir darauf achten müssen,
ob wir eine kommutative Funktion übergeben oder nicht,
dann hängt es mich davon ab,
ob wir Fuld R oder Fuld L nehmen, ja,
dass für Left oder R für Reit bestimmt,
an welches Argument das vorherige Ergebnis übergeben wird.
Ob die Operation das als zweites oder als erstes Argument bekommt.
Und deshalb muss man sich immer genau überlegen, welches man
zurzeit nehmen will. Ja, eine weitere ganz nützliche Hilfsfunktion,
die man immer mal braucht, ist sowas wie Build List.
Wenn wir aus dem Nichts eine Liste erst mal erzeugen wollen und haben irgendeine Gesetzmäßigkeit,
die die Elemente der Liste beschreibt. Können wir sagen,
Bildlist, eine Liste der Länge 5 und dann Übergeben wir eine Funktion,
die jeweils, hier wird durchgezählt, 0-1,
2, 3, 4, 5 hier,
das Argument der Listenpositionen, wie wir sind, übergibt.
Und daraus errechnet, wie das Element an der Stelle aussehen soll.
Also hier bekommen wir eine Liste, die 0 mal 0,
1 mal 1, 2 mal 2 und so weiter hat,
weil das die Funktion ist, die wir übergeben.
Und das ist auch eine gute Stelle,
uns noch mal dran zu erinnern, wofür wir die Landernotationen kennengelernt haben,
bei den Funktionen höhere Ordnung, brauchen wir die Lamda-Notation für Funktionen,
die wir noch einmal verwenden wollen und denen man sofort auf den ersten Blick ansieht,
was sie tun,
so das fände ich extra groß separat definieren müssen.
Also hier wollten wir eben eine Funktion übergeben, die exquadriert.
Wir hätten auch das Quer übergeben können,
aber um es deutlich zu machen, was hier geschieht,
habe ich mal eben reingeschrieben, Lamda X und X mal X. Gut,
wir können mit Funktionen höherer Ordnung auch allgemeinere,
die Kontrollrahmen schreiben.
Und das Beispiel dafür, hier sowas,
wir wollen etwas iterieren
Wir haben schon gesehen, wenn das, was wir haben,
lineare Datenstruktur ist, dann machen wir einen Map drüber oder
einen Fuld oder so und können drüber über diese Struktur iterieren.
Aber wenn wir aus dem Nichts heraus eine Iterationsschleife erzeugen wollen,
dann müssten wir nach dem, was wir bisher können,
rekursive Aufrufe machen.
Aber auch dafür gibt es Funktionen höherer Ordnung, das,
was wiederholt werden soll,
als eine Funktion übergeben bekommen und dann diese Funktion immer wieder
anwenden auf die bisherigen Ergebnisse.
Zum Beispiel Funktionen Iterate
Die ist dummerweise in Reckett nicht fertig vordefiniert. Das ist eine
der Funktionen, die ich ins Tools Modul getan habe.
Was macht Itterry? Das wendet eine Funktion immer wieder auf den vorherigen Wert an.
Und damit können wir dann folgen und rein erzeugen.
Und alle, die Sequenz aller Aufrufe kommt zurück als eine Liste.
Also Itterry, das erste Argument ist eine Funktion.
Dann müssen wir noch irgendwann sagen, ach so,
wann wollen wir eigentlich aufhören?
Ja, den Ende-Test übergeben wir auch als Funktion.
Das sollte ein Prädikat sein. Also eine Funktion,
der ein Resultat vom Typ Bullion ist und dann
einen Startwert
Wenn wir jetzt genau das machen wollen,
was wir hier getan haben, immer wieder zweimal,
zweimal, zweimal anwenden, auf die Elemente der Funktion,
könnten wir es so machen.
Iterate, also die Funktion,
die immer wieder angewendet werden soll, ist das erste Argument.
Nimm irgendein, das Mindestargument X und multiplizier es mit zwei Städtieren,
diesen Lamm der Ausdruck. Der Ende-Test, wir hören auf,
wenn X größer ist als 64. Und als Startwert nehmen wir
die eins. Und dann kriegen wir eben diese
schöne Liste hier
Oder wir wollen die Liste aller natürlichen Zahlen erzeugen,
bis zu einem Endwert. Da könnten wir sagen, Iterate,
Adire 1. Und der Ende-Test ist, wenn das X größer
gleich dem Ende ist, was wir hier übergeben haben.
Und wir beginnen bei 1. Und dann kriegen wir auf diese Weise
Managerites aufrufen. Die Liste der natürlichen Zahlen bis 7. Oder
wir brauchen sieben Leerzeichen nebeneinander. Ja, das hätte man
auch ganz gut mit Build List machen können.
Aber wir können uns mal angucken,
was wir hier so alles tun könnten
Wenn Sie diese Funktion hier sehen, nehmen Sie es nicht als Positivbeispiel.
Racket und Funktionen höherer Ordnung, die haben so eine bestechende Eleganz,
dass man versucht ist, Dinge, die man einfach zu sagen,
ziemlich kompliziert von hinten durch die Brust zu sagen.
Und manche funktionale Ausdrücke, die richtig elegant und toll aussehen,
wo man so viel Funktionen höherer Ordnung reingebracht hat,
wie man reinstopfen kann,
die könne der ja keinen Menschen mehr lesen.
Also hier geht es gerade noch. Also
wenn wir uns fragen, wie mache ich auf simple Weise
ein Leerzeichen
Das Einfachste wäre echt, wir nehmen die Funktion Bild List,
die wir vor fünf Minuten kennengelernt haben und sagen,
der erzeuge mir eine Liste, indem du fünfmal ein Leerzeichen erzeugst,
so und dann haben wir sie, ne.
Aber wir können es auch so machen, ja.
Wir nehmen, wir falten mit der Funktion String erpennt,
beginnen mit einem String, der das Leerzeichen hat, ja.
Und das machen wir so oft,
wie wir eine Funktion, die weitere Leerzeichen erzeugt über
die Liste der natürlichen Zahlen
Schickt dir. Map Lamber X. Gibt das Werkzeichen zurück über
die Liste aller natürlichen Zahlen bis Ende, die wir joben, aufgebaut haben.
Kriegen wir eine Liste zurück, die hat lauter Strings,
die ein Leerzeichen lang sind und die falten wir mit String erpennt.
Wie gesagt, es gibt einfache Arten, das zu machen,
aber es passiert oft, also wenn sie eine tolle Lösung gefunden haben,
sieht gut aus, sind fünf Zeilen, Funktionen,
höherer Ordnung aufgerufen, verschachtelt.
Und sie trinken erstmal einen Kaffee und schauen wieder drauf und
verstehen es dann nicht mehr, was sie geschrieben haben.
Dann kommt der Moment, darüber nachzudenken,
ob es nicht vielleicht eine simplere oder besser zu verstehende Funktion
Möglichkeit gibt oder ob man Kommentare reinschreiben sollte.
Gut. Was wir gesehen haben an diesem Beispiel hier,
dieses Fold Duell und Bitterite, dass die verhalten sich so als orthogenale Funktionen.
Bitterite erzeugt aus dem Nichts erzeugt es uns Listen und Falten,
kollabiert das Ganze wieder zu irgendetwas zusammen.
Also das sind zueinander orthogenale Funktionen,
die ganz gut zusammenpassen und die man meistens als Paar verwendet
Wir könnten uns einfach, um zu üben,
auch nochmal anschauen, wie wir Interret von Hand selbst schreiben könnten.
So ist es eben auch in der SE3-Bib drin.
Also Interret bekommt als erstes Argument ja die Funktion,
die in jedem Schritt wieder aufgerufen wird, ob das vorherige Ergebnis.
Denn Endetest und ein Startwert, ne? Und die Iteration besteht darin,
dass wir hier erstmal eine lokale Funktion Iteration bauen,
die macht Folgendes, die kriegt ein bisheriges Ergebnis und den Endenwert,
der dazukommen soll, ja, initialisiert einen neuen Wert,
in dem die Funktion angewendet wird auf den Endenwert hier.
So, wenn das Ende erreicht ist, führt diesen Enden,
ja, also der erfüllt die Abbrachtbedingung, dann geben wir das Resultat zurück.
Und in allen anderen Fällen rufen wir uns hier rekursiv wieder auf,
indem man den neuen Therm mit Cons vor das Resultat setzt, ja.
Und das als Ergebnis geben wir den neuen Therm zurück hier. So,
und weil wir hier eine End-Rekorsion haben, wo wir einen Akkumulator haben,
wo immer dieser neue Therm drinsteht, das neue Ergebnis drinsteht,
Theorisalt, müssen wir anschließend das Ergebnis mit Reverse umdrehen.
Also was sollten wir jetzt alles an Funktionen kennen?
Wir haben zuerst die Funktion im Ply besprochen.
Apply nimmt eine Liste von Werten und ruft die Funktionen auf,
als stünde sie als erstes Argument in dieser Liste
als erster Wert in der Liste all der Argumente
Weil wir ja nicht einfach eine Funktion auf eine Liste anwenden können,
wenn die sagen, bei 20 Argumente und die 20 Argumente kommen als Liste daher.
Das Apply schreibt uns den Namen der Funktion vorne in die Liste rein,
sodass praktisch Funktionsname am Anfang mit den Argumenten dahinter evaluiert wird.
Das ist immer sehr nützlich, wenn wir Sachen, wenn wir die vielen Argumente als Liste kriegen.
Denn als allerwichtigste Funktionen, die sie auf jeden Fall kennen,
müssen am Ende des Semesters Map Filter und Pult L oder Fult R,
also Map, das typische Entwurfsmuster,
wo wir eine Liste von Argumenten haben und jedes soll systematisch abgebildet werden,
filtern, über die Liste von Argumenten haben,
aber wir wollen eine Teilmenge davon rausfiltern.
Und falten, wenn wir eine Liste von Argumenten haben und wollen die irgendwie zusammenbauen,
ja, zusammenreduzieren auf einen Wert, das Minimum,
das Maximum, den Mittelwert oder was weiß ich,
das können wir eben mit Falten machen.
Und Itorate, das erzeugt aus dem Nichts eine Liste von Werten,
ja, baut sie uns auf iterativ. Wir haben auch gesehen,
wenn wir eine Zusicherung ins Programm reinschreiben, als Kontrakte,
Verträge, diese zum Beispiel Flat Contract und andere, das sind alles Funktionen, höhere Ordnung.
Die kriegen andere Verträge als Argument oder eben andere Funktionen. Gut,
es gibt in der Welt der Funktionalen,
der Funktionen höherer Ordnung auch noch imperative Strukturen,
mit denen wir zustandsorientiert programmieren können und ganz typische,
die möchte ich bei der Gelegenheit kurz einfügen,
weil wenn wir interimperative Strukturen tatsächlich mal benutzen wollen,
eigentlich ganz nützlich wären, das mit sowas wie Generatoren,
ja?
Ein Generator, das ist nur eine Datenstruktur in Reckett,
die wirklich einen Zustand hat. Und ein aktives Element.
Sie können sich es praktisch vorstellen wie einen Objekt in Java. So ein Generator,
der läuft in einer endlosen Schleife und hat einen Zustand,
weiß, was er bisher getan hat und weiß,
was er noch tun soll und gibt uns immer wieder einen Wert zurück.
Ja, wir können ihn immer wieder fragen nach dem neuesten Wert.
Also eine Quelle, aus der wir Werte bekommen können
Der Putt vom Generator wird zugeschrieben, dass er erstens keine Argumente hat,
sondern der läuft einfach los und muss schon wissen,
was er tun soll.
Und hat dann einen Body, also einen Körper,
in dem Racket-Anweisungen stehen. Und eine der Anweisungen sollte,
so eine Subolic is expression, sollte Heels sein, ja.
Heels sagt dem Generator, okay, an der Stelle unterbricht man.
Wie weit bist du gekommen und was ist der augenglückliche Wert, den du hast?
Und den bekommen wir zurück. Mit dem Generator Aufrufen bekommen
wir immer den Wert von dem Heels, wo er gerade steht.
Und beim nächsten Aufruf Da fängt er wieder an zu rechnen,
ab der Stelle, wo wir ihn unterbrochen haben.
Macht weiter, macht weiter, bis er wieder auf dem Heeld kommt.
Und dann gibt er uns den nächsten Wert, ja.
Also praktisch so ein Generator treiben wir oder ein Generate Debt
schreiben wir zur Arbeit an, indem wir ihn aufrufen.
Und er macht so lange, bis sein Kurt sagt,
Tierpause, da, hielt. Dann kriegen wir einen Wert
. Wir können also ganz simpel damit eine Funktion schreiben,
die wir aufrufen und die uns jedes Mal eine andere natürliche Zahl gibt,
die in der Reihenfolge sie aufgezählt werden.
Wir können, wir schreiben hier meine Funktion,
next für irgendein
Und da schreiben wir als erstes rein, hielt X. Ja.
Damit bekommen wir das X, was wir gerade haben, zurück.
Und dann ruft die Next sich rekursiv auf und berechnet 1 plus X. So.
Das ist also eine Funktion, die den ersten Jahr zurückgibt,
nämlich das, was gerade gekriegt hat.
Und dann im nächsten Aufruf das neue X berechnet,
rekursiv, aber sie sehen, das führt glücklicherweise dahin,
dass man hier wieder bei mir landet und diesen Wert
dann gleich zurückgibt und gleich zurückgibt und so weiter.
Und ein Generator für natürliche Zahlen wäre etwas,
er hat keine Argumente und da rufen wir einfach auf,
Wann? Ja. So. Und die sehen schon,
warum das eine imperative Programmierung ist und Zustand.
Orientiert. Im reinen funktionalen Programmierstil, ohne Zustände,
würde der Aufruf einer Funktion mit demselben Argumenten immer wieder dasselbe Ergebnis geben.
Wenn wir hier unseren Generator aufrufen, natz,
beim ersten Mal, kriegen wir eine Eins zurück,
wenn wir ihn wieder aufrufen kriegen, meine zwei,
das dritte Mal eine Drei und so weiter.
Also bei jedem Aufruf kriegen wir eine andere Zahl.
Die werden systematisch erzeugt, aber wir haben hier Eine Möglichkeit zustandsorientiert,
relativ funktional zu schreiben und trotzdem Zustände zu haben und sie zu verbergen.
Also wenn man ein Iterage selbst programmieren will,
macht man es typischerweise in Recket mit Hilfe von Generatoren.
Und wir würden als erstes so einen Generator bauen, dem wir eine Funktion übergeben.
Also das, was wir hier fest reingeschrieben haben,
next wann, dass wir das als Funktion übergeben an etwas,
was uns einen Generator erzeugt, der eben eine Funktion immer wieder aufruft.
Damit würden wir eben einen Bitte räts bekommen, ja.
Und dann eine weitere Funktion, die sagt,
ich möchte jetzt diesen Generator haben und ich möchte so lange Elemente nehmen,
die er mir gibt, ja,
der sprudelt so vor sich hin, taktt immer wieder,
bei jedem Aufruf gibt er mir einen anderen Wert.
Eine Funktion, die uns das automatisch einsammelt und aufhört,
wenn eine bestimmte Bedingung erfüllt ist. Und diese nennen wir Take,
also Elemente nehmen von einem Strom, der kommt,
und zwar so lange, bis eine Bedingung erfüllt ist, ja.
Und Diese Funktion F-Generator, die könnten wir so schreiben.
Also ein Generator, der bekommt eine Funktion und einen Startwert.
Das ist eigentlich genau das an Till, was ich vorher vorgestellt habe.
Nur, dass wir hier das im Inneren verheimlichen,
dass wir dein zustandsorientierte Art haben.
Dieser Generator hat seine interne Schleife, die taufen wir mal F-Loop.
Und das ist eine Funktion,
die als erstes erzielt macht und sich hinterher rekursiv aufruft.
Und wir rufen hier in diesem Let's
Track auf mit dem Startwert als Startwert und dann geht's los
Wenn wir jetzt sagen, F-Generator und übergeben als Funktion,
die Quadratfunktion, X-Malex hier, nur in Labinnernotationen ausgeschrieben,
muss nicht so sein, wir könnten es querschreiben.
Mit dem Startargument 2, das Kind haben wir Potenzen getofft,
wir rufen Potenzen auf, das erste Mal kriegen wir zwei,
das zweite Mal kriegen wir vier.
Also genau, was wir hier sehen, das macht genau das,
was ich vorher als Antill geschrieben habe, als Funktion,
die in Reckett nicht vorkommt.
In Reckett gibt es stattdessen eben sowas wie Generatoren und mit
Generatoren würde man an Antill so machen
So, wenn wir an jetzt, okay, da waren wir.
Das Ganze, Moment, das Chake habe ich hier
noch nicht erklärt, wo ist das denn abgeblieben?
Okay, na gut, kommt vielleicht noch.
Wenn wir in ganz allgemein den Recked-Datentyp Sequenz nehmen, ja?
Denn auf Deutsch könnte man vielleicht Sequenz nennen,
dahinter verbirgt sich alles, was eine lineare Anordnung hat.
Und wenn wir Code schreiben wollen, der gleich ist,
egal, Welche Art die Implementation dieser linearen Anordnung ist?
Ob das so eine Liste ist,
ein Vektor, eine Hash-Thebel oder Strinks,
dann können wir allgemeine Schleifen machen und die über eine Sequenz laufen lassen und wir verbergen,
was für eine Art von Sequenz übergeben wurde.
Ob das nur eine Liste ist oder so. Also so eine Fortschleife in Ragit,
die verwendet den Datentyp Sequenz, wir haben hier ein Element,
das wird initialisiert, egal welche Art die Sequenz ist,
aus dieser Sequenz werden nacheinander die Elemente genommen und wir haben jetzt eine Vorschlage,
die läuft über die Sequenz,
die hier als zweites Argument steht
Also ein I kann eins sein, kann zwei sein, kann drei sein.
In dieser Schleife zeigen wir es einfach an und kriegen dann eins,
zwei, drei. Oder 123. Wenn wir Sequenzen aufzählen,
weil es als eine Liste, also praktisch auf Listen,
abbilden wollen oder einen Generator haben,
der nacheinander alle Elemente einer Sequenz nimmt,
dann können wir Sequence Generate daneben und irgendeine Sequenz übergeben.
Das ist toll. Achso, ja. Und wenn wir jetzt
einen solchen Generator haben und wollen von dem die ersten 30
Elemente haben oder die ersten Elemente, bis eine Bedingung erfüllt ist,
dann können wir jetzt das versprochene Tech-Generator an Till schreiben.
Ja, wir übergeben hier irgendeine Sequenz. Hash-Tabelliste String, Vector, Egal.
Und sagen, wenn da auffähren wollen, ja. Wir sagen,
wir nimm mir von dieser Sequenz, die unendlich sein kann,
wenn da keine Abbruchbedingungen drinsteht, so viele bis diese Funktion,
die wir hier als Endeprädikat entübergeben, sagt, du,
ja.
Gut, könnten wir einfach so machen
Dass wir hier die Funktion in Producer aufrufen, so.
Introducer nimmt eine Sequenz, guckt sich die Elemente an
und liefert uns den Teil, der ein Prädikat erfüllt.
So, das erste Element vom Sekt, das Prädikat enterfüllt,
dann wird Introducer auf. Also wir können jetzt mit Vorschleifen,
die automatisch über Sequenzen laufen, und ein Element iten,
das wir aus dieser Sequenz herausnehmen, Mit Inproducer ist Zack und Ende Prädikat
. Hier ein Element bekommen und haben damit einen Generator.
Der liefert uns immer wieder ein Element, noch eins,
noch eins, noch eins, noch eins. Und wenn wir,
dass wir aufrufen, Tick, Generator, Antil,
Sequence Generator, erzeugt uns einen Generator mit Naturals 3. Ja,
dann würden wir hier die Teilliste 3, 4,
5, 6, 7 kriegen.
Wir haben drei begonnen und hören auf. Wenn
die Bedingung X größer 7 fürs erste Element erfüllt ist,
das ist nicht mehr mit drin.
Ja, damit können wir uns dann auch eine Funktion Itterage an Till bauen,
die jetzt aber zum Unterschied von vorher, von vorher,
nicht in seine Liste aller Werte liefert bis dahin.
Vielleicht interessiert es Zwischenergebnisse gar nicht. Wir wollen nur das
erste Element wissen, was den Wert erfüllt. Und den,
das merken wir uns mal einfach, dass wir auch das
in der SE3 befinden und verwenden das jetzt für ein Beispiel.
Bevor wir da hinkommen, sollten wir jetzt uns nochmal gucken
, angucken
Wie waren wir mit Modulen arbeitet? Wir haben sie bisher verwendet,
aber nicht genau besprochen, besprochen, was möglich ist, ja.
Beim Modulen bauen da einfach Einheiten, die zusammengesetzt genommen werden,
um ein größeres Darkett-Programm aufzubauen, ne.
Und dazu sollte man, so wie sie in Java-Klassen vorspezifiziert haben,
in Recket auch die Module spezifizieren, sodass man weiß,
wie man sie zusammenfügen kann.
Ja. In Recket ist jeder Fall,
der mit langem beginnt, automatisch ein Module,
man kann Module nicht schachteln,
oder sollte es nicht
Das geht auf den alten Spruch von Perbren Schanzen zurück,
der die erste Programmiersprache erfunden hat,
bei der man Sachen auch nicht zusammen einfach verschachteln konnte,
auf Modulebene.
Er hätte gesagt, Nesting ist for birds.
Okay. In Recket haben wir die gleiche Situation.
Wir können viele, viele Module schreiben. Ja,
wir können auch in einen Pfeil viele Module nebeneinander setzen,
aber ein Modul hat keine Untermodule.
So, was passiert, wenn dein Modul Ein Lesen in Record.
Dann wird die Syntax expandiert, das Modul wird übersetzt,
wenn es dann übersetzt werden soll.
Aber der Körper wird erst dann ausgeführt, wenn ein anderes Modul,
ein Element aus diesem Modul verwenden will.
Wenn es über Require dahin importiert wird. Jedes Modul wird
höchstens einmal ausgeführt, auch wenn es mehrfach über Require irgendwo reingezogen wird.
Was wir im Modul schreiben, das ist das,
was man sich von Modulen erhofft.
Die sind nur lokal sichtbar, den Namen. Aber alles,
was außerhalb verwendet werden können soll,
dafür schreiben sie Provide.
Das macht es außerhalb sichtbar
Wenn wir beim Provide, kann man ziemlich ausführlich sagen,
was man zur Verfügung stellen will, vor allem auch das,
was man aus anderen Modulen importiert hat,
kann man zur Verfügung stellen und sozusagen über ein Sammelmodul schaffen,
was alles, was sie importiert haben,
nach außen bringt oder ausgewählt Teile davon.
Also wir können, wenn wir hier ein Modul schreiben und haben aus irgendeinem anderen was drin,
ja, dann könnten wir das mit einem René exportieren.
Also wenn wir wollen, dass alles,
was aus unserer Modul kommt, mit dem Schlüsselwort Osterhase beginnt,
dann könnten wir hier in René nehmen,
Das mit René zur Verfügung stellen, uns einen lokalen Namen dafür ausdenken,
wie wir in lokal verwenden und einen,
der für die exportierte Version verwendet wird,
eben unser Osterhase zum Beispiel.
Wir können Structures exportieren, wir können sie gleich so definieren,
dass sie hier definiert werden und dann sich nach außen sichtbar werden.
Wir können auch einfach sagen, ich habe das Modul SE3
mit reingenommen und alles, was ich davon genommen habe,
das soll auch hier nach außen sichtbar werden.
Oder alles mit Ausnahme von. Und dieses alles mit Ausnahme von,
das ist wichtig, wenn sie Mehrere Module haben,
importiert haben und die verwenden alle für ein bestimmtes Element denselben Namen,
aber die bedeuten alle was unterschiedliches.
Und dann können sie sich überlegen, welches dieser drei Varianten der Sinusfunktion,
die sie jetzt bekommen, haben sie nach außen geben wollen,
aus dem Modul 1, 2 oder 3.
Also dafür ist das All from Accept, damit sie Namenskonflikte verwenden können.
Oder wir können doch einfach sagen, alles,
was hier definiert wurde, geben wir nach außen.
Das wäre ziemlich denkvoll. Da müssen nicht sie nachdenken über
die Anwender. Oder alles, was definiert ist,
aber mit Ausnahme, diese fünf Sachen will ich
geheim halten
Sie können alles nach außen geben, indem sie ein Präfix davor setzen.
Zum Beispiel die Osterhase wieder, das ist mein bestimmtes Modul.
Und dann werden ihre Namen hoffentlich konfliktfrei exportiert.
Okay. Und dann können sie auch noch Sachen.
Project machen, dann dürfen sie nicht verändert werden.
Sind sie zwar sichtbar, aber die sind geschützt. Gut,
also wir können jetzt im Toolsmodul, haben wir zum Beispiel drin,
Provides, Funktionen, Conjoin diss join albus,
never some als Funktionen oder als Prädikat, Every,
Itter, Unchild und so weiter, das steht drin,
ja, Tech Generator, F Generator.
So, und da steht eben auch drin, was wir aus anderen Modulen verwenden.
Ich verwende zum Beispiel Dev Macro sehr viel.
Ich verbinde Swinde mit allen Extras und Record Generator. Ja,
und dann speziell aus dem Paket Record Goui verwende ich
auch Ball, aber eben nur in diese Modul.
Gut, also das sind Beispiele,
wie man mit Provide und Record arbeitet in dem Practice Modul,
was wir angesprochen haben, über Fratale Schneeflocken gezeichnet haben.
Also das wird zur Verfügung gestellt und das wird gebraucht.
Ja, so sehen die Schnittstellen aus
Sie können im Provide Zusicherungen mit reinnehmen,
die erfüllt sein müssen, wenn sie nach außen gegeben wird,
dann müssen die Anwender, worauf sie sich verlassen können.
Wenn sie nicht erfüllt sind,
dann gibt es eine Fehlermeldung und dann sehen sie das eben.
Das hatten wir bei den Kontakten schon gesehen. Gut. Ja,
jetzt mal ein Beispiel, wo wir einen etwas größeren Programmtext entwerfen und
im Zuge dieser Vorlesung werden ab jetzt viele Fallstudien kommen,
wo wir rected anwenden und damit bestimmte Dinge machen.
Jetzt mal unser erster Versuch mit etwas funktionalem modularen Entwerfen,
ein etwas größeres Programm zu schreiben
Module kennen wir schon. Und was jetzt entworfen werden soll,
ist ein, erstmal für den Anfang ein recht kurzes Programmpaket,
mit dem wir null Stellen von Funktionen berechnen können.
Ja, ganz allgemein. Also wir hatten schon als mathematisches
Beispiel gesehen, wir können die Ableitungsfunktion einer Funktion bestimmen.
Mit der Funktion Drif, da haben wir eine Funktion erzeugt
aus einer anderen. Jetzt bauen wir einen Baustein,
der uns für gegebene Funktionen ihre Nullstellen sucht,
braucht man ja immer mal wieder und ist ganz gut,
man das als Racket-Funktion auch zur Verfügung hat.
Um warm zu werden und uns zu überlegen, wie man das überhaupt macht.
Erstmal für eine einzige Funktion, für die Funktion, für die Quadratwurzelfunktion.
Und im nächsten Schritt werden wir das verallgemeinern, indem wir
das Newton-Verfahren anwenden, um das für beliebige Funktionen zu machen.
Und dann sind wir jetzt beim Funktionalen Programmieren.
Wir müssen dem Newton-Verfahren dann natürlich sagen, welche Funktion ist
differenziert und für welche Funktion ist die Nullstellen suchen soll.
Aber erst mal zur Erinnerung, was die Quadratwurzel von X sein soll.
Und dann lösen wir das Ganze eben als Nullstellen-Problem.
Moment. Also, Quadratwurzel von X ist die Zahl,
ja, die mit sich selbst multipliziert die Zahl X ergibt.
Und außerdem soll sie positiv sein. Ja,
also wir schränken das erstmal auf die positiven Quadratwurzeln.
Wir könnten natürlich auch da zwei Wurzeln rauskriegen,
auch eine bei einer positiven Zahl, eine,
die negative Zahl mit selbst multipliziert.
Wir können aber, um das jetzt hier nicht exakte Gleichheit zu fordern,
was bei Floating-Point-Zahlen immer ein bisschen riskant ist,
auch sagen, wie genau wir es haben wollen,
indem wir jetzt sagen, der Rest, ja,
die Abweichung des Quadrats, der Quadratwurzel von X,
von X sollte möglichst klein sein,
kleiner als in Äpzilern und das Y geben wir hier vor.
So. Damit haben wir spezifiziert, was unsere Quadratwurzelfunktion können soll, ja.
Und jetzt müssen wir anhand dieser Spezifikation es so implementieren,
dass auch das rauskommt,
was wir uns erhoffen,
Eine Implementierung. Es kann durchführen. So, und hinterher müssen wir
eigentlich auch noch beweisen, dass tatsächlich das Richtige immer rauskommt.
So, was hatten wir gefordert? Die Quadratwurzel,
die wir schreiben sollen, größer gleich null sein.
Und der Abstand von Quadrat, der Quadratwurzel zu x kleiner
als epsilon und das epsilon so kleiner als null sein.
Wir können das jetzt mal einfach, indem wir uns
eine Quadratwurzel aus den Haaren sorgen für die zwei Versuche.
Unsere erste Schätzung für xgleich 2, sagen wir mal 1,4,
1,4,
1,
Mit sich selbst multipliziert, kommt noch nicht ganz hin,
modifizieren es ein bisschen, letzte Stelle ändern,
sind wir dicht da dran und wenn wir sie nochmal ändern,
dann schießen wir übers Ziel hinaus.
Die Differenz zur gegebenen Zahl ist jetzt aber richtig und damit
haben wir, mit dem wir eine Quadratwurzel gefunden.
So, jetzt für eine spezielle Funktion können wir das Newton-Verfahren
so anwenden, wenn wir irgendwie einen Ernährungswert haben.
Kennen Sie aus der Mathematik eigentlich? Wir müssen uns
hier überlegen, wie wir es im Recket machen
Also wir haben irgendeinen, in einem Iterationsverfahren versuchen wir Ernährungswerte,
was zu finden. Und wir nehmen jetzt mal an,
unser Ernährungswert heißt YNN und das sei Ernährungswert für Wurzel-X. So,
Newtons Verfahren, einfach zu Fuß ausgerechnet, sagt uns,
wir kriegen einen besseren Nährungswert, wenn wir das YN,
was wir schon haben, nehmen.
Das X durch Y-Teil, durch YNN-Teilen,
das ganze Halbieren und da haben wir einen besseren Nährungswert.
Was machen wir hier an dieser Stelle?
Wir berechnen die Tangentensteigung an den Funktionen und die Schnitzel der
Tangentin mit der X-Achse
Dass das, was es eigentlich tut.
Und wenn wir jetzt mal los iterieren, gut,
wir haben Y-0, ja, das soll 2,
dann wenden wir jetzt diese Familie ein,
Y-N plus X durch Y nennen das Ganze halbiert.
Dann kriegen wir hier 1,5 raus. Das wieder 1,5 da
reingesteckt und so weiter und so weiter und so weiter.
Sie sehen jetzt schon, warum wir die Funktion Itterate eingeführt haben.
Wir brauchen einfach etwas, was die Werte nimmt und in
jedem Schritt eine Funktion darauf anwendet und einen neuen Wert liefert.
Dafür haben wir Atherate gemacht. Und das können wir jetzt mit einem Funktionen,
höherer Ordnung machen, indem wir nämlich die Iterationsregel als Funktion übergeben,
die immer Y plus X durch Y rechnet und halbiert.
Diese Iterationsregel wollen wir im Prüf nennen, verbessern. Als Abbruchbedingung,
da müssen wir gucken, sind wir dicht genug dran,
sodass die Differenz von Y quadratminus X kleiner Y ist.
Diese Funktion wollen wir Good Enough taufen. Und als Öpzilon,
da sind wir mal großzügig, also allzu viel Genauigkeit erfordern,
bin ich. Ja, und das Ganze müssen wir in
einer Wiederholschleife machen und dann nehmen wir eben unser Iterate Until
Okay, also, unsere handgemachte Quadratwurzelfunktion,
da sagen wir hier erstmal Y, geben wir vor,
mit 0.001, ja, die Funktion Good enough.
Da berechnen wir eben den Absolutbetrag der Differenz zwischen dem Quadrat,
von unserem Schätzwert und X, ja,
und das soll kleiner sein als Apps.
Und für jetzt Iterationsregel zum Verbessern,
da nehmen wir hier unser Y plus X durch Y
geteilt durch zwei
Auch als Landeausdruck drinsteht. Und jetzt müssen wir nur noch sagen,
Iterate Until, Itter Until, mit Hilfe von Improve,
das erste Argument, was den Wert mal verändert,
die Abbruchbedingungen, Good Inuf und dann übergeben wir hier den Startwert.
Und das als Startwert nehmen wir einfach die Zahl selbst und dann kriegen wir raus,
Jörg Quadratwurzel von zwei ist eins durch, zwei, zwei plus eins durch.
Ja, wir haben es mit fraktaler Genauigkeit hier rausgedrückt.
Genau. Wenn wir jetzt, da ist noch nach inneexakt Wandel,
jeden Aufruf von exakt nach inneexakt machen,
dann sehen wir auch, dass hier die Quadratwurzel von vier,
ganz schön dicht an zwei dran liegt.
Gut. Ja, jetzt wollen wir das Ganze noch mit Verträgen versehen,
damit unsere Kunden müssen, worauf sie sich verlassen können.
Also unsere als Hilfsfunktion definieren wir hier mal einen Good.
Ja, ist es gut genug. Ist also X eine Wurzel,
die gut genug ist und hat eine lokale Variable hier in Apps eingeführt,
betestend, ob das Quadrat,
der Abstand zwischen Quadrat der Wurzeln und X kleiner ist als Y mehr.
Das macht unser Good hier. Und das definieren wir als
globale Funktion außerhalb
Und unseres. Jetzt können wir unsere Quadratfuchswurzelfunktion mit einem Vertrag definieren.
Ja. Also die Funktion einer Definition mit Vertrag,
die sieht so aus. Ja. Das ist eine Funktion,
die bildet irgendwas auf D ab und die ist ein Typ.
Und bekommt ein Argument. Und von diesem Argument schreiben wir
hier als Vertrag rein, das ist ein Flat Contract,
der prüft, ob der Wert größer gleich null ist, ja.
Das ist der Name der Funktion, die diesen Vertrag erzeugt.
Also Vertrag größer gleich null, haben wir hier fürs Argument X. So.
Damit haben wir jetzt das De so definiert,
dass man hier nichts Negatives übergeben kann
Und fürs Resultat sichern wir hier zu, einen Endkontrakt.
Wir erstens, versprechen wir, der gebende Zeit zurück.
Das erzeugt einen Vertrag, dessen logisches End aus zwei anderen Verträgen.
Erstens, dass das, was zurückkommt, eine Zahl ist.
Wir haben uns hier nicht mal festgelegt, ob das noch
eine Zahl ist. Das könnte auch eine Rationalzahl sein.
Und außerdem muss erfüllt sein, das Prädikat Gottier.
Das sichern wir auch zu. Also das,
was wir vorher definiert haben als Funktion Good,
das biert hier als dritte Bedingung rein.
Also all diese drei Dinge sind erfüllt fürs Resultat.
Wenn uns jemand eine Position positive Zahl übergibt, als Ex.
Wir haben ja übrigens nicht mal gefordert,
dass es eine Zahl ist, das könnten wir hier auch noch tun.
So, und jetzt wird diese Funktion hier so definiert,
dass sie diesen Vertrag erfüllt und das schreiben wir hier unten rein.
Was die Funktion tun soll, ist dieser Landerausdruck?
Ja. Hier ist Good Innuff, was wir verwenden,
dazu verwenden wir einfach das Good,
was wir außen definiert haben und hier für die Zusicherung verwenden.
Und als Iterationsregel haben wir hier unsere Iterationen reingeschrieben.
Ja, das Impruf ist eben diese Wurzel und so weiter.
Und was macht diese definierte Funktion Itter an Till
Iteriert, diese Empruffunktion, bis Gott ihn erfüllt ist.
Moment. Ja. So. Und jetzt rufen wir es auf,
die exakt inneexakt, ja. Zum Beispiel mit Miles Queruit
4 und wir kriegen ein Ergebnis raus und keine Klagen,
ja. All diese Zusicherung, die wir reingeschrieben haben,
die halten still, solange alles gut ist.
Aber wenn wir den Vertrag verletzen oder eine Vertragsverletzung erkannt wird,
dann kriegen wir dadurch eine Fehlermeldung. Zum Beispiel,
wenn wir hier jetzt unseren Miles Queruit aufrufen mit dem Symbol
4 und keine Zahl übergeben haben, Moment,
ich habe hier überhaupt nicht gefordert, dass das Argument eine Zahl ist.
Aber dieser Vertrag größer gleich null, der wird meckern,
wenn er keine Zahl kriegt, die er überprüfen kann.
Also er sagt, das Symbol 4 ist nicht größer gleich null.
Und deshalb kriegen wir hier eine Fehlermeldung. Er sagt uns,
was erwartet wurde, ein Wert größer gleich null,
aber wir bekommen das Symbol 4 bekommen. Gut,
also wir haben hier unsere Funktion good enough ans Querroot schon
ganz gut modular geschrieben, indem wir als Basisfunktion definiert haben,
die wir kombiniert haben mit Funktionen höherer Ordnung
Und damit haben wir einen modellanen Programmierstil. Das haben wir
jetzt gemacht, Fest verdrahtet für die Wurzelfunktion.
Und jetzt der nächste Schritt für Funktionen höherer Orten.
Wir möchten jetzt mal endlich eine erzeugen, die uns andere
Funktionen als Ergebnis gibt. Wir werden jetzt das allgemeine Newtonverfahren implementieren,
sodass es nicht nur für Quadratwurzeln geht,
sondern diese Iterationsfunktionen automatisch erzeugt wird, die null Stellen sucht.
Denn im Herzen das Judenverfahrenswurzeln zu finden.
Also die Anwendung des Newton-Verfahrens und Wurzeln zu finden,
bestand eben darin, dass wir Nullstellen gesucht haben.
Wir brauchen jetzt als erstes einen Baustein,
der ganz allgemein für irgendeine Funktion eine Nullstelle findet.
Und damit können wir dann Funktionen reinstecken und bessere Schätzung mit Hilfe des Newton-Verfahrens bekommen, ja.
Gut, also die Idee des Newtons ist,
wenn wir für eine Funktion eine Nullstelle suchen,
dann betrachten wir sie lokal einfach als gerade, als geradlinig.
Und anstelle der Nullstelle der Funktion, suche über die Nullstelle
der Tangente an der Stelle, wo wir gerade sind.
Wenn die Funktion, die Tangente sich gut an die Funktion anschmückt,
ist das gar kein Problem, dann kriegen wir damit schon
eine gute Schätzung und wir kommen immer dichter an die eigentliche Nullstelle ran,
wenn die Funktion die Tangente sich von so außen dran schmückt.
Also das hier sei unsere Beispielfunktion hier und wenn wir jetzt die erste Tangente nehmen,
die schneidet den Wert hier, wir nehmen diesen Schnittpunkt,
also an der Stelle, wo wir hier gerade sind,
wenn neben diesen Schnittpunkt jetzt als neue Schätzung Und für den Punkt berechnen wir jetzt die Tangente.
Und dir ist schon viel dichter dran,
nehmen wir als nächste Schätzung für den Punkt die Tangente,
müssen wir den Wert einsetzen, kommen dichter ran und so weiter.
Und irgendwann sind wir an der Nullstelle der Funktion angekommen.
Und ganz allgemein müssen wir um so Nullstellen von Funktionen zu finden,
einfach nur von dem Wert fettswert, wo wir gerade sind,
den Funktionswert an der Stelle nehmen und ihn durch die erste Ableitung teilen,
ja, und das subtrahieren wir von dem Y.
Und damit haben wir eine bessere Näherung und das können wir
iterativ immer wieder machen
Wenn wir jetzt das Newton-Verfahren anwenden, dann würde also die Funktion F,
für die wir die Nullstelle suchen, übergeben werden.
Und wir haben einen ersten Startwert, den wir haben. So,
also hier das Newton-Verfahren liefert uns als Ergebnis nicht die Nullstelle,
sondern eine Funktion, die die Nullstelle findet.
Woran sehen wir das? Hier steht Newton F,
das ist die Funktion. Und was kommt zurück?
Ein Lander
Also das Resultat von Newton von F, es liefert uns die Iterationsfunktion,
die bekommt als einen Startwert und iteriert dann über F so lange, bis wir die Nullstelle haben.
Gut, also wir kriegen dieses Newton von F, den Iterator zurück.
So, und der erwartet ein Argument First Guess. Der hat fest eingebaut,
eine Genauigkeit, der hat fest eingebaut,
eine lokale Funktion Good Innov, die testet,
ob wir angekommen sind, hat fest eingebaut,
den Aufruf, wo gerechnet werden soll,
Y N-F von Y durch F-Strich von Y. Das heißt,
wir müssen die Ableitung berechnen.
Wir werden eine Ableitung zu einer Funktion berechnen, haben wir
schon das letzte Mal gemacht, ja. Wir nehmen,
also F von Y und Teilens,
durch den Aufruf von DRIF von F. DRIF von F liefert
uns die Funktion der ersten Ableitung und da beginnen wir den Wert Y. So,
und das Ganze stecken wir in einen großen Aufruf von Itta Ante im Puff,
itteriere,
für lange,
bis es gut genug ist und starte mit dem ersten,
mit der ersten Schätzung hier
Also was haben wir hier, das Newton-Verfahren,
um null Stellen einer Funktion zu finden auf einer Folie?
Das geht im funktionalen Programmierstil unglaublich kurz und knackig.
Wenn wir jetzt also unser Quadratwurzel-Problem ganz allgemein lösen wollen,
dann sagen wir Newton. Und iteriere über die Funktion,
die den Abstand des Quadrates von Y zu X berechnet.
Und wenn wir da die Stelle gefunden haben,
an der dieser Abstand null ist,
dann haben wir gewonnen
Also hier kommt dieses, dieses Lande wird als Funktion übergeben,
nachdem wir suchen. Wir müssen praktisch, um das Quadratwurzel
und Quadratwurzeln zu finden, das auf Nullstellenproblemen abbilden.
Und wir starten mit unserer Zahl X. Nach dem gleichen Schema hier,
dritte Wurzel, ja, da müssen wir eben Y,
mein Y, mein Y, X gleich 0,
dafür die Nullstelle finden.
Dann haben wir es auch, ja. Oder die Entewürzel wieder hier,
Newton aufrufen. Liefert uns den Iterator und den Iterator,
wenden hier, also das Newton-Verfahren liefert uns bekommt die Funktion als Argument,
liefert uns eine Funktion als Resultat.
Diese Funktionen rufen wir auf und der wird diese Zahl X
übergeben als die Stelle, für die wir den Funktionen haben.
Also Newton, wenn wir das so aufrufen, liefert uns die Teratorfunktion,
da geben wir einen ersten Stadtwert und dann legen wir los.
Und hier können wir unsere Funktionen aufrufen und sehen,
wir sind ganz gut an die Wurzeln auch reingekommen.
Okay, all das funktioniert, weil im Racket-Funktionen Closures
sind funktionale Abschlüsse
Nichts davon würde gehen in einer Programmiersprachenwelt, wo eine Funktion einfach
nur ein Pointer of Code ist und keine Bindungsumgebung mitkriegt.
Das können wir uns hier noch mal klar machen.
Ja, wir kriegen hier hier eine Funktion zurück.
Ein Landerausdruck, der Newton uns gibt.
Ja. Vielleicht hier an der Stelle. Hier kommt ein
Lamda zurück. Eine Funktion, die braucht einen Startwert.
Und die rufen wir auf, außerhalb der Definition von Newton.
Aber die Funktion, die hier zurückkommt, die weiß aufgrund
der Bindungsumgebung, da hat mir jemand ein Äffüberg gegeben.
Das weiß noch genau, für welche Funktion die Nullstelle gesucht wird.
Weil die eben im Funktionalen Abschluss angefangen ist.
Deshalb geht das Ganze
Okay. Und da wollen wir jetzt noch mal kurz drüber sprechen,
weil die nächsten Funktionen höherer Ordnung, die wir ansprechen,
sind allen, die nur deshalb funktionieren,
weil Recket und manche andere Programmiersprachen auch in Closures haben.
Ja, funktionale Abschlüsse. Sonst kann man das,
was jetzt kommt, total vergessen. Das Wichtigste ist das
Curry-Verfahren. Currying ist eine Methode, Funktionen zu nehmen,
die mehrere Argumente brauchen und daraus eine neue Funktion zu ermitteln,
die weniger Argumente braucht. Also für sämtliche Übungsaufgaben,
die jetzt kommen und so werden sie in Zukunft immer Currieren brauchen.
Also das sollte gut sitzen. Komischerweise ist das Curry-Verfahren nicht nach dem Erfinder benannt,
sondern eben nachdem der zuerst die Nützlichkeit erkannt hat und es
weiter populär gemacht hat, eben nach dem Logiker Curry,
ja, erfunden worden ist es von Schönfinkel.
Und es gibt einige hartnäckige Menschen,
die in ihren funktionalen Programmiersprachen immer Curry umtaufen, in Schönfinkelink.
Also ich finde es ein bisschen mühsam, wenn man über Funktionskomposition und Ähnliches spricht.
Und Beispiele hat immer Crinwinkel zu schreiben. Also nehmen wir mal
den, populär gemacht hat und nennen die Funktion Curry.
Ja. Also die Grundidee des Currying ist simpel und in
vielen Programmiersprachen ist es einfach so eingebaut,
als sind taktische Zucker und geht wunderbar.
Also in Miranda kann man, braucht man nicht mal Curry auszuschreiben,
sondern man klammert einfach nur die Ausdrücke und da hat es damit implizit.
Ja, die Idee ist die folgende. Nehmen wir mal an,
wir brauchen eine Funktion, die soll immer das Maximum von zwei Zahlen finden,
aber es steht für uns fest, eine der beiden Zahlen ist drei.
Ja, also wir wollen das Maximum von drei und sechs,
von drei und acht,
von drei und
Das erste Argument fürs Maximum ist immer fest, die drei.
Und jetzt kann man natürlich hingehen und schreibt sie eine Funktion.
Ein Dreiermax und ruft auf Max von drei und dem Argument
. Ja, und das andere Argument ist offen gehalten.
Aber das wird einem abgenommen, funktionalen Programmiersprachen durch die Curry-Funktion.
Wir schreiben ja eine Funktion Curry,
die bekommt eine Funktion übergeben, die mehr Argumente hat.
Und dann eine Liste von Argumenten,
die festgebunden werden sollen, an die erste der ersten Argumente
der Funktion
beginnen von links
Ein paar Argumente werden offen gelassen. Nehmen wir an,
die Funktion hat fünf Argumente. Wenn wir hier jetzt schreiben,
Curry übergeben die Funktionen und hier drei Zahlen,
dann werden die ersten drei Argumente festgebunden und wir bekommen eine Funktion zurück,
die nur noch zwei Argumente hat, nämlich die zwei Parameter,
für die wir nichts übergeben haben.
Also um eine Funktion zu haben, die immer das Maximum gegen drei erzeugt,
würden wir einfach schreiben, Curry, Max, Sony.
Und das ist eine neue Funktion,
die braucht nur noch ein Argument und da können wir jetzt
hier die Zähne übergeben und dann kriegen wir raus
das Maximum von drei und zehn ist zehn
Ja. Und das ist eben sehr nützlich,
wenn man diese Ausdrücke in Aufrufe von Map,
von Filter und alles Mögliche reinschreiben kann,
wo wir Funktionen mit weniger Argumenten brauchen als das,
was die Funktion natürlich als Argument hat.
Das erspart uns die Lambas zu schreiben, ja.
Zum Beispiel die Funktion Curry Plus, wann ist die Inkrementfunktion?
Das wäre die Funktion, die jedes X nimmt und um eins erhöht.
Oder Curry durch eins, dass wir die Rezeptbruckwertfaktion,
wir haben damit mit ein paar Tastenschlägen die Funktionen beschrieben,
die eben den Kehrwert an der Zahl nimmt,
Dann die Halbierfunktionen können wir so schreiben. Oh, halt
, stopp, hier steht jetzt Curry A. Warum so?
Weil wenn wir das zweite Argument binden wollen und nicht das erste,
dann müssen wir der Curry-Funktion sagen, fang von hinten an.
Wenn wir Curry A schreiben, dann werden die Argumente nicht
von links nach rechts, sondern von hinten beginnt, gebunden.
Also die Quadrierfunktionen könnten wir schreiben als Curry A, Exponent X.
Das berechnet eben X-Hoch 2. Die Negation wäre das Subtrahieren von Null
und wenn wir wissen wollen, ob etwas gleich Null ist,
könnten wir es auch schreiben.
Curry equaliziere
Gucken wir uns mal an, wie wir die Scurry jetzt verwenden.
Also wie gesagt, es ist praktisch das Mittel,
das uns ersparen soll. Bei Map-Filter und so weiter,
hier im Alamda-Ausdrücke schreiben zu müssen. Wenn es ein Fall ist,
wo wir einfach ein paar Argumente festbinden wollen, nehmen wir Curry.
Wenn wir hier zum Beispiel schreiben, Lap Curry,
Creator, Zero und ein Argument von einer Liste,
würde das abgebildet werden auf die Anwendung der Funktion,
die testet, ob die Elemente größer als null sind.
Und damit würden wir dann das zurückkriegen. Oder eine Verdoppelfunktion,
Curry mal zwei, dann würden wir hier eben Hier einfach das Mal zwei führt.
Das eine verbleibende X, das wird jetzt ein 1,
2, 4 und so gebunden.
Und wir haben die Multiplikation mit zwei.
Also die Skurriling ist unglaublich nützlich. Und wir konnten es
mal am anderen Beispiel ansehen, wo es ganz praktisch ist.
Fürs Newtonverfahren musste ich ja eben ziemlich viele Tangenten zeichnen.
Und wie machen wir das? Wie haben wir
die Funktion F von Nixes 2,5 mal x Quadrat?
Ja. Und dazu rufen wir,
die das letzte Mal angeführte Funktion Graph Pfannen auf und sagen,
nimm die Funktion
Und die zeichnen mal in Rot. Ja, Grafhand nimmt ja eine Funktion als Argument
und wir müssen noch sagen, welche Farbe sie haben soll.
Also ihr habt ja gesagt, Grafhand zeichnet man F. Das,
was hier übergeben wird und macht das in Rot.
Und jetzt die Tangenten an vier Stellen zeichnen,
nämlich an den Stellen X gleich 1, 2,
3 und 4, wollte ich die Tangenten haben.
Ja, dann machen wir einfach ein Mapping,
in dem wir unsere Funktion zeichnet, Tangente nehme.
Die Tangente an die Funktion F und zwar für das Zeichne
sie einmal an der Stelle eins in Blau,
an der Stelle zwei in Grün,
an der Stelle drei in Purpur und an der Stelle vier in Zyern.
Also der Wert tut mir die Tangentin nehmen und die Farbe sind hier als Parameter offen gelassen,
dass Curryin sagt aber, zeichnet Tangentin als ersten Parameter,
nimm immer schon mal die Funktion F. Ja,
das ist mir hier mit Curryin gebunden worden.
Also das hier ist die Funktion,
die die Funktion F zeichnet und die erwartet noch zwei weitere Argumente,
die nicht gebunden worden sind, nämlich die Stelle,
wo die Fangente gezeichnet wird und die Farbe,
in der
sie gezeichnet wird
Was auch sehr nützlich ist als Funktion höherer Ordnung und uns eben eine neue Funktion erzeugt,
die wir an Meppen und Filter übergeben können,
dass die Funktionskomposition hintereinander Ausführung von Funktionen.
Also wenn wir an irgendeinen Wert X dazu,
erstmal die Funktion, geht drauf anwenden wollen,
dann kriegen wir einen neuen Funktionswert, neuen Wert zurück.
Y und darauf soll jetzt F angewendet werden, dann würde man
das mathematisch schreiben als Komposition, die Hintereinanderausführung von F und G.
Und das macht uns in Recket Die Funktion kommt aus. Compos,
so wie es in Recke zur Verfügung gestellt ist, nimmt beliebig viele Argumente.
Also wir können viele Funktionen nacheinander ausführen. Indem wir einfach sagen,
Compos FG und wir kriegen diese zusammengesetzte Funktion,
als Resultat, eine neue Funktion heraus.
Zum Beispiel die Funktion Caddudda, die wäre schon häufiger gesehen haben,
könnten wir schreiben, als Kompos, Kötter, Kötter.
Oder die Komplimentfunktion. Für die würde man
einen Currion machen von Compos an der Notfunktion
Und also Kompos ist eine pullimorphe Funktion,
da können wir alle Paare oder eben mehrere Funktionen reinstecken,
wo das Resultat des einen als Eingabe fürs andere geht.
Also wir können Definitionen relativ einfach schreiben, indem man einfach einen Kompos schreibt.
Also wenn wir F3 auf X davon F2 und den F1
machen wollten, pri, würden wir diese scheußliche Klammergebirge kriegen.
Wir könnten auch einfach stattdessen schreiben, Kompos, F1,
F2, F3, dann ist jedem Leser klar,
da werden die drei Funktionen nacheinander angewendet und man muss nicht anfangen,
Klammern zu zählen
Ehrlich gesagt, es ist eine Geschmacksfrage. Also da scheiden sich die Geister,
ob man lieber die Klammern schreibt oder lieber das Kompost.
Also eine kleine bedeutende Mehrheit in der funktionalen Programmierung sagt,
wenn ich eine Funktion so definiere, dann sieht jeder,
da ist das die Hintereinanderausführung der Funktion,
aber wenn das so beschrieben ist,
sieht man nicht auf einen Blick,
sondern muss anfangen zu blättern,
von welchem Typ sind eigentlich die Argumente,
die ich übergeben kann
Ja. Hier steht nur F3. Ich muss nachschlagen,
was verträgt F3, das ist vielleicht als erstes aufgerufen wird,
eigentlich für einen Datentyp als Argument.
Das kann weit weg sein. Also das ist bei dieser
Definition hier nicht mehr zu sehen und das irritiert manche.
Die wollen gern eine Funktionsdefinition immer so haben,
dass man auf den ersten Blick sieht, ob da,
ich habe da als Argumenttyp Reellzahlen oder Symbole oder so weiter, ne?
Also hier zum Beispiel, wenn ich sage,
ich habe eine Funktion,
Wurzel definieren und das ist ein Quadrat aus dem Absolutbetrag von
Aber da sieht man durch den Aufruf von Absolutbetrag
eigentlich schon ganz gut, dass da Zeilen reingehen sollen.
Ich könnte es genauso schreiben, Kompos, Querroot Ups,
als Hintereinanderausführung von Absolutbetreib und Quadrat.
Und da muss sie geben, wissen wir das Absolutbetrag verträgt.
So eine... Also die Funktionskomposition Currying und das ist alles
nur möglich, weil wir Closures haben, funktionale Abschlüsse.
Und wenn die Funktion zurückgegeben wird, ja,
die komponierte Funktion mit Kompos oder die gebundene Funktion mit Currying,
dann kommt die ganze Bindungsumgebung wieder von der Stelle,
wo es erzeugt wurde.
Und deshalb kann diese so definierte Funktion,
alle lokalen Variablen aus der erzeugenden Curry-Funktion aufrufen.
Und die sind da. Programmiersprachen, die nur diese Funktionspointer
übergeben, da gibt es eben nicht.
Vielleicht sieht man an dem Beispiel ganz gut, warum es
eine Closure sein muss. Ich mache hier eine Hintereinanderausführung von
der Ableitung von Sinus und von der Ableitung von Tanjans.
Ich rufe einmal Derriff auf, ich rufe zweimal Derriff aus.
Beide Aufrufe von Derriff geben mir eine Funktion zurück
Aber das, was diese Funktionen zurückgeht,
die erinnert sich in ihrer lokalen Bindungsumgebung an Sinus.
Und der Aufruf von diesem Drift erinnert sich an den Aufruf
von Tannens und liefert mir die Ableitung für den Tannens.
Also diese Werte sind jenseits dieses Komposes, was hier herauskommt,
ja. Da weiß man immer noch, okay,
hier ist mein Sinus übergeben worden und ein Tangens.
Und diese Aufrufe sind in der Closure drin, ja.
So, die Map-Funktion ist distributiv, Hinsicht der Funktionskomposition.
Ja, das können wir uns auch nochmal klar machen
Also wenn ich hier ein Lapping mache und dabei eine Funktion über die Liste schicke,
die dir hintereinander, hintereinander Ausführungen von F&G ist,
das macht das Currying hier, bindet Map an das,
was Compus zurückliefert, dann ist es das Gleiche,
als wenn ich erst ein Mapping mache von F und dann
ein Mapping mache von G und das Ganze mit Compos zusammen verbinde.
Also diese Distributivität ist hier gegeben.
Und falls die Umkehrfunktion von F existiert,
kann ich auch
ein Currying machen von
Und davon die Umkehrfunktionen nehmen oder stattdessen an die Umkehrfunktionen von
F mit dem Mapping, über die Daten schicken.
Und bezüglich gepennt hatten wir da schon mal an anderer Stelle festgehalten.
Also all diese netten Eigenschaften gelten mit der funktionale Ausdrucke schreiben.
Ich habe Ihnen in der SE3-Bipp noch ein paar Funktionen dran getan,
die man immer wieder braucht, die er beim Racket fehlen aus unverständlichen Gründen.
Und das sind die Döhnenfunktionen. Und außerdem sollten wir auch
noch über Closures und Ereignisroutinen sprechen. Also die Dylan-Funktion nur
ganz kurz erwähnt. Kunst, das ist ein Konstruktor,
der erzeugt konstante Funktionen. Braucht man ab und zu,
wenn wir Funktionen höherer Ordnung haben,
mit vielen Argumenten und wir müssen ein paar von diesen Argumenten stilllegen,
mit Default werden.
Dann kann man oft sowas brauchen, wie die konstante Funktion.
Also Kunst erzeugt eine Funktion, die immer dasselbe Ergebnis liefert.
Egal wie viele Argumente sie kriegt und was der Wert ist.
Also wenn ich diese Funktion, die bei Kunst von sechs
erzeugt wird, aufrufe mit den Argumenten eins und zwei Kreditsechs
Die Funktion Always, das ist ein Prädikat,
was immer wahr ist, egal was ihr als Argument habt.
Und das können die zum Beispiel erzeugen mit Constitu.
Ja. Wenn ich jetzt aufrufe, Always und übergebe,
wie auch immer, das Resultat ist True.
Die Funktion Never könnten beschreiben, indem wir die Constfunktionen verwenden mit
dem Argument False. Dann ganz nützlich sind Conjoin und Disjoin,
das sind Funktionen, die eine Funktionskomposition von Prädikaten machen.
Und zwar als großes logisches und oder als großes logisches
Also wenn ich ein Prädikat haben möchte,
das immer wahr ist für beliebig viele Basisprädikate,
dann kann ich zum Beispiel sagen,
ich mache einen Curry Air von Größer 3 und von kleiner 10 und habe damit,
wenn ich da von einem Conjoin mache,
ein großes und dieser beiden Prädikate, habe ich ein Prädikat,
was testet, ob die Werte zwischen 3 und 10 liegen.
Ein anderes Beispiel für eine konstante Funktion, wenn ich zählen will,
ich nehme die Liste und schicke da die Funktion drüber, die alles auf eins abbildet.
Da kriegt die eben eine Liste von Einsen. Ja,
und wenn ich jetzt die Länge der Liste zählen will,
kann ich sagen, Fultel, mit der Plus-Funktion,
Startwert 0, schicke die Funktion,
die alles auf eins abbildet, über die Liste,
ja, und ich habe eine weitere Variante für die Längsfunktion.
In Recket haben wir noch die Komplimentfunktion. Wir haben eine Funktion,
die in einer Liste ein bestimmtes Element findet, Find,
F, Predicted, ja, die uns die Restliste gibt,
wir mindestens ein Element der Liste,
findet MF-Prädikat,
Wir haben den Al-Quantor, das ist das Endmap.
Schick dir ein Prädikat über alle Elemente der Liste und stelle fest,
ob jedes Element der Liste dieses Prädikat erfüllt.
Wir haben ein Existenzquantor praktisch drin. Gibt es mindestens ein Element,
was die Werte erzeugt? Das können wir mit Org-Map machen.
Okay. Eine Viertelstunde und mal grafische Bedienoberflächen im Racket anzuschauen
und zu sehen, wie wir da Funktionen höherer Ordnung brauchen.
Also praktisch alle bedienen nur bei Flächen, die man in Reckett verwenden kann.
Die gehen davon aus, dass man das, was geschehen soll,
wenn man einen Knopf drückt oder auf irgendein Element auf der Berlin-Oberfläche klickt,
dass das beschrieben wird durch Ereignisroutinen, die wir übergeben.
Ja, und das sind Funktionen höhere Ordnung, die wir dann haben.
Ja, wir gucken uns mal das Allersimpelste an.
Das ist das Typ-Speck-Gui-Racket. Damit kann man nicht viel mehr
machen. Also Dinge, wo man was eingeben kann.
Ja, und Knöpfe, wo man drücken kann.
Und dann passiert was. Aber damit kann man immerhin
schon mal interaktiv werden. Es gibt vornehmere Feindere,
die verwenden wir in unserem Praktikum zur funktionalen Programmierung
Also auf Mausbewegung reagieren, auf Knopfdruck reagieren, Texteineingabe lesen.
Ja. Ja, aber kennen Sie es, dass Sie
dann Actionhändler-Eventhälter, Händler haben, die auf Ereignis reagieren können.
Und dann nehmen wir in Recket schlichtweg Funktionen höhere Ordnung und
wir geben denen direkt die Funktionen, die aufgerufen werden sollen,
wenn das geschehen ist.
Ja, das sind Callback-Functions. So, wie können wir
mal loslegen? Wir sagen, Require aus dem How-to-Design-Programms,
more do it, das Gui-Modul.
Und dann erzeugen wir ein Fenster und da sagen wir
Create Window und dann kommt erstmal der lange Schlange von Klammern
Zwar übergeben wir hier Listen von Gouy-Items und für jede Zeile
des Graphical-User-Interfaces übergeben wir eine Liste.
Also eine innere Liste mit drei Gouy-Items und da drunter
eine andere Liste für die nächste Zeile und so weiter.
Okay, um das Fenster anzuzeigen, sagen wir einfach Show
Window und übergeben das Window-Objekt, was wir jetzt zurückbekommen haben.
Und wenn wir es nicht mehr sehen wollen, sagen wir halt Window.
Gut, jetzt wollen wir da was reintun,
zum Beispiel ein Knöpfchen, das man drücken kann.
Und das kriegen wir einfach mit Make-Button
Kriegen wir, geben wir einen, begeben wir einen String.
Und dieses Event Bulien, das muss jetzt eine Funktion,
eine Funktion sein, die ein Prädikat ist.
Also zurückgibt True oder Fals, ne?
Event Bulien. Das ist das, was die Funktion,
die aufgerufen wird, wenn jemand den Knopf drückt.
Ja. So. Und deshalb heißt die so komisch,
Event Buillien, weil diese Funktion als Argument
ein Ereignis übergeben bekommt von dem Modul
Und an dem Ereignis können wir erkennen, was da passiert ist.
Und da gucken wir drauf, um dann zu entscheiden,
was wir tun sollen. Wenn wir die Funktion ausführen.
Mit Make Message können wir Text in das Google reinschreiben, ja?
Mit Drohmessisch, Message wird es auch angezeigt, ja?
Wir können mit Text-Content aus einem Textfeld,
wo die Benutzer was reinschreiben, können den Text zurücklesen,
ja? Und dann haben wir noch Choices,
das ist so ein Auswahlmenü, da erfahren wir,
welches zurückgekommen ist und dann können wir eben auch darauf reagieren,
Der Benutzer wird hier irgendetwas aus. Und wenn wir dann wissen wollen,
was er ausgewählt hat, dann rufen wir auf Choice Index,
dann erfahren wir zum Beispiel die Zahl 3, dann wissen wir,
dass er aus dem Menü das dritte Element ausgewählt hat.
Beginnt bei Null übrigens. So, wir machen mal
als erstes ein Fenster, da gibt es nichts anderes,
als man Quitt drückt, um es wieder wegzukriegen.
Und so, Window zeigt es wieder an. Also das
erzeugen wir jetzt mal als Beispiel. Wir rufen mit,
macht Triquaya, holen wir dieses Gui-Modul und dann erzeugen wir ein Fenster.
Das ist als Variable Vet zurück mit Create Window
Dann kommt die Liste von den Zeilen und wir haben nur
eine Zeile, das ist diese Liste,
wo wir Mac Button aufrufen und der hat dieses Quitt.
Und was tun wir, wenn jemand Quitt drückt?
Dann rufen wir die Funktionen auf High Window und ignorieren,
was da als Eventereignis übergeben wurde.
Und um das Fenster anzuzeigen, sagen wir Showhündo.
Um jetzt ein bisschen vornehmeres Guit zu kriegen,
erzeugen wir ein Fenster mit Eingabefeldanzeige
Also hier, Please and the your neben soll da stehen,
hier können wir jetzt was reinschreiben und dann sagen wir noch Hello World,
ja und es gibt auch noch einen,
okay und ein Quitt, ein bisschen mehr Action im Fenster.
So, den Text 1, Mick Text,
Please and the your name, ja, und Message ist ein Text,
den wir einfach anzeigen mit Stringle Pentello World, ja,
und dahinter noch 33 Leerzeichen,
damit das ein bisschen weiter hinten steht.
Okay. Ein
etwas vornehmeres Beispiel
Eine selbst definierte Uhr, wo wir hier einfach mal die
Zeile laufen lassen können und das klickt und tickt und so weiter.
Wir können in Recket auch simulierte Weltenanzeigen, wo wir einen Rahmen haben,
da läuft so eine Uhr und es passiert immer was.
Und damit können wir Spiele machen. Okay, was brauchen
wir für Spiele? Erstmal regelmäßig in Abhängigkeit von der Simulationsur
zu sagen, was jetzt angezeigt werden soll von dieser Welt.
Apropos Uhr, aha. Und dann die simulierte Welt
grafisch darstellen und bei jeder Zustandsänderung das Ganze neu zu zeichnen
Und das ist auch wieder etwas, was wunderbar geht,
indem wir Funktionen schreiben, die Funktionen als Argumente kriegen,
weil dann die Übergibelfunktionen immer sagen, was geschehen soll.
Und dann noch auf interaktive Eingabe, Maus, Tastatur und so reagieren.
Dafür haben wir ein tolles Tippspekt, das heißt Universe Record.
Und da werden alle Zustandsänderungen über Ereignisroutinen modelliert, Callback Functions.
So der allereinfachste Fall, den schaffen wir heute wahrscheinlich nochmal.
Es gibt die Funktion Animate, ja, die kriegt einen Parameter.
Und das ist ein Create Image.
Ja, das ist einfach
eine Funktion
Die wird aufgerufen und erfährt den Zustand der Uhr.
Die Uhr, die tickt 1, 2, 3,
4, 5, 6, läuft die Zeit.
Und unsere Funktion Animate, die wir übergeben, die kriegt
einfach die Uhrzeit mit. Und da muss sie wissen,
was zu dieser Uhrzeit gezeichnet werden soll als Zustand der Welt.
Also erzeugten kennen wir es, die Simulationsir wird gestartet,
die tickt 28 mal pro Sekunde.
Und bei jedem Tick wird der Zeitpunkt um 1 erhöht.
Und dann wird Create Image aufgerufen, das nächste Bild gezeichnet.
Zum Beispiel, wir rufen auf, How to Design Image,
How to Design Programms, das Image-Paket, um Bilder zeichnen zu können.
Und wir brauchen noch das Universe-Paket, damit wir Welten schaffen können.
Discreate UFO ist ein Beispiel direkt aus dem Halbdeckst.
Das habe ich nicht selbst geschrieben. Create you for seen kriegt,
was macht es? Es soll einfach ein landendes UFO hier zeigen.
Ein Bild, wo hier ein UFO ist und je nach
Uhrzeit sitzt hier immer tiefer und landet hier irgendwann.
So. Um jetzt das Ganze zu zeichnen, wird ein Rechtseck gezeichnet,
das ist hundertmal hundert und das ist durchgefärbt weiß,
steht hier,
das ist dieser weise Hintergrund
Dann ist es 50 Einheiten breit und hat eine Höhe, die variiert.
Also dieser Parameter Höhe soll uns sagen, wo das UFO hier reingezeichnet werden soll. So,
und das UFO wird drüber gemalt mit Underlay allein, zentriert,
zeichnen wir hier erstmal einen Kreis, der ist grün,
hier dieser kleine grüne Klecks und da drüber dieser durchgehende Strich,
40 mal 4, das ist das, was hier da durchgeht, ja.
So, und wenn wir das haben,
also eine unserer Funktionen wie Create Create Youforts ziehen bei einer gegebenen Höhe,
fertig, ja, dann müssen wir hier unten jetzt nur noch sagen,
Andy Made und als Parameter in Abhängigkeit von dem,
was von der Uhrzeit geschehen soll, übergeben wir dieses,
diese Funktion hier, ja.
So, jetzt läuft die Uhr und bei jedem Aufruf,
bei jedem Klicken der Uhr, wird U-Fuzzi aufgerufen mit einer
anderen Höhe und auf diese Weise wird dessen vor sich bewegen im Bild, ja.
Das ist das, was man im Rack jetzt sieht,
wenn man genau diesen Tod ausführt,
Ja, und jetzt zeichnen wir mal eine richtige Uhr als Beispiel.
Also, wir müssen die Uhrzeit feststellen,
da prectet die schöne Funktion Seconds to date.
Das hat so und so viele Sekunden sind verlaufen,
da machen wir einen Datumsrecord raus. Und dann können wir
abfragen, welche Stunde wir geschlagen hat und so weiter.
Zeichnen das Ziffernblatt. Und unsere Uhr würde mit Next Clockworld jetzt aufgerufen werden,
also das, was ist mir die nächste Uhr mit Ziffer
im Lab zeigt und kriegt jetzt auch als World übergeben,
Die Uhrzeit, die wir abgefragen haben.
Karin Zeckens ist ein Aufruf, der in Reckett einfach
in Müllisekunden abfragt, wie weit ist die interne Rechneruhr?
Und Seconds to Date erzeugt daraus das Datumsreckott. Der Parameter,
also die nächste Welt, die gezeichnet werden sollen,
ist dann einfach das Datum in einem vernünftigen Format.
Um jetzt das Ziffernblatt zu haben,
habe ich einfach mit Insert Image ein Bild von einem Ziffernblatt
genommen und das hier an die Variante Clockface gebunden.
Der Kenntnis auf den Bezeichnen, fragen wir mit Imagewitz,
wie bereit ist dieses Bild eigentlich,
bringen wir einen Pixel in die Breite.
Und dann erzeugen wir damit, wissen wir, wie breit das Bild sein muss.
Die Höhe fragen wir mit Imageshade, wie hoch ist.
Dieses Bild, was wir da eingesetzt haben. Und empty
zehn erzeugen wir zunächst eine leere Szene mit Place Image,
bauen wir in dieser leere Szene, indem wir einfach das Bild,
was wir jetzt gebaut haben, rein.
Ja, und da müssen wir noch die Stunden,
Minuten,
Sekundenzeiger reinsetzen
Das überlasse ich mal ihm,
dass sie sich an ihre Geometriekenntnisse erinnern, wie rechne ich aus,
in welche Richtung eine gerade vom Mittelpunkt des Feldes gezeichnet werden muss,
um Stundenzeiger, Minute, Zeiger, Sekundenzeiger, zu zeichnen,
wenn ich weiß, wie spät es ist.
Und diese Funktion, Stundenzeiger, Minutenzeiger,
also Kundenzeiger kriegen einfach diese Kunden übergeben und den kennen wir es,
worauf es gezeichnet werden soll.
Ja, treuer Klock wird. Zeichnet also zu einem gegebenen Zeitpunkt
Das Bild mit Imageshade, Image mit, die M,
die Zähne zeichnet die drei Zeiger und zeigt auch noch im Klartext angespitzt ist.
Hier kommen die drei Zeiger über die Bilder gelegt und das wird
direkt auf den mit Place-Image, auf dem Ziffernblatt abgelegt.
Wenn ich jetzt Andy mit Klopp aufrufe, muss ich
nur dies teuer Klopp-Wald übergeben und dann sehe ich hier meine Uhr tippen.
Wenn wir unsere Welt mit Interaktionen haben wollen,
hat es doch Zeit für ein Big Bang-Klasse
Da haben wir die wunderschöne Funktion. Big Bang, die erschöpft,
die schafft aus dem Urknall eine neue Welt, ja.
Also was macht sie initialisiert, eine simulierte Welt? Erzeugt ein Fenster,
in dem wir immer den Zustand der Welt sehen, ja.
Und dann startet sie die Simulationsuhr und wir können sie parametrisieren mit Ereignisroutinen,
die sagen, was geschehen soll, ja.
Also, wir übergeben den für einen Tick eine Funktion,
die für jedes Urereignis aufgerufen wird.
Wir übergeben mit Tutor eine Funktion,
also die verändert den Zustand der Welt,
Tutor zeichnet die Welt
Und stopp, wenn, sagt,
wenn das Weltenende gekommen ist. Ja, und Onki,
das sind Funktionen, die den Zustand der Welt verändern,
wenn die Benutzer irgendwas hier am Rechner tun.
Für Tastaturereignisse, ja, oder wenn die Maus bewegt wird und so weiter.
Ja, und dann müssen wir einfach nur sagen,
Big Bang übergeben die Breite des Bildes die Höhe und dann
die erste Welt und der Staatszustand der Uhr.
So, also für Onki,
Tutra und Ki müssen wir noch was tun,
die müssen wir anmelden und registrieren,
diese Ereignishändler und dann sind wir fertig,
Also wir erzeugen hier mal unsere Uhr direkt als Universum.
Ja. Wir erzeugen mit Forstclock eine erste Welt.
Dann geben wir dem Mütze doch einen Hinweis,
dass man Q tippen kann oder ein Escape, um aufzuhören.
Wir sagen, der First Bank, Big Ben, First Clock.
Bei Onzyk übergeben oder Next Clockwort. Bei Tutrain übergeben wir
ein Drog-Clockwort. Und da haben wir Groß das Ganze ist.
Wir sagen, wenn oft gehört werden soll,
indem wir bei Stop,
wenn eine Funktion übergeben
Wir sagen für On Key, was geschehen soll,
wenn da jemand eine Taste drückt, ja. Und Rekord,
das ist noch ein Parameter, mit dem wir sagen können,
ob das dann Protokoll geführt werden soll, während der Simulation.
Und das Norme ist das, was angezeigt wird im Fenster, ja.
Okay. Die nächsten Beispiele werden wir dann nächste Woche uns
anschauen. Also was sie mitnehmen sollen von heute ist,
ist erstens das Imagepaket, mit dem wir Bilder machen können.
Das brauchen wir für das Aufgabenblatt, was heute übergeben wird.
Und dass wir interaktive Spiele und Indiges machen können,
indem wir Ereignishändler übergeben, indem wir einfach Funktionen übergeben.
Und genauer werden wir uns das im Laufe des Semesters
noch anschauen.
Danke für die Aufmerksamkeit