nimmt und sie auf beliebige Anzahl von Listen anwenden kann, ja.
Idee ist, die Listen, die als Argument übergeben werden,
werden ein paarweise verknüpft nacheinander durch die Funktion und die Stelligkeit
der Funktion muss zur Zahl der Argumente.
Was denn, da hatten wir das letzte Mal viele Beispiele gesehen,
wie Elemente einer Liste um eins erhöhen und so weiter.
Also merken Sie sich bitte für Map,
das ist die eine Funktion, wo wir Resultat,
eine Liste bekommen, die genauso viele Elemente hat,
wie die Liste, die wir als Argument übergeben haben.
Filter erwartet ein Prädikat als Argument. Deshalb schreibe ich den Namen
mal mit einem Fragezeichen und kann auch beliebig viele Listen übernehmen.
Und das wird uns eine Resultatliste geben, die gefiltert ist,
hinsichtlich der Argumente. Also alle Elemente,
die das Prädikat erfüllen werden im Resultat stehen.
Hier kommt also eine kürzere Liste zurück
Dritte wichtige Fall, über den ich heute sprechen möchte,
ist das Falten von Listen,
wenn wir Listen als Argument haben und Funktionen übergeben,
die die zu einem einzigen Wert kollabieren.
Ja, und das Falten kommt in mehreren Ausprägungen und deshalb
hatte ich an der Stelle aufgehört,
weil ein bisschen Zeit brauchen, darüber zu reden.
Also Elemente der Folge von Werten, das ist das Problem,
sollen ein paarweise mit demselben Opperator verknüpft werden.
Und dann wollen wir einen einzigen Resultatwert bekommen. Reduzieren der
Liste mit der Faltungsfunktion, die gibt es in zwei Ausprägungen,
Fuld L, Fuld A,
je nachdem wie begonnen wird
Gestartet wird mit einem neutralen Element,
das wir auch als Wert übergeben müssen,
das auch das Resultat, wenn die Liste leer ist.
Da kommt einfach dieses neutrale Element, was wir übergeben haben, zurück.
Nur wenn die Operationen aus Addieren besteht und so,
empfiehlt es sich mit einem neutralen Element null zu beginnen.
Beim Multiplizieren ist die Null nicht so praktisch. Da würden wir
lieber mit der Eins anfangen. Und damit können wir dann
Werte reduzieren, das bekommen. Also die drei Argumente,
die wir brauchen, einmal den Opperator übergeben als Funktion,
passend in der Stelligkeit zur Zahl der Listen, Startwert.
Und dann eben eine oder mehrere zu faltende Listen und das
wird von links nach rechts verknüpft,
wenn wir Fuld Allen nehmen, ja.
Der übergebene Operator, die Funktion FS zweistellig.
Und verknüpft eben immer das bisherige Resultat mit dem,
was als nächstes in der Liste kommt.
Ja. Okay. Zum Beispiel,
wenn wir simpel die Summe aller Zahlen in der Liste haben wollen,
würden wir es so machen. Fuld All übergeben das Plus,
neutrales Element null und dann hier die Liste mit den Zahlen.
Und dann kommt erwartungsgemäß 0 plus 1 plus 2 plus 3
, also 6 raus.
Oder das Produkt aller Zahlen,
dann mit dem Startelement
Zentralen Element eins, ja. Eins, mal eins,
mal zwei, mal drei. Minimum-Funktionen, ja. Und genau wie beim Map,
wir brauchen nicht nur die einen,
sind nicht beschränkt auf die eingebauten Standardfunktionen,
wir können unsere eigenen übergeben oder eben genauso wie sie in Java,
vielleicht einfach mal Ex und Top, eine anonyme Klasse schreiben,
wenn sie einen Iterator brauchen,
können sie in Recket dann hingehen und hier einfach einen Lamdeausdruck,
also die Closure als anonyme Funktion hier reinschreiben.
Und warum kommt hier drei raus
Ja. Das war zuerst dieser Lambda-Ausdruck und merkt über die Liste ein, zwei Seiten,
wenn jetzt, glaube ich, jedes Mal eins müsste.
Das kommt jedes Jahr 1-1-1-1 raus.
Und die Zahlen werden auch, wenn jetzt das Pragma da eins ist.
Ja, genau. Also das soll ihnen den ersten zeigen,
dass wir natürlich Funktionen höherer Ordnung nach Lust und Laune,
wo es Sinn macht, schachteln können, ja.
Und das wird ein ganz typischer Programmierstil für sie in Zukunft
sein, statt Rekursion im Wesentlichen verschachtelte Funktionen höherer Ordnung.
Also hier haben wir die Map-Funktion und die macht nichts anderes
als aus unserer gegebenen Funktion hier eine Strichliste von einzeln.
Für jedes Element kriegen wir eine Eins zurück,
wie Benedikt das eben gut erklärt hat
Wenn wir jetzt praktisch dadurch gezählt haben,
dass wir das hier auf eine Strichliste abgebildet haben,
dann könnten wir die Länge einer Liste tatsächlich so verklausuliert programmieren,
dass wir sagen,
bilde alles auf eins ab und dann addiere die eins nur,
damit hast du wieder Enge der Liste gezählt.
Gut. Ja, einfach, um zu sehen,
wie das fold funktioniert, gucken wir uns da tatsächlich mal den Code an.
Also my fold. Ja, wenn die Liste leer ist,
wie immer hören wir auf und geben den Startwert zurück.
Ja, null access. Und in allen anderen Fällen,
in diesem Fall hier rekursiv, Falten wir den Rest der Liste,
indem wir die Funktion auch F anwenden, auf das erste Element der Liste,
mit demselben Startwert oder neutralen Element und auf den Rest der Liste.
Also so wird diese zweistellige Funktion nacheinander jeweils auf die Werte angewendet.
K, der Liste, K, der Liste,
K, der Liste, was wir da haben.
Und mit dem Resultat verknüpft, was wir hier übergeben.
Oh, schade, steht schon auf der Folie.
Ich wollte sie gerade fragen.
Jetzt mal drüber nachdenken
Ob das Ganze entre-kursiv ist oder nicht. Woran sehen wir
denn, dass es entre-kursiv ist? Noch jemand,
ein Angebot? Nee, dann. Erstmal ist es sozusagen
der äußerste Funktionsabdruck, der macht bei der Rekrusive aus.
Deswegen ist das das Letzte, was auch berechnet werden muss.
Genau. Also wir erkennen eine entrekursive Funktion daran,
dass das Resultat mit nichts und absolut nichts mehr verknüpft werden muss.
Und hier beginnt der Arztzweig direkt mit dem rekursiven Aufruf und
damit geschieht alle Berechnung auf Akkumulatorposition. Also wir sehen,
dass sie praktisch Das Stadt, der neutrale Element,
wie einen Akkumulator genutzt wird in dem Fall.
Ja, und das ist tatsächlich entrekursiv ist. Wir können ja sehen,
wenn wir die Trace-Funktionen einschalten und dann sehen, was passiert.
Hier füllt sich so langsam das Startelement. Zu Anfang haben wir
das leere Element als neutrales Element und dann nach und nach wird der Start wert,
mit dem wir den Rest verknüpfen, eben immer länger,
bis wir fertig sind.
Und Was hier auch auffällt, darauf bitte ich zu achten,
das ist ganz, ganz typisch für entrekursive Funktionen.
Unsere Liste ist hier umgedreht. Ja, im Wesentlichen haben
wir mit dem Falten von links die Reverse-Funktion neu programmiert.
Weshalb wird sie umgedreht, weil das, was vorne steht,
im Akkumulator ist und wir pappen mit Cons immer,
was ich davor, also haben wir es am Ende umgedreht.
Das ist genau das, was wir angewendet hatten als Veryverse,
entrekursiv gemacht haben. Also wenn sie mit Fold L arbeiten,
immer dran denken, hinterher das Resultat mit Reverse
wieder richtig rumdrehen
Ja, okay. Dann, die zweite Variante fullt er.
Die ist linear rekursiv, also vom Aufwand her deutlich größer,
aber wir bekommen das Ergebnis gleich richtig rum zurück.
Also wenn wir uns anschauen, was fullt,
er macht die zweite Ausprägung, dann sehen wir,
dass hier die Funktion angewendet wird,
zuerst auf den Seed und dann auf das Resultat und
damit andersrum verknüpft wird
Und wir sind also praktisch bei Kommutativen Funktionen ist es wichtig,
dass wir uns klar machen, ob wir mit Fuld Air
oder Fuld L arbeiten. Okay, gut. Ja,
also wenn und die Funktionen, die wir übergeben,
nicht kommutativ sind, dann sollten wir darauf achten,
ob wir von links oder von rechts falten.
Und selbst wenn man sich das gut überlegt hat, kann man manchmal trotzdem Denkfehler machen.
Also das ist auch experimentell klären, ob man richtig gedacht hat.
So, wenn wir sehr lange Listen haben,
dann ist die allgemein rekursive Fassung Meistens ungünstig,
weil sie den unnötig tiefen Aufrufkeller aufbaut.
Ja, andere Funktionen höherer Ordnung, die ebenfalls nützlich sein können,
möchte ich jetzt noch so ein bisschen streifend beleuchten.
Aber für die Klausur merken sich bitte auf jeden Fall Map-Filter und Fult-L. Ja,
also die drei Fälle müssen für die drei Fälle,
müssen sie wirklich am Ende des Semesters Standardfunktionen höherer Ordnung griffbereit haben und sie sofort wissen,
was nehme ich.
Ich habe den Fall Liste abbilden,
Liste bleibt gleich lang
Holla, das ist das Map. Ich habe den Fall,
die Liste soll durchsucht werden nach etwas,
sie wird kürzer, dann nehme ich das Vierter,
der zweite wichtige Fall oder ich will als Ergebnis gar keine Liste,
sondern einen Wert, dann falte ich von links oder rechts,
wie es beliebt, ja.
Was wir jetzt an Funktionen uns noch anschauen wollen,
das sind Hilfsfunktionen, die man für die Parameter unserer Funktionen höherer Ordnung
nimmt und da ist zum Beispiel ganz nützlich das Bildlist,
das du so aus der Klasse der Generatorfunktionen,
wir brauchen eine Liste mit bestimmten Eigenschaften und nette Art,
das zu machen, ist das folgende.
Ich möchte eine Liste der Länge 5 haben und die Elemente der Liste sollen,
sagen wir, die Quadrate der natürlichen Zahlen sein.
Ja, dann kann ich Bildlist verwenden,
übergebt die Länge der Liste und dann eine Funktion,
die nacheinander die natürlichen Zahlen, 1, 2, 3,
4, 5 als Argument erhält und uns dann die Elemente aufzählt,
indem sie die Funktionen jeweils oft den Zähler anwendet.
Also Bildlist ist praktisch das, was sie nehmen würden,
in Java Oder das Äquivalent zu dem, was sie machen würden,
wenn sie eine Vorschleife mit einem Zähler haben und eine Liste aufbauen wollen.
Dann ist das sehr nützlich. Und wenn wir einfach wiederholen wollen,
bisher haben wir es hier in der Vorlesung ja immer gemacht,
um mal ein bisschen Rekursion zu üben und da Training zu kriegen und fit zu werden,
indem wir wiederholen, dadurch gemacht haben,
dass wir dieselbe Funktion rekursiv immer wieder aufgerufen haben.
Eine tolle Art, eine Schleife zu machen. Ja,
das braucht man Schleifen. Na gut, manchmal sind Schleifen einfach besser lesbar.
Und für solche Fälle können wir das deutlich machen,
dass sie mit der Rekursion eine Iteration meinen,
wenn wir eine Funktion höherer Ordnung verwenden, die schon Iterate heißt, iterieren.
Ja. Also wenn wir jetzt mal die Quadrate erzeugen wollen,
indem wir immer wieder die Funktion,
eine bestimmte Funktion aus vorheriger Reultat anwenden wollen,
dann könnten wir Iterate verwenden.
Iterate als Funktion höherer Ordnung, schon ein bisschen fortgeschritten,
die braucht gleich zwei Funktionen als Parameter.
Gegen Ende des Semesters wird uns das nichts mehr ausmachen,
dann übergeben wir auch mal locker und lässig
acht Funktionen als Parameter
Aber jetzt sind wir schon bei Stufe 2. Also, wir übergeben zwei Funktionen.
Erstmal die Funktion, die immer wieder angewendet werden soll.
Und die zweite Funktion, die uns sagt,
wenn wir fertig sind, Ende Prädikat, ja, und ein Stadtwert.
Und damit könnten wir jetzt die Liste aller Zweierpotenzen bis zu einem bestimmten Wert aufstellen,
indem wir sagen, iteriere, iterate, nimm die Funktion,
die den vorherigen Wert verdoppelt, ja, und dann höre doch bitte auf,
Wenn das Resultat größer ist, als 64, ein Ende-Test.
Ja, und Anfang tun wir. Mit der 2. Ja
, ich merke gerade, die berechnet die Vielfachen.
Und nicht die Zweierpotenzen. Gut, oder wir wollen die,
ja. Ist dieses Israels-Entrikot. Die Antwort ist selbst.
Oder ist das direkt irgendwie... Also das,
so wie es gemacht ist, ist es nicht entrepursiv,
falls sie die Implementation, soweit ich gesehen habe,
das nicht, ja, Einfach mit Cons zusammenbaut.
Bei den Standardfunktionen ist die einzige Art, sich anzugucken,
ob es Iterate ist oder nicht, indem man aufs Entrekursiv ist oder nicht,
indem man versucht, das zu tresen.
Nur Standardfunktionen lassen sich meistens nicht tresen. Da muss man
einhüllende Funktionen schreiben, die das testen. Okay,
zum Beispiel mit Itterate die Liste der natürlichen Zahlen erzeugen,
würden wir einfach sagen, übergebe die Funktion Adira 1,
etwan, die müssen wir uns als Stützfunktion übrigens selber schreiben,
die ist nicht fertig.
Und als Endetest, größer gleich, N. Und dann sind wir
fertig und schon haben wir hier unsere natürlichen Zahlen erzeugt.
Oder wenn wir fürs Einrücken so und so viel Spaces erzeugen wollen,
könnten wir es so machen. Von links Falten,
mit einem Stringer pennt und dann unsere natürlichen Zahlen,
die wir eben als oft zählende Liste erzeugt haben, mit abbilden.
Und ja, also wenn wir uns das mal anschauen,
im Zusammenhang, dies Fold Fuld,
können wir in die Klasse der Funktionen Reduktoren kollabieren,
auf einen Wert treiben tut.
Und Itterate im Gegenzug ist ein Generator,
mit dem können wir aus einem Startwert Strukturen aufbauen.
Und häufig brauchen wir im Programm beide Sachen einfach komplementär.
Generatoren und Reduktoren, über die Kontrollabläufe,
über unsere Daten aufzubauen. So, hier haben wir,
es ist ein Nachbau von Itterate und da kommen wir
auf die Frage
nochmal zurückkommen
Also wie gesagt, wir haben,
wenn wir selber implementieren wollten, müssten wir eine Funktion schreiben,
die Funktionen übergibt, ein Endeprädikat, Startwert.
Und dann würden wir hier eine lokale Funktion Iteration definieren,
als Einhüllendes fürs Ganzes,
die nimmt als Parameter jetzt nur noch das bisherige Resultat und Zähler,
der einem sagt,
bei welchem Thermen wir sind,
rechnet einen neuen Therm aus dem bisherigen alten Wert
Ja. Und dann wird, wenn die Bedingung erfüllt ist, das Resultat zurückgegeben.
Und andernfalls Iteration, rekursiv aufgerufen. Und wir sehen,
das Resultat wird hier auf dem Akkumulator zusammengebeutet, bekommen den neuen Wert zurück.
Also das ist in der Tat endrekursiv Iteration, wird mit nichts mehr verknüpft.
Deshalb müssen wir es am Ende, wie vorhin erwähnt,
umdrehen, weil die endrekursiven Funktionen die Listen produzieren,
es meistens verkehrt drum machen. So, nochmal Frage,
weshalb wird für das Einführen der lokalen Funktion Iteration hier weggebraucht.
Jemand anders weiß bestimmt auch. Gut, frag dich mal anders.
Danke. Was würde passieren, wenn ich da Noulette geschrieben hätte?
Ja. Ja, nicht sichergestellt, dass es richtig um ausgewertet wird.
Fast. Also es wäre nicht sichergestellt, dass die Funktionen bekannt ist,
wenn sie hier drin rekursiv aufgerufen wird. Ja, Lett ist so,
dass wenn in der Argumentliste Bezug genommen wird auf das,
was man gerade definiert, dann haben wir ein Problem.
Wir können es, wenn es nur eine Schicht ist,
machen mit Lettstern, dann werden zwei,
wenn wir zum Beispiel eine Variable in Abhängigkeit von einer anderen einführen wollen,
dann würde ein Lettstern reichen.
Aber hier müssen wir in derselben Definition,
wo wir gerade bei sind, den Namen verwenden.
Und das ist So, von der Definitionslage her,
dass selbst letztes Stern nicht mehr reicht.
Der Stern hilft mir bei der zweiten Variable. Okay,
und dann können wir eben in dem Fall sagen,
mit Leckdreck, ich bin dabei, etwas rekursiv zu definieren,
also was immer hier schon definiert ist, betrachte es als definiert im Rumpf.
Deshalb, da Leckdreck. Also wir haben das normale Let für lokale Variable,
alle werden parallel gebunden, wir haben das Leck Stern,
wo wir die Reihenfolge der definierten Variablen erzwingen und ich glaube,
das erste Beispiel, was wir hier hatten,
wo Wir etwas versuchen rekursiv zu definieren und dann müssen wir zum ultimativen Allett greifen.
Das ist das Lat-Track, das uns erlaubt, rekursiv zu definieren.
Ja, und dann am Ende mit Reverse umdrehen, ne?
Also, was sollten wir jetzt schon an Funktionen höherer Ordnung kennen für Sie?
Die Kontrollliste, Apply, Wendefunktionen auf eine Liste an,
so als wären es die Argumente flach ausgeschrieben.
Map-Filter, Falten in zwei Ausprägungen, Iterate für Wiederholung, ja.
Und Bitte an Till, da werden wir später noch drüber sprechen.
Und die ganzen Kontrakte für Zusicherung. Wenn wir Zusicherung konstruieren
, haben wir auch Funktionen höhere Ordnung übergeben.
Ich habe jetzt ein paar Seiten im Skript,
die ich aber jetzt nicht vertieft behandeln möchte.
Es ist einfach mehr für diejenigen, die Interesse haben,
auf weitere Spezialitäten, in funktionalen Programmierstilen reinzuschauen und sich das ein bisschen genauer anzugucken.
Es gibt ein schönes Paket Generator, Record Generator.
Und mit dem können wir Generatoren direkt als Objekte erzeugen
Einfach, indem man sagt,
ich erzeuge einen Generator ohne Argumente und der hat einen Body.
Und was diese Struktur tut, ist, sie läuft so lange
bis im Body der Funktion irgendwo der Aufruf von hier kommt.
Und dann gibt sie einen Wert zurück.
Und bei jedem neuen Aufruf bekommen wir den nächsten Wert.
Dann läuft dieser Body einfach weiter. Ja, es ist eine Endlosschleife,
die nach und nach immer wieder einen neuen Wert erzeugt.
So, und dann jedes Mal, wenn wir auf den Generator fragen,
gib mir einen Wert, dann kriegen wir den nächsten.
Er läuft bis zum nächsten Stoppschild, hielt.
Vorfahrt beachten
So, weshalb, auch nochmal eine Frage,
wer hat denn eine Idee, warum ich das hier nicht
intensiv behandle, in einer funktionalen Vorlesung, ja?
Weil man darf quasi zustandesabhängigkeiten,
hat denn der letzte Zustand und über das Wert zurückgegeben wurde,
wenn der erst bekannt vorausgesetzt wird, das nächste gilt.
Aber auf Zielweise könnte man die Super-Generatoren. Zum Beispiel,
genau, oder vom Terminal einlesen oder so,
was da zielt uns macht oder dir diese Generatoren machen,
die erzeugen uns einen endlosen Strom von Werten
Ja. Und sind hochgradig zustandsorientiert wie Benedikt eben gesagt hat.
Und das heißt, der Standardtest, ob Bezugstransparenz vorliegt,
der würde hier sofort sagen, halt stopp, nein,
wir sind, wenn wir mit Generatoren arbeiten,
nicht mehr in einer Bezugstransparenten Welt.
Wir bekommen jedes Mal einen neuen Wert.
Also der Zustand der Berechnung verändert sich. Deshalb überspringe ich
den Teil mal. Es bringt Spaß, damit zu programmieren.
Wir können das mit Vorkonstrukten und ähnlichem zusammenbringen
Und ich gehe mal trotzdem bis zu, es gibt ja
bis zu den Funktionen, wo wir weder in der Welt,
in der heilen Welt, der Bezugstransparenten Sprachen sind.
Und gucken wir uns eine zweite Art der Iterationen an.
Bitte Antel Till. Das Iterate, was wir eben besprochen hatten,
das erzeugt als Generator aus einem Startwert eine Liste von Werten.
Wenn wir aber nur iterieren wollen,
um einen Wert zu finden, der eine Bedingung erfüllt,
dann brauchen wir diese Zwischenwerte nicht und legen keinen Wert drauf,
die mit Konzellen zu einer Liste zusammengebaut werden,
kostet Rechenzeit,
kostet Speicher
Wir brauchen die Liste nicht, wir wollen nur den Endwert.
Und dann sagen wir, iteriere bis das und das erreicht ist.
Wir wollen nur den Endwert haben. Und das macht die
Funktion bitte an Till. Bis eine Bedingung erfüllt ist.
Können wir uns so selbst definieren? Wir wollen iterieren,
eine Funktion, solange bis das Ende, bis ein Endeprädikat erfüllt ist.
Und wir beginnen mit einem Startwert. Und schöne Endrepusive Formulierung
. Wenn das Endeprädikat sagt, alles okay,
geben wir den augenblicklichen Startwert zurück und andernfalls machen wir das Ganze wieder,
wenden aber an den jetzt aktuellen Startwert unsere Funktionen F an.
Zum Beispiel hat früheren Merkwurst-Versionen immer primär geklappt,
wenn wir einfach mal klingeln wollen oder so,
iteriere bis die Zahl erreicht ist,
indem die Funktion Berl aufgerufen wird und die macht nichts weiter,
als das Blöckchen läuten zu lassen.
Mein Meck ist leider stumm in der Hinsicht,
also da bringt es nicht viel.
Okay, aber probieren Sie es mal aus
Wir haben ja wieder eine Funktion, höhere Ordnung,
die zwei Funktionen als Argument braucht.
Das Ende, Prädikat und das, was zu tun ist.
Ich habe es dann einfach mal, das Klingeln visuell gemacht,
wo wir doch nichts hören würden. Ganz kleinen Ausblick,
damit sie, wenn sie etwas größere,
die größere Programmbeispiele im Skript lesen können wollen, wie funktionieren eigentlich Recket-Module?
Sie kennen natürlich Modellarisierung von Programmen aus Java,
wo sie mithilfe von Klassen und Paketen
ihre Programme strukturiert haben
Es hat seit langem, langem in der Informatik einen Streit gegeben,
ob man eigentlich Modularisierung machen soll, indem man Dinge schachtelt oder nicht.
Und der Vehement des Verfecht dagegen jegliche Art von Verschachtelung war Per Brinchansen,
also einer der Größen, die die Betriebssystemtheorien eingeführt haben.
Der hat gesagt, Nesting is for Birds.
Also Vögel bauen ihre Neste, aber Informatiker doch nicht.
Nesting heißt eben verschachtelt, das englische Wort. Und Racket ist es auch so,
dass wir nicht so ohne weiteres geschachtelte Module haben.
Die sind die Folgen in der Hinsicht der MIT-Linie, Nesting is for Birds.
Also wenn immer wir in unserem Racket-Modul schreiben,
slash lang und in irgendeine Sprache, langen Racket oder so,
dann haben wir damit automatisch schon mal ein Modul erzeugt.
Und das ist flach, es ist ungeschachtelt, ja.
Okay, wenn wir ein solches Modul schreiben,
passiert erstmal eine Nix, aber wenn die Moduldefinition ausgewertet wird,
dann wird die Syntax expandiert und das Modul übersetzt.
Moment, was soll Syntax expandieren, heißen? Racket hat
spezielle Sprachelemente, die feinen Syntax. Und Damit können wir
eigene Sprachelemente einführen. Und genau das passiert an der Stelle,
wenn wir eigene Sprachelemente eingeführt haben, wie zum Beispiel DevClass oder Weil oder ähnliches.
Wir haben unsere My iF, wir haben das vielleicht wirklich als
eigene Sprachelement als Special Form Expressions definiert über die feinen Syntax.
Dann wird es bei der Modulexpansion als erstes diese Syntax expandiert und
das Programm in ein anderes übersetzt, wo dann klar ist,
was diese Sprachelemente bedeuten sollen.
Also das geschieht sofort, taktische Expansion wird sofort gemacht,
aber es wird noch nicht ausgeführt, das Programm,
sondern es wird erst ausgeführt,
wenn das Modul mit einem Require in ein anderes Modul importiert wird.
Das garantiert aber auch, dass jedes Modul höchstens einmal ausgeführt wird,
wenn es mehrfach über Require importiert werden sollte.
Also wenn wir beispielsweise dasselbe Modul in vielen anderen brauchen,
wollen wir aber nicht, dass es jedes Mal gleich ausgeführt wird.
Also beim ersten Require wird es ausgeführt und dann nie wieder.
Was im Modul als Name eingeführt wird, ist nur lokal sichtbar,
aber können aber verlangen,
dass es woanders gesehen werden kann,
indem wir Provides reinschreiben
Okay. So typischerweise könnte ein Modul mit einem solchen Preamble beginnen,
ja, wo wir dann genau sagen, was wir haben wollen,
also wir schreiben mit provide rein, dass wir nach außen sichtbar werden,
machen wollen und da können wir ein bisschen willerisch sein,
ja, diese Provide-Spacks können verschieden sein.
Also wir können, wenn Namenskonflikten vorliegen, zum Beispiel sagen,
unter welchem Namen das dann der Außenwelt bekannt gegeben werden soll,
praktisch ein Alias, ne, in seinem lokalen Namen,
ihr können wir nach außen III taufen oder so.
Und wir können Wenn wir lokal Verbundtypen,
Structures angeführt haben, die nach außen auch sichtbar machen,
wir können alles, was wir von anderen Modulen bekommen haben,
nach außen weitergeben und sagen, also dieses Modul stellt auch das bereit,
dann muss es ein anderes, was uns verwenden will,
das nicht auch noch importieren.
Also dieses All from Module nehmen, ja,
oder wenn der Namenskonflikte sind, ja, exportieren wir alles,
aber nicht die Curry-Funktion oder so, ja.
All defined accept und, oder wir können es mit
einem Prefix exportieren
Um damit auf diese Weise dann auch Namenskonflikte zu lösen.
Ja, dann weiß man auch, wenn jemand anders etwas
verwendet anhand des Präfixes, aus welchen Moduls gekommen ist.
Und wir können verschiedene Stufen von Protect machen.
Da will ich jetzt nicht näher drauf eingehen,
aber beim Exportieren haben sie also eine ziemliche Kontrolle drüber,
was mit den exportierten Namen geschehen kann.
In der SE3-Bib sind einige Module drin, die ich für sie
definiert habe. Und unter anderem zum Beispiel das Tools-Modul,
wo ganze Menge Funktionen vordefiniert sind, die im Skript verwendet wären,
also dass sie nicht alle selbst schreiben können.
Und da ist zum Beispiel dieses Itha an Tildren und Manuel Itterite-Version
des Gen Attorage ein eigener Generator, ja,
oder eine Funktion, die vor allem Generator aufruft und vom Generator.
Werte nimmt ein bisschen Bedingung, erfüllt es. Okay,
was in ein solches Modul auch reinkommt, ist meistens,
dass man sagt, dass man auch dazu nehmen will und das sind diese Requires,
wächst damit importieren.
Provide macht es nach außen sichtbar, Require erlaubt uns
andere Module zu verwenden
Und Modul, was wir ja schon angesprochen hatten,
dass Schneeflocken zeichnen kann, rekursiv. Da habe ich zum Beispiel
dieses providere Infractives-Demo. Damit können wir eine Demo aufrufen,
die uns diese Schneeflöckchen da malt oder ein Fractil erzeugen,
die Snowflake-Line und sowas ist da alles drin.
Und gebraucht wird dafür die Imagebibliothek, wird das Simulationsuniversum gebraucht.
Und dann aus der SE3 gibt noch ein paar schöne Sachen wie MacOS-Mordul,
mit dem kann ich Sprachausgabe machen oder ist Tools-Mordul oder da eben ein simples Gui,
mit dem wir grafische Interaktionen machen können.
Und meine eigene Vektorgrafik. In den Top-Löffel-Einheiten,
da können wir eben auch Verträge für diese exportierten Objekte unterbringen,
was auch sehr nützlich ist. Also die Zusicherung für globale
Variable des Moduls, die in anderen Bereichen importiert werden.
Und das wäre dann ein Provide mit einem Contract.
Kommen wir auf das zurück, was wir am Anfang mal angesprochen haben. Sehr wenig hier,
nur damit sie dass Reckett nicht nur mit der Brille sehen,
ach ja, nettes Spielzeug, wir sitzen am Interpret,
ja, tippen unsere Funktionen ein, meistens crashen sie,
weil wir einen Fehler gemacht haben.
Und ich glaube, ich werde nie ein großes System damit schreiben.
Reckett bietet eine ganze Menge, dass man tatsächlich fast wie mit
der vollen Kommenlissumgebung damit inzwischen eine sehr solide Softwarearchitektur machen kann.
Und ein Beispiel dafür ist eben, dass Dr. Reckett selbst,
ne? Gut. Kommen wir mal dazu,
dass wir in unserer Softwarearchitektur nicht nur zusichern wollen,
dass es keine Typkonflikte gibt oder sowas,
sondern vielleicht sogar zusichern wollen, dass das Programm echt das tut,
was es tun soll
Versuchen, das dadurch zu erreichen,
dass wir jetzt nicht mit Testen lösen,
sondern mehr beim Programmentwurf uns schon überlegen,
wie wir die Funktionen schreiben, damit sie am Ende das auch tun.
Also beginnt mit einer Spezifikation und passend zur Spezifikation des Programmschreiben
. Bei funktionalen Sprachen liegt es als erste Gedankenübung nahe,
dass man tatsächlich mal anfängt, mit Funktionen im mathematischen Sinne schlichtweg Funktionen überzahlen.
Und ich hoffe, ihre Erinnerungen an Matheunterricht gehen noch so weit zurück,
dass sie wissen,
wie man so von Hand eine Funktion schreiben würde,
die Wurzel ziehen soll durch Intervallschachtelung oder so
Mit so einem simplen Beispiel an. Quadratwurzel einer Zahl.
Können wir ja so sagen, das ist doch die Zahl,
die werden wir sie quadrieren, die Zahl selbst ergibt.
Und außerdem wollen wir nur die Wurzeln haben, die positiv sind.
Also wir wollen zusichern, die Quadratwurzel für irgendeine Zahl ist größer Null.
Wir denken überhaupt nur an Wurzeln aus positiven Zahlen.
Übrigens habe ich hier nicht mit drauf geschrieben.
Und das Resultat soll die Bedingung erfüllen,
dass die Quadratwurzel quadriert die Zahl selbst ist
Oder aber, wenn wir schon wissen, naja,
mit der Rechengenauigkeit, das wird hapern.
Also, sagen wir, die Differenz zur eigentlichen Zahl
soll kleiner sein als eine Genauigkeit, die uns vorschwebt.
Gut, wenn wir jetzt mit so einer Spezifikation loslegen,
dann kommen wir das implementieren. Ich gehe eigentlich davon aus,
dass sie diese Sachen aus dem ersten Semester kennen.
An diesem Beispiel möchte ich Ihnen einfach gleich zeigen,
hinterrücks und wie man dieses Programm besonders elegant macht mit Funktionen
höherer Ordnung
Gut, aber wir haben jetzt die Spezifikation und wir wollen
es implementieren und wollen natürlich dann auch beweisen,
dass es die Implementation, die Spezifikation erfüllt.
Also unsere Anforderung, das Quadrat immer, die Wurzel immer größer gleich null,
die Differenz kleiner Apps und das Y soll auch größer als null sein.
Und an einem Beispiel würden wir sehen,
wenn wir mit zwei beginnen und wollen daraus die Wurzel haben
und sorgen uns mal einfach einen Schätzwert aus den Fingern,
zum Beispiel den da,
wenn wir den mit sich selbst multiplizieren,
sind wir noch
ein bisschen ab
Wir verändern Ihnen etwas, sind wir immer noch ab,
beim dritten Mal, aber kommt Sinn, Bedingung ist erfüllt.
Ich hoffe, Sie haben so schnell im Kopf mitrechnen können.
Okay. Und wie macht man es eleganter,
wenn wir einfach nur immer Zahlen probieren, probieren,
probieren, können wir ja lange probieren.
Wir brauchen etwas, was uns hilft, schnell zu probieren.
Und dafür haben Sie in der Mathematik die Nomerik-Anteile der Vorlesungen
und haben da bestimmt auch das Newton-Verfahren kennengelernt, zum Wurzelziehen.
Gucken wir uns das mal als ein Beispiel an,
wie machen wir dies Probieren, jetzt richtig schick mit Funktionen,
höhere Ordnung. Also Newton-Verfahren ist ja dafür da,
dass wir uns iterativ einer Zahl nähern, die Bedingungen erfüllen soll.
Und der Bessere. Und der geschätzte Näherungswert, den wir
als nächstes nehmen wollen, den bestimmen wir über die Ableitung der Funktion.
Also die Tangentensteigung gibt uns eine Idee, wie wir den nächsten besseren,
verbesserten Wert finden. Also wir gehen mal davon aus,
wir haben eine Bedingung, unser X,
davon wollen wir die Wurzeln haben und jetzt im konkreten Fall der Quadratzahl,
Nehmen wir an, wir haben irgendeinen Schätzwert hier,
den wir uns aus den Fingern gesaugt haben.
Der nächste bessere Nährungswert, den kriegen wir,
indem wir zu dem bisherigen Nährungswert X geteilt durch den Nährungswert addieren und das Ganze halbieren.
Dahinter steckt, wenn Sie sich vielleicht erinnern,
genau berechnetes Schnittpunkt Zertangente an die Funktion mit der X-Achse.
Aber glauben wir erstmal einfach so, die Formel hat sich bewährt.
Damit haben wir jetzt also einen neuen Wert.
Und das würden wir iterativ immer wieder machen,
solange aus jedem YN ein neues YN in plus 1 berechnen
Bis unsere Bedingung erfüllt ist. Und wir sehen schon,
also wenn wir einfach die Zahl selbst als ersten Verdacht,
falls zu ihrer eigenen Wurzeln machen,
dann kommen wir ziemlich schnell in die richtige Gegend.
So, und das jetzt, die Teilaufgaben,
die wir da haben, würden wir jetzt auf Funktionen höhere Ordnung ab.
Okay, die Iterationsregel verklausulieren wir in einer Funktion im Proof.
Verbessere Y. In jedem Schritt muss das alte Y genommen werden,
X durch Y dazu addiert und das Ganze halbiert.
Denn eine Abbruchbedingung ist es gut genug.
Dazu müssen wir testen das Y-Quadrat minus X kleiner Epsilon
Und als Y sein, werden wir großzügig und sagen,
also genauer brauchen wir es nicht.
0.003 reicht. Ja, und fürs Wiederholen.
Dann nehmen wir jetzt unser eben eingeführtes Itter-Antill, ja?
Um diese Iteration funktional zu schreiben. Also,
die Funktion. Mais Querroot fall X. Wir definieren hier,
dass Y 0.0, sind doch ein bisschen genauer geworden.
Fragen definieren eine zweite Funktion, good enough.
Jetzt reicht übrigens das letzte Stern, weil das alles
nebeneinander schön hier definiert wird.
Also, good enough,
das ist einfach
Der Absolutbetrag vom Quadrat von y minus X soll kleiner sein als Epsilon.
Und die Iterationsregel, unsere Funktionen im Beruf, naja,
nehme das alte Y, addiere X durch Y dazu,
teils durch zwei. Die zwei Funktionen. Und dann,
unsere Wiederholschleife, wir nehmen Mitte an Till, sagen,
wiederhole das im Beruf, immer wieder,
solange bis Good Enough sagt, gut genug.
Und dann sind wir da. Und wir beginnen mit
dem Wert X von hier oben selbst.
Und kriegen dann als Quadratwurzel von vier heraus 2 plus 1
durch eine ziemlich hohe Zahl
Wir sind dicht genug an zwei dran. Gut. Wenn
wir es mit innen exakt der Ausgabe machen, sehen wir,
dass wir tatsächlich mit der Wurzel wirklich sehr gut in die Gegend gekommen sind.
Wenn wir da jetzt noch Kontrakte in unsere Funktionen einbauen,
um sicherzustellen, dass sie wirklich erfüllt sind,
dann führe ich jetzt eine Hilfsfunktion Gott ein,
weil wir den Test einmal für den Vertrag brauchen und einmal für die Abbruchbedingung.
Also wir definieren hier X,
Wurzel X ist eine gute Wurzel zu X beigegebenen Epsilon,
wenn der absolut Betrag der Differenz kleiner ist als Epsilon.
Und dann können wir jetzt bis M. Square Root,
mysquare Root Variante 3, so definieren,
dass wir hier oben den Vertrag reinschreiben und sagen,
es ist eine Funktion.
Vertrag für eine Funktion, wo wir zusichern,
X ist größer gleich, das ist der größer Gleichkontrakt 0.
Und das Resultat ist eine Zahl und erfüllt die Bedingung Gott
Und hier steht, was diese Funktion mit Kontrakt tun soll,
das ist der Teil, den wir eben besprochen haben,
wo die beiden lokalen Funktionen good enough und im Proof angeführt worden sind.
Und dieses good enough verwendet denselben Test wie hier oben die Zusicherung.
Und das Itter an Till steht hier auch, verbessere,
solange bis gut genug und dann sind wir fertig.
Und dann sehen wir auch, wenn wir es aufrufen,
keine Fehlermeldung, nichts Vertrag ist eingehalten worden. Okay,
wenn wir es von minus eins aufrufen, naja,
dann passiert das, was immer passieren sollte, nämlich,
dass wir hier die Meldung bekommen, Vertrag wurde verletzt, ja.
Und der Vertrag wird hier aufgezählt, allerdings nicht sehr lesbar, muss ich sagen.
Also, ich habe mir da in einem System,
was für Didaktik gedacht ist, ein bisschen übersichtlichere Ausgabe gewünscht.
Aber okay. Ja, also unser Maisquerrutsch,
das haben wir zusammengesetzt aus drei Funktionen,
als erste Übung wirklich im Funktionalen Programmentwurf.
Die lokalen Funktionen, die können alle auf dem Parameter X
zugreifen, sodass sie ihn gar nicht brauchen.
Das macht es sehr einfach. Und wenn wir das im Modul Gut unterbringen,
da kriegen wir damit auch verständliche und wartbare Programme.
Gucken wir uns jetzt das Newton-Verfahren mal etwas allgemeiner an,
weil ich möchte an diesem Beispiel zeigen,
dass wir mithilfe der Funktionen höherer Ordnung unserer eigenen Funktionen höherer Ordnung schaffen können,
die wir dann als eigenen Kontrollrahmen nehmen.
Also was wir eben gesehen haben,
ich mache eine Iteration mit Hilfe-Nüten-Verfahren für die Wurzel einer Zahl
, da hatten wir fest verdrahtet drin,
die Formel für Approximation einer Zahl mit Newton-Verfahren
Aber das hilft uns nicht, wenn wir jetzt die dritte Wurzel,
vierte Wurzel oder sonstige Wurzel haben wollen,
sondern mit Funktionen höherer Ordnung können wir uns einem für alle
mal das Problem vom Hals schaffen,
indem wir das Newton-Verfahren selbst als Kontrollstruktur vorgeben.
Ich habe dieses Beispiel übernommen aus dem Buch Introduction to Functional
Programming von Bird and Wodler und da ist das Ganze sehr schön in Miranda gezeigt.
Also was ich hier habe, sind praktisch reckend Übersetzungen von den Miranda-Programmen.
Gut, also was hat unser großer Newton sich ausgedacht?
In dieser Iterationsformel, die wir gesehen haben,
da steckt die Annahme, das ist eine Funktion,
in der Nähe der Nullstelle durch eine gerade Approximiert werden kann.
Sie sehen schon, es kann Funktionen geben, wo diese Hoffnung trügt.
Also die werden entweder gar nicht differenzieren können oder die
eben nicht stetig sind und wo es sonstige Probleme gibt.
Also das Newtonverfahren ist gut, wenn in der Nähe der Nullstelle
die Funktionen selbst durch eine Gerade Approximiert werden kann.
Und dann schätzen wir einfach die Nullstelle,
indem wir Städte Nullstelle der Funktionen, die Nullstelle der Tangente nehmen.
Und hoffen, dass wir uns auf diese Weise immer dichter
an die richtige Nullstelle ranhoppeln. Und können wir also uns so vorstellen,
wenn das Route hier mal unsere Quadratfunktion ist,
wenn wir unsere erste Iteration hier,
diese Tangente in Zyan geschrieben oder dann diese,
an der nächsten Müllstelle kriegen wir die und so weiter und so weiter.
Und am Ende sind wir tatsächlich ganz dicht an der Nullstelle dran.
Gut, also machen wir es jetzt mal so,
dass wir einfach ein allgemeines Newton-Verfahren zur Suche einer Nullstelle für eine beliebige Funktion leben.
Da müssen sie es nie wieder selbst im Kopf machen.
Also ein sehr nützliches Programm. Also wir haben irgendeine Funktion
F und brauchen der Nullstelle und haben einen Schätzwert, YNN.
Und dann subtrahieren wir eben von diesem Schätzwert, die Differenz aus der Funktion und ihrer Ableitung. So,
was wir also brauchen als erstes, ist das,
was immer die Funktionen ist,
dass wir uns jetzt eine Funktion höherer Ordnung schaffen,
die zu der Funktion die Ableitungsfunktion,
wir müssen ja einfach einen Strich aufrufen,
wir brauchen nicht den Wert selbst,
sondern wir müssen dieses F von Strich aufrufen können,
wenn es das schafft.
Und dann können wir eben diese neue Nährung nehmen,
das Ganze iterativ machen wollen. So, das Allgemeine Newtonverfahren, ja?
Wir nennen das Ganze also Newton und stecken unsere Funktionen F
hinein, für die wollen wir eine Nullstelle haben.
Innen drin haben wir eine lokale Funktion, First Guess, ja? Und dieser erste Schätzwert,
der wird berechnet eben mit unserer Genauigkeit, die wir vorgegeben haben,
mit dem Good Innov, was wir schon kennen,
mit der Iterationsregel, und wir sehen jetzt,
wir müssen hier übergeben, Die Ableitung der Funktion F,
weil F kann ja eine Beliebige sein,
die können wir die Ableitung nicht fest vertragen.
Wir müssen noch eine Funktion schreiben, wie diese Funktion F ableitet.
Und dann können wir iterieren, indem wir eben immer wieder
Good DeNough aufrufen im Wert First Guess.
So, kriegen wir es hin, ins Querroot aufrufen.
Wir wollten die Entewurzel haben, ja. Und da ist,
übergeben wir als Bedingung eben, dass das Quadrat von Y
kleiner sein soll
Als der Wert, den wir hatten,
oder für die Tubel-Crude würden wir eben hier die Funktionen übergeben.
Die versucht, die Nullstelle dieser Funktion zu finden.
Y, mal Y, mal Y,
minus X ist 0. Diese Funktionen würden,
die Nullstelle dieser Funktionen ist genau die gesuchte dritte Wurzel.
Und so weiter. Entewurzel. Also einmal diese simple Funktion
Newton geschrieben. Und wir sind nie wieder in Verlegenheit,
wenn wir irgendwo null Stellen von Funktionen brauchen.
Wir haben den Kontrollrahmen
Okay, unsere Testfälle hier. Richtig. So,
das Ableiten von Funktionen hatten wir erst mal davor schon angesprochen,
als wir über Tangenten gesprochen haben.
So, die Funktion der Riff haben wir jetzt einfach wiederverwendet.
Ja, weshalb brauchen wir funktionale Abschlüsse und Closures?
Das möchte ich Ihnen an zwei weiteren Beispielen nochmal deutlich machen,
nämlich, dass wir die dringend brauchen für die Konstruktion von Hilfsfunktionen,
speziell mit Currying,
eine ganz wichtige Testfall und zweitens fürs Zusammensetzen und hintereinander ausführen von Funktionen,
um neue Funktionen zu konstruieren
Gut. Also diese, das Curry-Verfahren ist mit das Allerwichtigste,
was sie auf jeden Fall gut können sollen am Ende des Semesters.
Funktionskompositionen wird oft gebraucht, weil es
nicht ganz so zentral und dann gucken wir mal.
Was ist mit dem Curry-Verfahren gemeint? Stammt eigentlich aus der Theorie,
wenn man versucht hat, Beweise über Funktionen zu schreiben und sind
nicht immer tausend Varianten schreiben wollte?
Also für jede Variante der Funktionen mit beliebiger Zahl von Parametern,
sondern wollte es nur einmal schreiben müssen und zwar für einstellige
Funktionen.
Und dann gibt es dies
Die Hilfskonstruktion von Schönfinkel, dass man gesagt hat,
na prima, dann bilde ich meine entstelligen Funktionen auf
eine Folge von Anwendungen einstelliger Funktionen ab.
Aber Schönfinkel hat ja erstens einen komplizierten Namen,
der sich in Englisch nicht gut zitieren ließ.
Und zweitens hat er nicht genug geklappert mit seiner tollen Entdeckung,
berühmt geworden ist das Verfahren, in dem Curry gezeigt hat,
wie man es wirklich gut verwendet.
Und deshalb heißt das einfach Curry-Verfahren. Also genau genommen müssten
unsere Funktionen Schönfinkel heißen und wir müssten schön Finkelinks sagen,
aber in Programmiersprachen finde ich es immer nur als Currying.
Er hatte einfach
den besseren Namen.
Also wenn Sie heiraten und einen Namen wechseln wollen,
heiraten Sie lieber jemand, der Curry heißt,
als Schönfinker, wenn Sie berühmt werden wollen, ja.
Gut. Okay, also der hat einen Schönwinkel übernommen.
Also was ist die Grundidee? Wir haben Funktionen höherer Ordnung
und wollen Funktionen übergeben, die nur eine Stelle, die einstellig sind.
Aber die Funktionen, die wir schon haben, ist dummerweise zweistellig.
Zum Beispiel die Maximumsfunktion. Wenn wir das Maximum gegen eine
andere Zahl haben wollen und wollen, dass eine Map-Funktion stecken,
brauchen wir eine einstellige Funktion und der zweite Wert mit gegenüber,
den wir das Maximum haben wollen,
so muss anders mit übergeben werden und den übergeben,
wenn der Closure.
Ja, das ist eigentlich die Idee dabei. Also,
wir machen einfach Funktionsabstraktion. Wir brauchen immer das Maximum von
irgendeiner Zahl gegenüber drei. Ja? Und dafür führen wir
die Hilfsfunktion Currying ein, die Funktion Max in eine Funktion überführt,
die immer das Maximum gegen drei bestimmt.
Und das macht die schöne Hilfsfunktion Curry,
die kriegt eine Funktion und ein Argument und wir kriegen eine neue Funktion zurück,
bei der das erste Argument an den übergebenden Wert
gebunden ist
Also wenn wir sagen, Curry, Max,
nimm die Maximumfunktion und 3, dann haben wir eine Funktion,
die berechnet das Maximum aus 3 und einem weiteren Argument, das kommt.
Also hier haben wir diese gecurryte Funktion stehen als Aufruf und dann überginnen wir
das zweite Argument und das Maximum von 3 und 10 ist 10, nicht weiter überraschend.
Also Currying gibt uns einfach eine neue Funktion zurück und bei der ist das erste Element schon belegt und die Funktion,
die hier zurückkommt, weissten Wert des ersten Argumentes,
aus der Bindungsumgebung, das steckt in der Closure drin,
nämlich das X ist hier in Bindungskontext,
wo dieser Landerausdruck ausgewertet und definiert wird,
ist das als Parameter der Curry-Funktion ja definiert.
Also dieser Ausdruck Curry Max 3 hat sich gemerkt, aha,
das X von dem Curry war 3 und deshalb kann diese
Funktion eben auch das Maximum von 3 und 10 mal rechnen.
Mit Curry können wir wirklich sehr elegant Hilfsfunktionen für viele,
viele Fälle definieren und können damit Programme sehr schön lesbar machen.
Zum Beispiel eine Funktion, die etwas um eins erhöht,
die Inkrementfunktion, die kriegen wir einfach,
da müssen wir nicht mehr groß schreiben, die feine Inkrement plus X1,
sondern wir schreiben einfach Curry plus eins, das ist die Funktion,
bei der ein Argument von plus schon an eins gebunden ist.
Ja, oder um ein Reziprugwertfunktion, eins durch X zu kriegen.
Einfach machen wir ein Curry von durch an eins.
Für die Halbierfunktion, oh, leider habe ich jetzt nicht
Curry geschrieben, sondern Curry Air.
Warum brauche ich da eine andere Art von Curry
Das weiß auch jemand anders. Benedikt. Weil wir dachten,
das ist das zweite Argument, wo noch nicht das erste ist.
Genau. Gibt es so einen Curry eigentlich
auch für mehrere Argumente, jetzt für mehrere Argumente?
Ja, gute Frage, werden wir gleich sehen.
Hier haben wir einen Currying für ein Argument. Das war meine Faulheit,
ich wollte keine Lust, hier eine Funktion,
eine eigene Curry-Funktion zu schreiben mit beliebig vielen Argumenten.
Die Standardfunktion Curry in Racket, die nimmt beliebig viele Argumente
Ja, also wenn ich ein Currying haben will für viele Argumente,
dann würde Curry alle Argumente, wie sie hier aufgezählt sind,
von links beginnend binden und frei bleiben,
alle weiteren Argumente der Funktionen F,
für die kein Argument mehr da war.
Von links beginnen macht Curry. Und Curry ging von rechts,
Curry ja, als Standardfunktion in Reckett, beginnt von rechts zu binden.
Und damit Bleiben dann die Argumente nach links frei.
Und wenn wir hier durch zwei teilen wollen,
dann müssen wir Curry A schreiben, sonst würde es ja heißen,
durch zwei X, also zweigeteilt durch X-Ma,
wir wollen eigentlich X durch zwei haben, also müssen wir von rechts binden.
Also diesen kleinen Unterschied hier, der ist wichtig, ja,
den merken Sie sich bitte, wenn Sie mit Curry arbeiten,
das ist so der erste Flüchtigkeitsfehler, den man macht,
die falsche Curry-Variante zu wählen.
Also auch wenn wir X-Sucht 2 haben wollen,
das soll der Exponent und durch die Basis sein,
müssen wir uns natürlich genau überlegen,
ob wir Curry A oder Curry nehmen
Und mal ein paar Beispiele. Also wir brauchen das Curry
eigentlich stets und ständig, wenn wir mit Map,
mit Filtern, mit Fold arbeiten.
Niemand hat da wirklich Lust, große Funktionsdefinitionen vorher zu machen,
wenn man nur so kleine Ex- und Top-Funktionen braucht.
Also wenn wir beispielsweise aus der Liste drauf testen wollen,
ob die Elemente alle kleiner Null sind oder größer Null.
Wenn ich hier schreibe, Curry größer Null,
dann habe ich einen kleiner Test für die Argumente gemacht.
Alles, was größer als Null ist,
erfüllt mich
die Bedingung
Null größer X. Also würde ich in dem Fall als Resultat für eins in False kriegen,
False, False und diese beiden negativen Zahlen sind die ersten,
für die ich ein positives Resultat kriege,
weil minus sechs kleiner als null ist.
Oder hier bei der Multiplikation ist das egal. Also wir wollen
Zahlen verdoppeln, dann würden wir einfach schreiben, Map,
Curry mal zwei und das würde über diese Liste hier gehen.
So, um dieses Bild mit den Tangenten zu schreiben,
habe ich eben auch in Currying gemacht an Argumente
Kleine Funktion, zeichne viele Tangenten an meine Funktion
F. Also die Funktion Graf-Fun hatten wir das letzte Mal angesprochen,
als wir die Abgleitungsfunktion definiert hatten. Und ich zeichne hier
jetzt also die Funktion F in Rot, ja, unsere Quadratfunktion.
So. Und dann mache ich ein Mapping, wo ich die Funktion zeichne,
Tangente aufrufe, die wir auch schon mal besprochen hatten.
Und diese Funktion zeichnet Tangente hatte zwei, hatte drei Parameter,
nämlich erstens den Namen der Funktion. Und dann die Stelle,
an der die Tangente gezeichnet werden sollte und die Farbe,
in der sie gezeichnet werden sollte.
Und wir wollen jetzt in allen Fällen einmal fest die Funktion, die Tangentin nehmen.
Von F. Und dann, das einzige, was variabel ist,
ist die 1 und, ja, die Stelle und die Farbe.
Also binde ich mit Currying die Funktion F schon mal ein Zeichen Tangentin,
da habe ich eine neue Funktion,
die zeichnet immer die Funktion F und braucht nur noch zwei Parameter,
nämlich Die Stelle, wo die Tangente gesucht wird und die Farbe,
in der sie gezeichnet werden soll.
So, der nächste Fall für Funktionen höherer Ordnung.
Curry war jetzt die, eine der ersten Beispiele,
wo wir Funktionen geschrieben haben, die neue erzeugen.
Der Riff war eine Ableitungsfunktion, bestimmen hat er uns eine gegeben.
Die Curry liefert uns neue Funktionen, indem es Parameter festbelegt.
Was man relativ oft auch braucht, ist,
dass man Funktionen einfach in ihrem Ablauf zusammensetzen will
Hintereinanderausführung. Also zuerst, wenn die Funktionen gehe an und
was da rauskommt, dann möchte die Funktion F-Draud anwenden.
Ich kann jetzt Klammern, Gebirge bauen und das so schreiben,
F&G von X, ja. Mit zweien geht es noch,
aber mit Szenen bringt es keinen Spaß mehr.
Und dafür haben Sprachen wie Miranda und Teskel, den Infix-Opparat,
das Funktionskomposition. In Recket haben wir da für die Standardfunktionen
Kompos. Ja. Und zum Beispiel die Funktion Kedödel,
die könnten wir einfach schreiben als Kompos,
könnt ihr köttern
Oder die Komplimentfunktionen, die im Gegenteil aus einer anderen Funktion macht,
ja, könnten wir erzeugen, indem wir die Funktionskomposition nehmen
und der als erstes Argument schon immer das Not übergeben.
Dann habe ich eine Funktion, die eine andere Funktion negiert.
Und das können wir mit Curry hinkriegen und haben damit
eine Funktion, die jede Funktion ins Gegenteil verkehrt.
Und wie schreiben wir das Compose, also die Hintereinanderausführung von,
geht zuerst angewendet und auf das Resultat F,
in dem wir einfach hier Compose definieren,
Funktionen als Argument und letztendlich kommt dann für die zusammengesetzte Funktion es angewendet aufs Resultat von Geht zurück, ja.
Auch Compose, Eda, gleich wieder die Frage kommt,
kann in der Standardimplementation in Reckett beliebig viele Funktionen nehmen.
Und wenn wir mit Compose und Currying arbeiten,
eine ganze Menge netter Eigenschaften, zum Beispiel Compose ist assoziativ,
wenn wir im mathematischen Sinne Funktionskompositionen machen,
dann können wir zuerst geben mit Haar zusammensetzen und dann davor
hängen oder aber zuerst diese Komposition nehmen und dann Haar dahinter setzen,
das ist dasselbe.
Und das Kompos bewahrt uns wirklich vor Klammergebirgen.
Gut, in der Listwelt scheiden sich die Geister,
was schicker ist. Also viele lieben den klaren Stil,
dass man einfach eine Funktion F einführt,
indem man sagt, ich haue hintereinander weg,
diese Funktionen auf mein Argument F1 F2 F3
und habe eine neue Funktion
Kann ich zum Beispiel schreiben, die Funktionen,
die die Quadratwurzel aus dem absolut bizarren Betrag an der Zahl zieht,
ist die Komposition aus Quadratwurzeln und Absolutbetrag.
Und das ist die Funktion WU. Warum scheiden sich die
Geister? Alles Puristen sagen, ach Mensch, nee.
Hier habe ich jetzt eine Funktion, Wut definiert und
kein Mensch weiß, wie viele Argumente ihr habt.
Und von welchem Typ die Argumente sind.
Das Ding heißt einfach WU. Warum mich rauszukriegen,
wie viele Argumente WU hat,
muss ich mir die letzte Funktion in der Reihe angucken und mich fragen,
welche Argumente hat die eigentlich?
Weil das sind die Argumente,
die reingehen
Also wenn man Funktionen so definiert, um die zufriedenzustellen,
die Gang wissen wollen, wenn eine Funktion definiert,
was hat sie eigentlich für Argumente und von welchem Typ sollen sie sein?
Würde ich hier immer einen Kommentar reinschreiben und zumindest ist die Benutzer
von der Pflicht entbinden, dass sie hier suchen müssen,
wo sind eigentlich die Argumente meiner Funktion.
Gut, also Compos, Compos, Curry und so weiter,
die funktionieren wirklich deshalb, weil in rein funktionalen Programmiersprachen Funktionen,
nicht einfach Pointer auf Code sind, sondern die Bindungsumgebung haben.
Sie sind Closures. Wir haben gesehen, dass Curry weiß immer,
was der Wert ist, der genommen werden soll,
weil in der Bindungsumgebung, wo die Funktionen gebunden wird,
tatsächlich das Argument enthalten ist.
Also das Suggepackt, dass jede Funktion in Racket,
den lokalen Bindungskontext mit sich herumschleppt, das führt dazu,
dass wir zehnmal die Funktionen Sinus mit Curry binden können,
an andere Argumente und jede dieser gekörrigten Funktionen
weiß,
welches ihr Argument war
Deshalb funktionaler Abschluss Closure. Haben wir schon mehrfach angesprochen,
aber hier ist es wirklich wichtig. So ohne Weiteres können
Sie Curry oder ähnliches überhaupt nicht nachbilden in Sprachen wie C.
Wer fiel denn C programmiert hat, eure Objective C und so,
der weiß, wir können locker einfach invektoren,
die Adressen von Funktionen, Funktionspointer reinsetzen und so weiter.
Aber dann zeigen die zum nackten C-Kurt für diese Funktion
und Es ist keine Bindungsumgebung der lokalen Definitionen dabei.
Ja, das muss man sich dann selbst noch nachbauen,
um wirklich mit Closures arbeiten zu können.
Reine funktionale Programmierung geht einfach nur in Programmiersprachen, die Closures kennen.
Geht zum Beispiel sehr schön im Power, wenn man Paul-Skripte macht. Gut,
wenn wir beispielsweise sowas schreiben, ja, ein Compose,
Hintereinanderausführung aus der Ableitung der Sinus und der Ableitung des Tangens.
Denn dieser erste Aufruf von Drif, Drif ist ja eine Funktion höherer Ordnung,
kriegt die Funktion und gibt Die Ableitung zurück,
die weiß, wenn dieser Teil ausgeführt wird, aha,
ich sollte den Sinus ableiten, weil das eben in der Closure steckt.
Und der zweite Aufruf gibt eine andere Funktion zurück,
die sich dadurch unterscheidet, dass in deren Closure die Tangens-Funktion steckt, ja.
Also das ist mal ein Stück sinnvoller Programmcode in Reckett,
der nichts enthält als die Namen von Funktionen, ja.
Also so ungefähr der Stil, in dem wir programmieren werden,
also Zahlen brauchen wir selten, ja.
Die werden dann diese Closures intern genau repräsentiert als Projekte sozusagen,
wie man das in Yara machen würde,
wo man ein anderes Objekt dabei explizit hinschreiben muss oder generiert
der Interpret da tatsächlich dynamischen Programmquot für diese Funktionen,
die da wieder andere Funktionen aufgeben.
Also zum einen hat der in Top-Ritter tatsächlich eine eigene Datenstruktur,
ein Datentyp in Vironment. Wir können mit den Umgebungen,
die können wir wie alles andere in Racket auch hin und her erreichen.
Und diese Umgebung sind in der Definition einer Closure als Wert dabei.
Und das ist ein verschachtelter Stack von der Tiefe der der Umgebung.
Also die innerste Umgebung hier vom Drift,
die zeigt auf Sinus und die wir rüdern,
zeigt auf eine neue Umgebung, wo das Kompuss definiert ist und diese ganzen Umgebungen sind geschachtelt.
Also das ist sehr schön in dem Buch von Ebelson, Nancessmann,
erklärt, wie diese Strukturen aufgebaut sind und Reckett erlaubt uns,
auch die Bindungsumgebung, als Objekte weiterzugeben,
sodass wir Funktionen in einem bestimmten Bindungskontext auswerten können.
Davon würde leider keine Zeit, damit auch noch zu arbeiten,
aber das ist so im Herzen einer Eines Programmierstils in Reckett,
der mit Skim eingeführt wurde, Continuations. Also wir können,
wenn wir eine Berechnung begonnen haben, in Reckett ohne weiteres sagen,
ach jetzt machen wir mal Feierabend Pause,
dann packen wir den Zustand der Berechnung ein,
frieren den einen als Objekt und tun in eine Datenstruktur,
geben es an eine andere Funktion und die soll an der Stelle mit der Berechnung weiterfahren, fortfahren.
Also Continuations kann man eine ganze Menge tolle Sachen machen,
quasi parallel programmieren und so weiter oder eben auch Zustände aufheben, ja.
Wenn man ein Spiel programmiert, das man an derselben Stelle
weitermachen kann, die angefangenen Berechnungen einfrieren.
Und für all das gibt es fertige Datenstrukturen. Und genauso
sind eben die Umgebungen Datenstrukturen. Okay. Noch einen Satz,
also einfach um ein bisschen Theorie mit in die Vorlesung einzubauen.
Alles Dinge, die wir verwenden könnten, wenn wir Beweise machen wollen, ja.
Also für hintereinander Ausführung mit Compos. Da gilt,
wenn wir Compos mit Map und Currying zusammen durchführen, ja.
Dann ist es das Gleiche, als wenn wir einen Curry machen würden,
von der Map-Funktion auf G, ja, also hier,
was machen wir hier, eine zusammengesetzte Funktion,
bilden aus F und G und die schicken wir mit Map über irgendetwas anderes.
Und dieser Satz sagt uns, das ist dasselbe,
als wenn wir das Map separat machen,
erst mit der Funktion G und dann mit der Funktion F.
Und das per Hintereinanderausführung ausführen. Und Wenn wir,
wenn eine Umkehrfunktion zu F existiert,
dann können wir auch ein Mapping mit F machen und davon die Umkehrfunktion bestimmen,
dass dasselbe, als wenn wir ein Mapping mit der Umkehrfunktion von F gemacht hätten.
Und wir können all das auch über zusammengesetzte Listen machen,
da nochmal Erinnerung an den Satz, den wir schon hatten.
Wenn wir zwei Listen mit Appent verbinden und darüber ein Mappen machen,
ist es dasselbe, als wenn wir Teillisten mit Map bearbeiten und das dann verbinden.
Es gibt eine weitere schöne Welt von funktionalen Programmierern. Von Funktionen höherer Ordnung.
Und die fehlen leider sowohl in Viranda als auch in Recket.
Und ich habe sie zum Teil neu definiert in der letzten Recket-Version,
sind sie inzwischen aber auch nur dummerweise unter einem anderen Namen,
als in dem ganzen Rest der Programmierwelt bekannt sind,
eingeführt worden.
Aber das sind Funktionen, die man
immer wieder braucht
Weshalb eigentlich? Also die meisten Funktionen, die ich hier aufgeschrieben habe,
werden ihnen als ziemlich nutzlos vorkommen, weil sie immer dasselbe tun.
Ja, aber genau das braucht man manchmal. Weshalb,
wenn wir einen Kontrollrahmen aufsetzen,
bei dem wir eine Funktion höhere Ordnung schreiben,
die den allgemeinen Fall beschreibt,
wie ein Problem gelöst werden kann und alles bedenkt,
dann hat das Ding vielleicht 20 Parameter.
Aber unsere speziellen Probleme, die kommen vielleicht mit zehn Parametern
aus und für die anderen zehn Fälle brauchen wir einfach nur Standardlösungen.
Und diese Standardlösungen, da übergeben wir dann einfach,
tue nichts Funktionen
Dafür sind diese Funktionen da, dass wir in die vielen überflüssigen Parameter,
die wir gar nicht brauchen, so ein Dynastik reinstecken können.
Zum Beispiel, wir brauchen eine Funktion, die immer einen konstanten Wert erzeugt.
Also die Funktion, die immer einen konstanten Wert zurückgibt,
können wir so erzeugen mit Kunst 6,
gibt uns eine Funktion, die egal,
was ihre Argumente sind und wie viele Argumente es sein mögen,
die wird immer sechs zurückgeben.
Also hier rufe ich die Funktionen,
die hier erzeugt wurde, mal auf und übergebe ihr
zwei Zahlen und was immer die Zahlen waren
Die wird erstmal die Argumente fleißig auswerten, darauf Rechenzeit verschwenden.
Und dann sagt sie uns sechs. Und ein Prädikat,
das zu allem Ja und Am sagt, immer trug,
könnten wir so erzeugen, also die Always-Funktion, konst zu.
Und was immer die Argumente sind, von welchem Typ oder
wie viel, also die würde uns immer zu ergeben.
Die Funktion Never, die niemals war, tue, sagt er.
Also ich bin der Geist, der stets verneint,
haben wir hier. Never, wie auch immer,
kommt dann eben Falls raus,
wenn wir es mit Kunst erzeugen
Das heißt also, hier haben wir
eine Funktion definiert durch einen Aufruf einer Funktion höherer Ordnung.
Funktionen, die schon mehr tun und ich ziemlich oft verwende
, das sind Conjoinen und Biss-Join.
Sie kennen Konjunktionen und Dysjunktionen als logische Operatoren natürlich.
Das Logische und das oder für Wahrheitswerte. Und hier reden
wir jetzt über die Konjunktionen und Dysjunktionen von Prädikaten,
wann Funktionen die Wahrheitswerte haben
Um aus Basisprädikaten ein höheres Prädikat höherer Ordnung zu bauen.
Das Conjoin ist eine Funktion,
die liefert uns, ein neues Prädikat, das testet,
ob alle Basisprädikate wahr sind, die wir übergeben haben.
Disjoinen liefert das neue Prädikat, das prüft,
ob mindestens eines der übergebenen Prädikate beigegebenen Argumenten wahr ist.
Also wenn wir wissen wollen, ob ein Wert eine Funktion haben wollen,
die immer testet, die Zahl kleiner als drei ist.
Und ja, ne Quatsch, größer, deshalb,
was teste ich hier eigentlich? Von rechts bindend,
also doch zwischen drei und zehn liegt ja das Curry A genommen.
Man muss da sehr genau hingucken. Ja,
dann kann ich einfach diese Vergleichsoperatoren größer und kleiner an die Argumente
binden mit Curry und dann mit Conjoin die Funktion erzeugen,
die beides testet.
Oder eben um diese Strichlisten zu erzeugen, die wir schon
ziemlich oft erzeugt haben. Das ist jetzt,
glaube ich, die zehnte Art,
das zu machen
Wir erzeugen, die immer konstante Funktionen,
die dann immer den Wert eins ergibt,
wenn wir die jetzt über unsere Liste schicken,
na prima, dann haben wir Strichliste von Einsen.
Und um die Länge dieser Liste rauszukriegen,
würden wir also diese Strichliste von Einzelnen nehmen und Falten,
indem wir als erstes Argument Null nehmen und wir haben auch schon wieder eine Variante,
wie wir die Längenfunktion feststellen können.
So, Funktionen, die es in Racket vordefiniert gibt,
da gibt es inzwischen so viele,
dass kaum noch Raum für neue Übungsaufgaben bleibt
Zum Beispiel eine Funktion, die ein Prädikat nimmt und
das negierte Prädikat erzeugt, heißt Niggate. Sehr nützlich,
wenn sie etwas Bestimmtes in einer Liste suchen,
also nicht ein Element, dessen Wert sie kennen,
sondern ein komplizierteres Suchkriterium haben,
nehmen sie die Funktion höherer Ordnung Find F,
da können sie ein Prädikat übergeben und eine Liste und dann bekommen wir,
denn das Element zurück,
was an die Bedingung erfüllt
Oder wenn wir wissen wollen, ob mindestens ein Element
das Prädikat erfüllt, dann bekommen wir die Restliste zurück.
Mem F, Member, teste mit einer Funktion F,
bekommt das Prädikat übergeben die Liste und dann werden wir die Restliste ab der Stelle,
wo das Suchprädikat erfüllt ist, bekommen.
Der Altquantor und der Existenzquantor erfüllen alle Elemente einer Liste,
die Liste jetzt als Menge interpretiert oder mindestens ein Element der Liste,
den Wert, bekommen wir mit Endmap oder All-Map.
Auch hier das Prädikat rüberschicken, and Map.
Und können das Prädikat, kann viele Listen gleichzeitig prüfen,
wenn das Prädikat mehrstellig ist, wie eben die Map-Funktion.
Und da wird getestet, ob wirklich jede Kombination von Listenelementen das Prädikat erfüllt.
Oder hier bekommen wir Bully, das Trut zurück,
sobald das erste Element die Liste, die Bedingung erfüllt.
Ja, und ein weiterer Bereich, wo Funktionen,
höhere Ordnung, gang und gäbe sind,
das ist Die Verwendung von Funktionen als Händlerfunktion, die auf Ereignisse reagieren,
in einem Simulationskontext oder eben in einer grafischen Bedienung, oberflächliche Guis.
Also zum Beispiel, wir wollen ein Programm schreiben,
das was Bestimmtes tut, wenn bestimmte Mausknopf gedrückt wird.
Oder wenn die Maus sich bewegt hat oder wenn Texteingabe erfolgt ist.
Und all das wird in Recordless dadurch gelöst,
dass wir Funktionen haben, aktiviert werden,
wenn das Ereignis eintritt und dann unsere Händlerfunktionen aufrufen, die wir übergeben haben.
All das sind Funktionen höhere Ordnung. In Yava kennen Sie die Klassen-Actionhändler,
Eventhändler, wo genau für diese Dinge eben Delegit-Objekte übergeben werden,
die dann die Methode aufrufen, die ausgeführt werden soll,
wenn es geschehen soll.
Und das machen wir, indem wir einfach direkt an die Closure übergeben.
Callback-Functions heißt das dann in Recket oder List. So,
wenn wir Und das mal ausprobieren wollen an einem Spielzeuggui.
Ja, das simpelste Bedienoberfläche direkt bietet.
Es hat Schöneres, aber darin können wir erst mal üben.
Also wir verwenden das Modul How to Design Program Squee.
Ja, und das hat schon mal als ersten Basisdienst Create Window.
Damit können wir ein Fenster erzeugen. Und dieses Fenstern füllen
wir zeilenweise mit Bedienelementen. Jede Zeile von Bedienelementen ist hier
so eine geklammerte Liste. Und da tun wir die,
bedienen Elemente rein. Und als Resultat bekommen wir ein Fenster
zurück und dieses Fenster sollten wir uns merken
Den Werte mit West auch mal anzeigen können oder so.
Show, Window zeigt so ein Fenster an und halt,
wenn nur verbirgtes Fenster. Und ja, was sind so
? Bedienen Elemente zum Beispiel einen Knopf, ne?
Make-Button erzeugt uns ein solches Gui-Item und das können wir in
unser Fenster eben tun, ja? Bekommt ein String,
damit wird der Knopf beschriftet und wir können noch ein Ereignis übergeben,
sehen wir gleich. Dieses Event Bullion, das ist Ja,
ein Funktion, die als Eventhändler aufgerufen wird,
also da kommt jetzt genau die Funktion hin,
die ausgeführt werden soll, wenn jemand diesen Knopf drückt.
Und als Resultat, ob es erfolgreich war,
kriegen wir an Truhe der False zurück.
Okay. Anzeige, Texttextfelder können wir mit Make Message machen,
ein String und es gibt auch ein Gui-Item, ja.
Und wenn wir einen solchen haben, können wir ihn
mit Troy-Message auch schreiben und anzeigen
Eingabefeld, wo jemand einen Text fällt,
wo meinen Text reinschreiben kann, bringen wir mit Make-Text.
Und abfragen, was der Mensch vom Bildschirm geschrieben hat,
können wir mit Text-Contents machen. Dann fragen wir das Gree-Item,
was hast du da im Moment eigentlich für einen Text drin stehen?
Kriegen wir einen String zurück. Ein Auswahlmenü, wo man
verschiedene Sachen fragen kann, okay, machen wir mit Make-Choice.
Und da kommen einfach nur ein paar Strings rein und bekommen dann auch ein Gree-Item zurück.
Wenn wir jetzt einen solchen Choice haben, können wir fragen,
was ist denn das augenblicklich augenglücklich ausgewähltes Trinks von den vielen,
die da sind. Und das macht die Funktion Choice-Index,
die sagt uns, da ist zurzeit der dritte ausgewählt.
Und dann können wir uns überlegen, was wir dann tun wollen.
Okay, ein Programm, was nichts weiter kann,
als sie von der Welt verabschieden.
Ich habe hier ein Fenster erzeugt und da ist ein Quitt.
Und das wurde angezeigt mit Joe Window. Und wie machen
wir das? Require, wir ziehen dieses Paket rein,
von dem ich eben gesprochen habe, das Modul.
Wir definieren ein Fenster mit Create Window und da müssen jetzt,
müssen jetzt eine Liste von Zeilen aufgeführt werden.
Also die Liste von Zeilen,
das kriegen wir mit List und die erste Zeile kriegen wir mit List,
Make-Button Quit und der Quit-Button macht nichts weiter als Haid Window.
Ja, das ist der Händler, den wir hier übergeben.
Und dann zeigen wir das Fenster an mit Show Window
und warten, dass jemand den Quit-Button drückt.
Und dann war das Spiel auch schon zu Ende.
Sehr rudimentär. Mit ein bisschen mehr Komfort können wir hier noch ein Textfeld vorsehen,
wo man seinen Namen eingeben kann und eine Nachricht hello vote rausschreiben
und haben noch eine,
okay,
Knopf hier
Okay, win the way. Also das würden wir so machen,
die feinen Text, wann? Make text, peace and
I your name. Und die Message wann war die Cello Word,
was wir gesehen haben, ja? Mit Make String erzeugt,
dann hinterher noch 33 mal Space, damit das Ding linksbündig erscheint. So,
was wir das nächste Mal machen werden, da habe ich,
wenn wir heute kaum noch einsteigen können,
wir machen mal als erste Übung für Simulationswelten,
wie wir sie grafisch in Racket machen können,
werden wir uns eine eigene Uhr und ein eigenes kleines Spiel programmieren,
wir die Zeiger ticken sehen und so weiter.
Und dann kleine Simulationswelt, in der wir interaktive Abläufe machen können.
Also was in Rackett ganz schick ist und was für in unserem Programmierpraktiker immer üben ist,
dass wir Rackett-Programme verteilt auf verschiedenen Workstations ausführen lassen können und die
über ein gemeinsame Datenstrukturuniversum Nachrichten austauschen können und kommunizieren können.
Aber davon das nächste Mal
, danke