Guten Morgen, meine Damen und Herren. Dann lassen wir
uns mal beginnen. Wir haben das letzte Mal ja angefangen,
mit den ersten Sprachelementen von Recke das hin Gefühl dafür haben,
die sind taktisch so ein Recked-Programm aussieht.
Heute möchte ich in ein paar Dinge zeigen, die in
anderen Programmiersprachen nicht so vorkommen. Das ist eine Grundidee bekommen,
weshalb man eigentlich diese Art von Syntax gewählt hat und was damit möglich ist.
Das erste wichtige Thema heute wird sein Zeichen Symbole, Und dass wir drüber sprechen,
weshalb eigentlich in diese Sprache Dinge wie Symbole eingebunden wurden.
Ist klar, die an dieser Sprache neben der reinen Mathematik sind eben,
unsere Kolleginnen und Kollegen aus dem Bereich Künstliche Intelligenz,
die versucht haben, wir Tracket-Programme zu schreiben,
mit denen man Bedeutung von Dingen errechnen kann.
Also, um mal ganz konkret am Beispiel zu nennen,
eben Dinge wie Expertensysteme,
mit der sie natürlicher Sprache Anfragen formulieren können und ihnen am Ende gesagt wird,
ja, also lieber Patient, mit den Symptomen,
die sie so aufgelistet haben,
vielleicht was sollten Sie mal Ihr Testament machen oder so.
Jedenfalls die. Also Dinge, wo aus einem bestehenden Vorrat an
bekannten Fakten und zusätzlichen Beobachtungen mit Hilfe von Schlussfolgerungsmechanismen irgendetwas berechnet wird.
Und dazu muss man eben in der Lage sein,
Einheiten zu benennen, die hantiert werden.
Anders als die klassischen Daten in Programmiersprachen. Anderes Beispiel,
wo wir die Grundlagen in dieser Vorlesung auch noch besprechen werden,
wie man da hinkommen kann, mit Hilfe von Symbolen,
werden so Programme wie Mathematiker, die in Gleichung symbolisch lösen.
Also in der Lage sind Gleichungen aufzulösen nach Variablen Verschiedene Lösungen
zu finden, Formeln zu vereinfachen. Da wird eben nicht
mit den Zahlen gerechnet, sondern mit den Bestandteilen hantiert.
Und genau diese Programme gehen zurück auf die Verwendung von Symbolen,
die wir in diesen formalen Darstellungen repräsentieren.
Das Zweite, wo ich heute noch hoffe, hinzukommen,
ist die schöne simple Datenstrukturliste, die für uns das Arbeitspferd sein wird,
eigentlich für alle Datenstrukturen, die wir haben.
Na gut, wie wir damit loslegen,
wollt ihr noch eine Frage in die Runde stellen?
Letzte Woche bin ich ja gebeten worden, ob wir nicht
zu dieser Vorlesung das Skript drücken können und angehören gezeigt,
dass sie bereit werden, es zu bezahlen.
Generell, wir dürfen nicht mehr drücken auf Kosten der Universität,
sondern es ist tatsächlich nur noch möglich,
wenn sie sich an den Kosten beteiligen.
Nur, ich kriege mal nach den Preisen erkundigt, die
unsere Uni-Druckereien nimmt. Und ich finde, für das Skript,
was ich habe mit seinen mehr als 1000 Seiten,
wird der Preis schlichtweg unverschämt.
Also sie sind echt besser dran, wenn sie die Seiten,
diese zur Prüfungsvorbereitung wirklich haben wollen,
in PDF rauskopieren und dann das persönlich drucken
Nach den Meldungen, die wir bekommen haben,
wie viele interessiert werden, müssten sie damit rechnen,
dass das gedruckte Skript über die Uni-Druckerei so um die 36 Euro kostet.
Und das ist, finde ich,
schlichtweg zu viel für einen Vorlesungsskript, muss ich sagen.
Wer wird denn bei dem Preis immer noch interessiert, dass wir es zentral drucken?
Okay, ich glaube, das beantwortet meine Frage,
dann lassen wir das einfach. Ich bin keinem böse,
wenn sie einfach aus dem Skript dem PDF,
was sie haben, sich die Kapitel,
von denen sie meinen,
das sind die wichtigen Definitionen und so rauskopieren und die
separat drucken
Übt oder das PDF in Druckerei geben und das dann nach der Sehstärke
ihre Augen entsprechend verknallern, dass der Papieraufwand geringer wird.
Also der Preis von 36 Euro war jetzt der geschätzte Preis,
wenn wenigstens 100 ist, zentral drucken lassen, sonst wird es noch teurer.
Okay, gut. Ja, um Symbole einzuführen,
wollte ich einen ganz kurzen Ausflug machen in die Symbiotik,
also wirklich Aridas Deutsches Fassung Symbiotik für Dummis oder so.
Ich möchte da nicht einsteigen, weil das eine Wissenschaft für sich.
Nur Zeichen, Symbole gehen in letzter Zeit immer durch die
populäre Literatur mit solchen Büchern wie der Name der Rose und so weiter.
Und jeder hat eine intuitive Vorstellung davon, was damit gemeint sein kann.
Ich möchte einfach zeigen, was Symbole aus der Sicht einer Programmiersprache sind.
Zunächst mal sind Symbole Dinge, die aus Zeichen aufgebaut werden
und diese Zeichen werden auf der Basis eines Alphabets erstellt.
Bei der maschinellen Informationsverarbeitung, Nachrichtenverarbeitung haben wir als Zeichen frei vereinbarte diskrete Schrift- und Zahlzeichen.
Und die Programmiersprachen werden beschrieben als Worte über einem Alphabet Sigma,
das haben sie in den formalen Grundlagen der Informatik ja gelernt.
Damit können wir formale Sprachen bilden. Der Teil ist gegessen
und ist in Recket wirklich nicht anders als sonst auch.
Das Wichtige ist jetzt,
wir können so eine formale Sprache aufsetzen und sagen,
das sind die Worte unserer Sprache,
gebildet aus dem Alphabet,
was wir haben,
A bis Z und noch
ein paar Umlaute
Und wenn wir damit Nachrichten übertragen wollen, müssen wir diesem Zeichen aber eine Bedeutung zuordnen.
Und darum geht es eben bei den Symbolen.
Also wenn ich irgendwie eine obskure Buchstabenfolge sende,
muss man beim Empfänger und bei mir natürlich ein Gemeindesverständnis drüber herrschen,
was das bedeuten soll.
Für die Nachrichtenübertragung müssen wir eine Bedeutung zuordnen. Und wenn
es um maschinelle Informationsverarbeitung geht, sprich ein Computer kriegt diese Nachricht,
dann muss es eindeutig und effizient ermittelt werden können.
Also es hilft uns nichts,
wenn wir das in einer Art verschlüsseln oder aufschreiben,
wo unser Computer dann fünf Wochen rechnen muss,
um rauszukriegen, was wir gemeint haben.
Und es sollte eindeutig sein. Und das sollte überhaupt ermittelbar sein.
Sprich, die Bedeutung eines Symbols muss eine berechenbare Funktion sein.
In endlicher Zeit einen Wert liefern. Und damit können wir
jetzt in der Definition für ein Symbol kommen,
aus der Sicht der Informatik und speziell jetzt im Zusammenhang
mit Programmiersprachen
Symbol, einfach ein Paar aus einem Zeichen und seiner Bedeutung.
Und die Bedeutung dieses Symbols wird je nach Verwendung frei vereinbart.
Dasselbe Zeichen kann je nach Kontext eine völlig andere Bedeutung haben.
Und in Lizb und Racket,
also eigentlich seit der ersten Erfindung von Lizb,
sind Symbole eingebaut in die Programmiersprache und wir können ihnen eine Bedeutung zuordnen.
Gut, Beispiele einfach für Symbole und ihre Bedeutung in
unterschiedlichen Kontexten
Wenn sie nur dran denken, sie kommunizieren mit irgendeinem Menschen,
den sie auf der Welt treffen und schütteln den Kopf oder nicken.
Je nachdem, in welchem Kulturkreis sind,
kann das was gänzlich anderes bedeuten. Also Zeichen verwenden,
deren Bedeutung nicht eindeutig klar ist, ist ruskant,
wenn sie nicht wissen, welche Zeichensymbolwertzuordnungstabelle ihr Gesprächspartner intern verwendet.
Oder man sieht die hübsche Flagge L irgendwo auf einem Schiff.
Wenn jemand, der gerade ein Textbuch stabiert,
ist es der Buchstabe L
und weiter nichts
Aber wenn sie eine solche Flagge auf einem blauen Fahrzeug sehen,
öffentlicher Dienst, Wasserpolizei oder so,
dann wird die alle Papiere bereithalten und anhalten,
weil das eben bedeutet, anhalten,
wir möchten an Bord kommen und ihr Schiff durchsuchen oder Ähnliches.
Und die bestimmte Zeichen,
wir können dieselbe Nachricht mit vielen, vielen Zeichen geben.
Also in die andere Richtung ist die Zuordnung auch nicht eindeutig.
Genauso wie die Flagge L auf dem Schiff bedeutet,
anhalten,
wir wollen an Bord kommen
Das Mordezeichen, kurz, lang, kurz,
kurz gegeben, heißt eben auch, ja, anhalten.
Oder wenn da eine Lampe vor sich hinblinkt mit dem Zeichen oder eine Schallfolge,
um genauso gut kann aber auch kurz, lang,
kurz, kurz von einem deutschen See meine Liebeserklärung sein.
Ich liebe dich, weil das eben der Rhythmus ist,
mit dem man sich das Zeichen merkt, ne?
Oder wir können die Zahl 4, je nachdem,
was wir tun wollen, ganz unterschiedlich repräsentieren.
Arabische Schreibweise, römische Schreibweise, spezielle Zeichen,
als Aufzählung, Strichliste von Elementen,
ja,
oder als arathmetischer Ausdruck
Okay, also die Zuordnungssymbolwert oder Symbolbedeutung ist nicht immer eindeutig,
ist jedenfalls keine eins zu eins Abbildung.
Und ich fand die Frage,
was so ein Symbol ist und was es bedeutet wirklich,
nett formuliert in dem schönen Buch Alice in Wanderland.
Also gerade die Frage, Bedeutung von Zeichen ist,
in diesem uralt-Weihnachtsklassiker von Luis Carol
sehr schön beschrieben
Wenn sie die Kinderbücher nochmal lesen,
mit den Augen eines Informatikers, der einer Informatikerin,
werden sie entdecken, dass da viele Grundlagen für unsere Disziplin
vorweggenommen wurden und schon als Zeichen aufgezeigt wurden.
Okay, was sagt denn Alice im Dialog mit Tamti Damtji?
Sie trifft da dieses merkwürdige Wesen und steht absolut nicht.
Was der ihr sagen will. Ja,
genau so ein Problem, Similiotik und unterschiedliche Bedeutung.
Okay, I don't know what you mean by glory,
Alice seid. Haben die Dumpy Smile contemptously? Of course,
you don't to like that you. I mean,
does a nice knock
down argument for you
But glory, doesn't mean the nice knockdown argument,
alles objected. But I use the ward,
have to done this set in a rather scornful touren.
It means just what I should it to me,
night I more not less. Genau. Und das ist,
so ist es eben auch in Reckett. Wir sagen für jedes Zeichen,
was es bedeuten soll, und speichern es, ja.
Also in Reckett sind Symbole, Objekte. Und die sind
genau dann identisch, wenn ihr Name gleichgeschrieben wird.
Das ist erstmal einfach. Wir führen ein Symbol ein,
geben ihnen einen Namen und wann immer wir ihn genau so schreiben,
haben wir dasselbe Symbol.
Kurz und gut, jede Variable,
wie wir sie bisher in Reckett benutzt haben,
ist ein Symbol
Und wir können zusätzliche Namen einführen, schreiben, in Listen
reinschreiben und so weiter. Und das sind Symbole. Aber
die haben erst eine Bedeutung, wenn wir ihr eine zuordnen.
Und wir brauchen nicht erst Variable definieren und so.
Wir können einfach mal so aus Listen und Typto haben,
die Dampftiere im Programm schreiben und haben damit ein Symbol,
haben die Dampte und müssen dann das Bitter aber erklären,
wozu wir das Ding verwenden wollen.
Gut, die Funktion Zimbeln, ein Racket,
ist ein Typ Prädikat und Typ Prädikate sind alle Funktionen,
deren, die einen Wahrheitswert zurückgeben, war oder falsch.
Und die Vereinbarung ist konsistent für alle Systemfunktionen,
wenn der Name einer Funktion mit einem Fragezeichen endet,
dann ist das für uns ein Hinweis,
das ist ein Prädikat und das liefert die Wahrheitswert,
ja oder nein zurück.
Natur der Falls. Und ich möchte Sie bitten,
wenn Sie Funktionen definieren, die Wahrheitswerte zurückgeben sollen,
also die Prädikate sind, denen auch Namen zu geben,
die auf ein Fragezeichen enden.
So,
mit der Funktion, iQV
Da können wir Symbole vergleichen. Die sagen uns, ob zwei Symbole gleich sind.
Und wenn wir Strings haben, können wir daraus neue Symbole erzeugen.
Also wenn ihn im Namen während der Berechnung des Programms basteln sich anstrengend zusammen,
würde ich sagen, ich möchte mit diesen String,
den ich heute abhängig von der Uhrzeit berechnet habe,
meine Variable benennen, okay,
dann verwenden sie String to simple und sie haben ein neues Symbol.
Und wenn sie wissen wollen, von irgendeinem Symbol,
das ihnen übergeben wird, wie der Name geschrieben wird,
dann verwenden sie Symboltes dringen.
Gut, die ersten simpelsten Symbole. Im Moment fragen sie sich bestimmt immer noch,
wozu muss man Variable den Wert kriegt, Symbol taufen?
Ich hoffe, das wird später klar.
Fangen wir erst mal mit diesen einfachen Beispielen an.
Also wir definieren hier eine Variable Reckett und eins,
dann ist Dreckett ein Symbol und es hat die Bedeutung und den Wert der Zahl eins.
Komm, wollen wir gleichberechtigt sein, Java, definieren wir
auch mit eins. So, wir können fragen,
ist Reckett denn ein Symbol und dann kriegen wir False zurück
und jetzt werden sie wahrscheinlich fragen, wieso?
Ich habe doch gerade erzählt, Name ein Zeichen geschrieben,
ist ein Symbol. Wir sind hier schon bei diesem simpelsten
Beispiel an einer Stelle der Feinheit der Auswertung in Reggaed.
Gucken wir noch mal, was hier überhaupt so passiert mit Werten.
Ich habe hier definiert eine Variable Reggaed 1 und das Ergebnis,
was wir dann im Interpret da sehen,
das sollte dieser Pfeilleger andeuten, ist erst mal gar nichts.
Ich habe es hier notiert über das Symbol Bottom,
der undefiniert, der undefinierte Wert.
Reggaed ist so gebaut, solange wir Dinge tun,
die den Zustand ändern, kriegen wir kein Ergebnis zurück.
Den undefinierten Wert
Also es ist eine Definition, ändert den Zustand der Berechnung
und damit Bums haben wir erst mal keinen Wert als Resultat,
damit wir gar nicht erst in die Versuchung kommen,
der Definition in Ausdrücke einzubauen und damit weiterzurechnen.
So, also unsere beiden Definitionen hier haben ein Symbol,
Reckett eingeführt, ein Symbol Java eingeführt und denen Werte zugeordnet.
Und jetzt fragen, wer ist Reckett ein Symbol und kriegen
falsch zurück. Ich hoffe, das Resultat überrascht sie.
Was hier bei der Auswertung passiert, ist das Folgende.
Wir rufen eine Funktion auf
Und ich habe das letzte Mal schon gesagt, wenn wir
Argumente an eine Funktion übergeben, Werte drecket sie aus.
Und gibt es Ergebnis dieser Auswertung an die Funktion?
Was ist hier passiert? Das Symbol Rackett ist ausgewertet worden
zum Wert 1. Und wir wenden dann die Funktion Symbol
an auf die 1. Und natürlich ist eins kein Symbol.
Also das ist, deshalb kriegen wir hier Trut zurück.
Wenn wir dagegen sagen, Quotierung, Rackett,
wir meinen dieses Zeichen, und fragen,
ist das ein Symbol,
dann kriegen wir Trut zurück
Also wir müssen ab jetzt bei allem, was wir tun,
höllisch aufpassen, meinen wir den Wert, den eine Variable hat,
oder meinen wir die Variable an sich?
Die Variable an sich, Reckett ist ein Symbol.
Und das habe ich durch Disquittierungszeichen gemacht, das stoppt die Auswertung.
Und hier wird an die Funktion Reckett das Ausgewertete Argument übergeben.
Stellen Sie für einen Moment vor, der hätte gestanden,
Klammer auf, plus eins Reckett, dann wäre es klarer,
dann wird nämlich gefragt, ist das, was bei der Berechnung rauskommt,
ausgewertet, der wäre zwei, ein Symbol.
Und dann ist klar, dass der Volks rauskommt.
Aber der Wert einer Variablen ist eben Kann viele Typen haben und
wenn es die Eins ist, ist es kein Symbol.
Gut, fände ich einfach nur das Symbol hier hinschreibe.
Wird es ausgewertet und wir kriegen den Wert oder seine Bedeutung und das ist die Eins.
Wenn ich es quotiere, kriege ich das Symbol selbst zurück.
So, wenn wir hier fragen, ist Tracket gleich Java,
kriegen wir wahr raus, warum?
Weil eben Recke zu Eins ausgewertet wird,
Java wird zu Eins ausgewertet und diese beiden Werte sind gleich.
Also EQV sagt prima, ja, selbe, selber wert.
Wenn wir Das Symbol an sich vergleichen, den Namen.
Quotetreckett mit Quotet, ja, da kommt Falz raus,
weil die sind eben nicht gleich geschrieben, also sind sie verschieden.
Wenn wir jetzt unser Symbol hernehmen,
das Symbol selbst oder nicht etwa seinen Wert und geben es
an die Funktion Simble to String,
kriegen wir einen String raus und können dann zeichenweise uns den Namen des Symbols vornehmen und auswerten und gucken,
was ist es denn textuell?
String to Simble gibt uns ein neues Symboljava.
Und wenn wir dann fragen, ob das was Simble to
String uns liefert, ob das Symbol ist.
Na, prima True. Das ist eins. Gibt es
zu dieser Seite hier Fragen? Gucken wir mal weiter. Also,
warum machen wir diesen ganzen Zirkus anders als in imperativen Sprachen?
Können wir in Recket eben die Symbole, die Variablen,
selbst nehmen? Und in die Datenstrukturen einbauen und uns zur
Laufzeit neue Variablen erzeugen und mit ihnen weiterarbeiten? Also,
wir können all unsere Symbole, die wir haben,
in eine Liste reintun oder in einen Vektor und damit loslegen und versuchen,
eben eine Analyse zu machen
Und das funktioniert aber nur,
wenn wir tatsächlich jetzt auch eine Notation,
eine Syntax haben, bei der wir unterscheiden können,
ob wir das Symbol selbst oder den Wert nehmen,
der ihm zugeordnet wurde.
Ja. Wenn man jetzt ein Symbol hat,
also zum Beispiel so wie Frank Your Simple,
man will jetzt den Wert haben, gibt es dafür auch eine Funktion.
Wir haben jetzt ein Symbol angeführt mit String to Simple und
dann kriegen wir erstmal das Symbol und das hat noch keinen Wert.
Wir müssen eben noch einen Wert zuweisen. Und das kann man
eben machen mit der Funktion Set und Mit imperativen Sprachelementen.
Aber wir werden sehen, dass wir ziemlich viele Symbole an sich verwenden,
ohne dass wir denen überhaupt einen Wert geben.
Das ist, meistens brauchen wir sie nur, ohne einen Wert.
Werden wir an Beispielen sehen? Können Sie in der letzten Reihe hören,
wenn die Mantiforen etwas fragt oder die Frage verstehen?
Dann muss ich in Zukunft sie wiederholen.
Okay, gut. Also die Frage war,
ob es Funktionen gibt, um den Wert einer direkt gefragten.
Direkt an direkt eingeführten Symbols zu verwenden.
Wir können es einfach mal ausprobieren
Wenn das Dr. Reckett hier vorkommt. Okay. Aha.
Hier ist leider das Fenster für Dr. Reckett zu klein.
Schade. Das wird nix. Gut,
müssen wir hinterher ausprobieren, ob das geht.
Und ich muss meinem Rechner mal ein bisschen Strom geben.
Irgendwas habe ich bei all der Technik
doch noch vergessen
Gucken. Der hier. Ja, da kommt es drum.
Also auch in der natürlichen Sprache, speziell in deutsche Verbänden,
wissen wir, dass wir einen Unterschied machen müssen,
ob wir ein Wort meinen oder die Bedeutung des Wortes.
Also richtig schön, pandites Beispiel aus der Feuerzangenbohle,
Adventszeit wird es wieder im Avathon aufgeführt hoffentlich oder im Unikino
Wenn da der Lehrer Bömmel sagt, der Pfeiffer ist ein
frecher Lümmel, dann meint man eben den Hans Pfeiffer,
der da so gemeint zu seinen Lehrern ist.
Aber wenn man, das war die Antwort auf die Frage
an Hans Pfeiffer, wie schreibst du dich denn?
Und er hat gesagt, mit 3F, ja,
nicht mit 2. Und da geht es eben darum,
wie wird dann Name geschrieben, Dedikto?
Ich sage etwas Dedikto, ich meine das Symbol selbst und das,
was bezeichnet wird. Und in der anderen Frage,
der Pfeiffer ist ein frecher Lümmel,
dann meinen wir das, was Pfeiffer bezeichnet,
das Objekt selbst
Und in Reckett lösen wir das Problem eben, dadurch was,
also dass wir in Deutschen durch die Anführungszeichen machen,
dass wir diese Standardfunktionen Quote verwenden, quotiere.
Das war das kleine Häkchen, was ich angezeigt hatte.
Also sie können Quote direkt als Funktion der Special Form hinschreiben oder eben
das Säkchen machen. Wenn wir irgendetwas quotieren mit diesem,
der Quotefunktion oder Häkchen,
dann meinen wir wörtlich den Text und nicht das,
wozu es ausgewertet wird,
wir hindern Recke dran,
dieses Argument auszuwerten und es gibt uns
das Symbol weiter
Abkürzend ist es eben Disquittierungszeichen. Bitte nicht verwechseln,
also das einfache Anführungszeichen, nicht verwechseln mit den Akzenten für
französische Vokale, die sie auf ihrer Tastatur auch haben.
Gut, fangen wir mal an, hier Kaskaden von solchen Quartierungen zu machen.
Also hier wollen wir mit Define eine Variable einführen, Pi.
Und da wird sie noch nicht quotiert,
weil die Feinde ist eine Special Form,
die das erste Argument nicht auswertet, sondern wörtlich nimmt.
Wir brauchen was nicht zu quotieren. Und die Zahl hier,
die wollen wir aber als Zahlen Wert nehmen,
also muss sie nicht quotiert werden,
dann können wir fragen Pi
Die bekannte 3, 1, 4 und so weiter.
Wenn wir stattdessen die Auswertung blockieren und dieses Kurzzeichen davor machen,
kriegen wir den Namen Pete zurück.
Das wird eben ausgewertet, disquotierte Pee zu dem Symbol Pee selbst.
Wenn wir jetzt übervorsichtig sind und das viermal quotieren,
was das Ergebnis, wir kriegen die Dreifachquotierte Variable zurück.
Werden wir später auch noch brauchen. Und hier in arathmetischer Ausdruck
wird ausgewertet, wir kriegen die vier. Wenn wir ihn quotieren,
kriegen wir tatsächlich diesen Ausdruck und dann können wir loslegen solche arathmetischen Ausdrücke,
in Listen einzubauen, in Datenstrukturen und Teilausdrücke zu nehmen und sie anders zusammenbauen.
So, ein Name, den wir eingeführt haben,
der noch keinen Wert hat, wenn wir inquotieren,
können wir ihn einfach so als Text verwenden und das evaluiert zum Namen des Symbols,
das noch keinen Wert hat.
Dass er keinen Wert hat, merken wir daran,
wenn wir dem Recket sagen, werte mir mein Auto aus.
Das hat keinen Wert und wir kriegen dann die bekannte Fehlermeldung.
Wir haben eine unbekannte Variante. Unbekannte Variable referenziert. Wenn
wir irgendetwas Quotiertes haben, Gibt es Standardfunktionen in Recke?
Das ist genau die Funktion, die uns den Entörpritt des Programms auf,
des Systems aufruft. Die heißt, die Well evaluiere.
Und wenn wir zum Beispiel hier sagen, evaluiere mir,
das Symbolpie, ja, dann kriegen wir den Wert zurück,
an den Pi gebunden wurde.
Oder wenn wir irgendwo einen Programmtext zusammengebaut haben und quotieren das Ganze,
plus 2, 2 und sagen, werte es mir aus?
Das können wir aus irgendeiner Datenstruktur geholt haben, wird in anderen Teilen des Programms zusammengebaut.
Also unsere erste Begegnung mit automatischer Code-Erzeugung, ja.
Dann wird Eval das unquotiert neben diese erothmetischen Ausdrucks sehen und ihn auswerten, ja. Gut,
und das ist eben das, was so,
das Eval macht, das ist schlichtweg der Kern,
jedes Lisp oder es geben Recket-Systems, der Inteurbritter-Top-Level,
der hat diese sogenannte Read-Eval-Print-Loop, also Listenausdruck,
ruft Eval auf, die Funktion,
die wir eben gesehen haben, drückt das Ganze,
zeigt uns das Ergebnis an und wartet wieder auf
die nächste Eingabe,
Und in Reckett haben sie ihn ausgefeilte Art,
wie sie dieses Evel parametrisieren können, um Auswerte,
Kontext, Bindungsumgebung weitergeben und so weiter.
Das ist die Basis für alle Spracherweiterungen, die man eingebaut hat. So,
was das Ganze jetzt ein bisschen spannender macht, ist,
dass wir nicht nur einfache Bedeutungen von Wert und Symbol haben können,
sondern ganze Ketten aufbauen können.
Also wenn der Wert eines Symbols wieder ein Symbol ist und
dessen Wert ist wieder ein Symbol und dessen Wert ist wieder ein Symbol,
dann können wir auf diese Weise
eine ganze Referenzkette aufbauen
Und wir können also Symbole betrachten als Namen von abstrakten Werten,
die wir von irgendwoher in unserer Berechnung erzeugen.
Also Symbole referenzieren andere Größen und diese anderen Größen können wieder Symbole sein.
Hier führe ich eine Variable oder einen Symbolname ein und gebe dem
den Wert Susi. Und dann der Name von Name,
dem sage ich, sei mein Symbolname.
Wenn ich Name von Name einfach hintippe, Das kriege ich.
Das, was jetzt die Vereine gemacht hat, nahm er zurück.
Und wenn ich aber sage, Ivel,
evaluierend Name von Name, kriege ich Susi zurück.
Warum? Ivel ist eine hunznormale Funktion. Ja,
sie kann zwar viel, aber es ist eine hunznormale Funktion.
Also geschieht die Auswertung eines Ivels so. Das,
was immer das Argument hier ist, wird es erst mal ausgewertet.
Und was da rauskommt, wird an Ivel übergeben.
Sodass Auswerten von Nahmützen, von Name,
führt zum Symbolname und Ivel wird sein Name auswerten.
Und da haben wir unsere Susi wieder hier
Auch diese Geschichte ist vorweggenommen in Alice, in Wanderland,
in dem Dialog von unserer Alice mit dem Ritter.
Ja, das war genau so eine konfuse Geschichte.
Ich lasse es mal den Mac vorlesen, denkst du,
und ich meine Stimme, einen Moment.
Ja, ich sag euch. Das ist ein weder Versorgung
. Das ist ein weder Versorgung
Dass wir helfen zu uns merken. Ja, ja,
ja, ja, ja, ja, seine Sohn ist voll.
Er war nicht gerettet, versaufen. Oh,
die gerettete Art. Glas mau, nur das Böbeln.
Das sagt ihr, es traut höchst, du nimmst.
Da greifst du auch nicht mehr, wie es kommt,
im Büro. Nee, meine Herren, mein Herren,
du warst beides so ein Kathleten, du warst von mir too that.
Ah, night say, too too, too that,
too. Okay, was haben wir hier als Geschichte?
Der Name vom Name, vom Name, vom Name.
Das ist so ungefähr das Fundament dessen,
was wir auch mit Symbolen im Racket machen können.
Und vielleicht können wir das mal versuchen, Kleines Racket-Screen-Programm zu machen.
Also, the name of the song is a sitting,
the song is a sitting on the gate and the tunes my own invention.
Und das heißt Waste and means. Also definieren wir mal einfach
Waste and minz, ist der Name von Sitting on Agate.
Und Waste and means is call the Aged Man, ja?
Und Headdogs Eyes ist wiederum der Name von The Aged Aged Man.
Und wenn wir das jetzt auswerten lassen,
einfach hier in Recket reintippen, kriegen wir The Aged Aged Man zurück.
Spielen Sie es mal nach beim, ich glaube,
man kann es erst beim zweiten oder dritten Mal lesen,
wie sich die Sachen alle referenzieren.
Jetzt können wir hingehen und die Age-Age-Men oder Waste and Means auswerten lassen.
Und wir kriegen unser PRU, Ivar Headdogs Eyes, die letzte Definition.
Headdogs Eyes wird einmal ausgewertet, das ausgewertete Argument an Evel übergeben.
Das ostgewertete Argument ist die Age-Aged Men. Und wenn wir
den mit Ivar auswerten, kriegen wir Base and Means zurück.
Und wenn wir das Ganze nochmal auswerten lassen,
haben wir das Assitting on the gate.
Und tatsächlich die letzte Bedeutung rausgekitzelt aus diesem Dialog
Gut, wir verwenden es jetzt dafür,
ganz praktische Programmbeispiele, also eine der ersten Übungen,
die man so in jedem Lissbuch findet, ist,
wir versuchen mal auszurechnen,
was die Seitenlängen von Dreiecken sind und dann zu sagen,
habe ich ein gleichzeitiges schiefwinkliges und so weiter.
Also wir werden ein Racket jetzt verwenden und symbolisch schlichtweg,
um symbolische bezeichnet zurückzugeben, als Pferd einer Berechnung, ja.
Erinnern Sie sich erst erste Jahr, wo sie mit Dreiecken in der Schule agiert haben.
Da gab es eben all die Fälle, wenn sie drei Seiten Längen gegeben hatten.
Es ist gar nicht möglich, weil daraus ein Dreieck zu machen,
weil die Seiten ab plus B zusammen kürzer sind, als die dritte Seite.
Also geht nicht. Oder A, B und C,
alle drei Seiten sind leicht lang, dann hieß das Ding gleichseitig.
Ja, oder zwei Seiten sind gleich lang,
dann heißt es gleich schränklich und alle drei sind unterschiedlich dann.
Dann nennen wir es schiefwinklig. So. Und wir wollen jetzt eine kleine Funktion schreiben,
da geben wir einfach drei Werte, A, B,
C und es soll uns sagen,
welche Art von Dreieck wird denn da vor uns haben.
Und wir fordern, damit wir die Funktion nicht zu kompliziert schreiben müssen,
dass sie aufgerufen wird mit dieser Zusicherung, die Seitenlänge A soll kleiner,
gleich B sein, soll kleiner, gleich zäh sein.
Wir setzen voraus, dass die Argumente sortiert übergeben werden.
Ja. So. Und dann schreiben wir mal. Unsere erste Racket-Funktion,
die ein bisschen mehr tut, analysiere A, B,
C. Und fürs erste schreibe ich mir einfach die Zusicherung hier oben als Hauptkommentar rein, ja.
Diese drei Simicolands heißt ein Kommentar mit Bedeutung, also A,
kleiner, gleich B, kleiner, gleich C.
In so zwei oder drei Doppelstunden werden wir da hinkommen,
wie wir solche Zusicherungen so in die Funktionen reinschreiben,
dass sie automatisch getestet werden beim Aufruf der Funktion.
Gut, dieses Analyse soll jetzt ausgeben,
dass es kein Dreieck ist, wenn A plus B kleiner ist als C.
Ja, dann geht es eben nicht. Wenn A gleich zäh ist,
soll es sagen, das ist ein gleichzeitiges Dreieck.
Oh, Moment, woher weiß es jetzt,
dass B genauso lang ist wie A und C?
Ja. Ja, durch die Zusicherung. Genau, ne?
Ja, sie ist gleich so groß wie das inzwischen nicht
ja auch
Also, wem muss größer gleich a sein und kleiner
gleich zäh und wenn abends hier gleich sind,
bleibt dem armen B nichts anderes übrig, als gleich groß zu sein.
Genau. So, als nächstes testen wir es denn a gleich b,
dann sagen wir, es ist gleichschenklich oder wenn B gleich zäh ist,
sagen wir auch gleichschenklich und in allen anderen Fällen ist es eben schiefwinklig.
Können wir so aufrufen und sie sehen, wie wir hier die Symbole verwenden.
Wir verwenden sie hier schlichtweg erstmal als Ex-Konstanten.
Dann bei der Gelegenheit noch eine kurze Kurzer Einschub,
ich hatte bisher nicht das Case-Statement angeführt, das ist auch sehr praktisch,
wenn wir Fallunterscheidungen machen müssen, mit diskreten Werten.
Und dafür nimmt man gerne Symbole. Können wir das so schreiben,
erstmal Klemmer of Case, hier einen Ausdruck rein,
der getestet werden soll, hier schreiben wir eine Liste von Werten rein.
Und wenn der Ausdruck einen Wert ergibt,
der in der Liste dieser Schlüsselwerte auftaucht,
dann wird diese Bedingung ausgewertet,
und davon können wir ziemlich viele in diesen eckigen Klammern
hier reinschreiben
Ach so, ich hatte das nur in meiner Übungsgruppe erwähnt.
Ich verwende immer die eckigen Klammern für Namenswertpaare. Das ist in Record
jetzt erlaubt. Früher gab es nur eine Sorte Klammern,
aber inzwischen haben sie ein paar mehr Formen zugelassen,
einfach damit das Ganze gelockert aussieht.
Und man kann damit die Struktur deutlich besser machen. Der Editor prüft nicht nur,
ob wir eine öffnende Klammer zumachen, der prüft auch,
ob die Klammer von derselben Art ist, die geöffnet wurde.
Und es gibt uns ein bisschen mehr Sicherheit.
Also in alten Büchern werden sie immer noch finden,
dass alles rund weg, egal weg,
Den normalen Rundenklammern gemacht ist, aber sie sind herzlich eingeladen,
diese neue Syntax zu versuchen mit den eckigen Klammern.
Gut, und wir wollen jetzt einfach mal eine Funktion schreiben,
um das Peace zu testen, die uns sagen soll,
welches Geräusch denn ein Tier von sich gibt.
Wir haben, also hier dem, das Symbol,
Tier und je nachdem, zu welchem Wert es evaluiert,
wenn der Wert Hund ist, dann soll wohl Wort zurückkommen.
Wenn der Wert Katze oder Tiger ist,
dann wollen wir ein nettes Mujau hören. Und ansonsten,
wenn der Wert Q ist, soll Mut zurückkommen.
Okay, unser Tier war in dem Fall der Tiger,
Gott. Also das Case ist auch ganz praktisch. Okay,
kennen Sie auch aus Java natürlich, ist immer dann sinnvoll,
ein statt des Konditionals, wenn Sie Werte vom gleichen Typ haben.
Das geht immer gut im Case, ja?
Also in dieser Reihenfolge werden die Ausdrücke der Reihe nach mit
verschiedenen Schlüsseln verglichen. Und wenn der Vergleich erfolgreich ist,
dann wird der Wert zurückgegeben und verglichen wird mit der Funktion EQV.
Wir müssen wissen, mit welcher Funktion ein Gleichheitstest gemacht wird.
Die letzte Klausel kann geärzt sein
Also, wenn wir testen wollen, ist eine Zahl eine Primzahl,
ja, dann hier eben mögliche Zahlen, wo wir sagen,
Primzahl und bei anderen sagen wir es zusammengesetzt.
Oder wenn hier etwas, wenn die Zahl,
die hier rauskommt, nicht in der Liste auftaucht,
kriegen wir als Resultat einen undefinierten Wert zurück und das hängt von der Implementation ab,
die sie vor sich haben,
ob das fatal ist oder nicht.
Racket satt mich wieder undefinierte Wert, definiert wird, ja.
Also, der kann schlichtweg repräsentiert werden durch einen Programmabbruch
Testen, ob etwas ein Vokal oder ein Konsonant ist oder irgendein Zwischending.
In dem Fall, gut. Gut. Zu den Namenskonventionen
. Ich hatte schon ein paar angesprochen, weil wir eben
nicht gezwungen sind durch die Syntagsanalyse der Sprache zur Typdefinitionen einzuführen.
Wir könnten es, aber meistens werden wir das Abkürzen schreiben.
Sollten wir die Programme so schreiben, dass implizit klar ist,
welcher Typ das ist. Also alle Funktionen,
die Wahrheitswerte zurückgeben, denen sollten wir Namen geben,
die mit dem Fragezeichen ändern
Und das Fragezeichen sollte nur dann verwendet werden,
wenn wirklich garantiert nur einer der Werte True oder False zurückkommt.
Namen von Funktionen, die den Zustand alles Objekts verändern.
Die sind in Reckett durchweg bezeichnet mit dem Ausrufezeichen dahinter und sagen,
Achtung, imperative Sprachelement, alle Sicherheitsvorkehrungen sind weg.
Ja, wir können uns nicht mehr auf Bezugstransparenz verlassen.
Wenn Sie je so eine Funktion schreiben,
bitte geben Sie auch einen Namen,
die ein Ausrufezeichen hat,
als Verhandlung an alle Benutzer
Und für Konversionsfunktionen hat sich allen gebürgert, dass einfach dieser Pfeil im Namen auftaucht,
also Character to Integ, Jalistus dringen und so weiter.
Dann ist klar, von welchem Typ,
zu welchem Typ sie konvertieren, das ist auch völlig klar,
was der Typ, der das Argument ist,
und was der Typ des Resultats ist.
Okay, jetzt wollen wir anfangen, obwohl unser Thema eigentlich Funktionen,
Funktionale Berechnungen sind, uns ein paar Datentypen anzuschauen,
damit wir eben reichhaltigere Berechnungen machen können.
Eine ganz kleine Reinblick oder Erinnerung nochmal zum Auffrischen,
was den Datenabstraktionen eigentlich ist. Und dann gucken wir uns
ein paar elementäre Datentypen an und springen sofort zu den Listen,
weil das das Neue ist, was Recke zu bieten hat.
Ja? What? Also das Spielchen kennen Sie ja nur
schon seit zwei Semestern von den Operationen und Implementationen durch Methoden in Jaava.
Man kann nicht alles auf alles anwenden,
wenn der Typ nicht stimmt, haben wir ein Problem.
Oder wenn die Klasse nicht stimmt, genauso ist es im Recket eben auch.
Wir haben Funktionen, die eben nicht auf alles anwendbar sind.
Also wenn wir hier Einen Symbol Apfel einführen und geben dem den Wert eins,
dann hat es den Typ einer Zahl und wenn wir ein zweites,
zweite Variable haben, Birne und geben der als Wert eine Liste,
dann kriegen wir eben einen komplett anderen Typ heraus.
Wir können, das dritte ist ein Wahrheitswert nehmen, ja,
Apfel plus Apfel geht gut, aber Apfel plus Birne eben nicht.
Und es gibt die typische Fehlermeldung, die man auch bei
Typfehlern erwartet wurde. Reckelt wird es sagen,
als zweiter Typ wurde eine Zahl erwartet, aber Leute,
ihr habt mir dies hier gegeben
Und das andere Argument nebenbei bemerkt war, ein Zimmer,
darüber beklage ich mich nicht. Also so in der Art
sind die Fehlermeldungen. Was ist jetzt mit einem Datentyp gemeint?
Also ich habe es ganz nett beschrieben gefunden im Buch von
Niklas Wirth, als er die Sprache Oberen eingeführt hat.
Als Datentyp haben wir das Universum der Werte. Wenn wir uns vorstellen,
die riesige Menge aller denkbaren Werte,
die in einer Programmiersprache ausgedrückt werden können, dann können wir versuchen,
da Ordnung reinzubringen, eben über die Anwendbarkeit von Funktionen.
So, wir haben hier ein Racket zum Beispiel in diesem
riesigen Universum
Werte vom Typ lüste. Wir haben Werte vom Typ Frisida Funktionen,
wir haben Strings, wir haben Zahlen und so weiter.
Und wir bringen Struktur hinein, indem wir sagen, alles,
auf das dieselbe Gruppe von Funktionen angewendet werden kann, ist vom selben Datentyp.
Zum Beispiel, das ist eben der Datentyp Zahl,
weil wir eine numerischen Funktionen anwerten, senden können,
dass der Datentyp liste, weil wir dort alle Listenfunktionen anwerten,
wenden können und so weiter.
Also wir machen damit wieder eine Abstraktion hier bei der
funktionalen Abstraktion gemacht hat, haben über Strukturgleichheit von Berechnungswerten,
haben wir jetzt eine Typgleichheit eingeführt über Gleichheit der Anwendbarkeit von Funktionen.
Wir kriegen damit Gruppen von Werten, die äquivalent sind,
indem sie gleich verwendet werden können. Gut, und damit
kann man eben zu der typischen Definition für Datentyp kommen.
Datentyp, oder wir sagen kurz Typ,
ist die Menge aller Werte, der eine konstante angehört.
Oder die von einer Variablen oder einem Therm angenommen werden können.
Und die bestimmt die anwendbaren Funktionen auf diesen Werten. So,
und die typischen Typen, die wir in weggetan,
sind für sie alles alte Bekannte im Wesentlichen.
Wir haben elementare Typen, die sie aus Java genauso kennen,
Zahlen, Typ Namber, Wahrheitswerte, Bullyen, Zeichen.
Wir haben strukturierte Typen, das sind Listen, wir haben Sets,
Mengen, wir haben Zeichenketten, wir haben Verbunde.
Wir haben zusätzlich, habe ich jetzt eine Liste nicht aufgeführt,
weil das so ein Thema für sich ist, auch Objekte.
Racket hat ein ausgefeiltes Objektsystem, das eins zu eins von Java abgeschrieben wurde.
Insofern finde ich es nicht sonderlich spannend. Wir haben Funktionstypen,
wo wir sagen können, wir haben Funktionen, die ein Argument nehmen,
auf ein Argument abbilden oder, oder, oder.
Dann Polymorphe-Typen, das sind eben Typen, die auf die verschiedene Funktionen anwendbar sind, vielgestaltige.
Das Wichtigste davon ist, die leere Liste, ja,
die ist gleichzeitig eine Listin, der keine Zahlen sind,
eine Listin, der keine Objekte sind, ist eine Liste,
in der keine Autos sind, eben eine Liste,
in der alles nicht drin sein kann.
Und damit hat sie jeden Typ für Typ für spezifische Listen der generische Typen,
das sind eben Klassen von Strukturgleichen Datentypen und mit denen werden wir meistens arbeiten.
So, wir haben die konkreten Datentypen,
das sind die Elementaren und wir können Programme sinnvoll strukturieren,
indem wir eigene abstrakte Datentypen einführen, indem wir eben genau das angeben,
die Menge der Werte und die Menge der Operation.
Ein Datentyp hat neben der Verwendbarkeit noch einen anderen Aspekt,
nämlich wie viel Platz brauche ich, um ein Element dieses Typs zu repräsentieren?
Das ist die Mächtigkeit oder Kardinalität und Das Minimum, was wir für jeden Daten,
Wert eines Datentyps brauchen, ist der Logarithmus der Kardinalität.
Weil das bestimmt, wie viele Bits wir brauchen,
um einen Wert zu repräsentieren. Okay. Dann,
ja doch, das muss ich auch noch erwähnen,
die Programmiersprachen unterscheiden sich jetzt eben auch darauf hin,
ob das Typsystem, was wir haben, latent oder manifest ist.
Manifest ist das, was sie aus Java kennen. Das,
was sie dauernd gemacht haben, ehe sie überhaupt irgendetwas tun können,
müssen sie eine Klasse schreiben und genau sagen,
wie die Werte aussehen, Eben die Objekte, die dieser Klasse angehören,
das ist eins zu eins festgelegt und für jede Operation,
muss auch klar sein, was die beteiligten Typen sind.
Es wird zur Übersetzungszeit geprüft. Und ein datentes Typsystem ist das,
was wir in den meisten funktionalen Programmiersprachen haben,
in Recket oder kommen Lisp und Skim, also eigentlich all diesen Programmiersprachen,
aus der Lisp-Familie haben wir ein latentes, dynamisches Typsystem.
Es wird erst in dem Moment, wo sie das Programm ausführen, geprüft,
Sind die Typen kompatibel zwischen den Funktionen oder den Werten, die sie übergeben?
Statische, latente Typosierung haben sie in all den funktionalen Programmiersprachen,
die so auf Miranda Häskel zurückgehen.
Da wird zur Übersetzungszeit des Problems schon versucht abzuleiten,
was der Typ sein kann. Es ist nicht schlecht,
sie erfahren beim ersten Übersetzungsversuch, ob sie Typfehler gemacht haben,
ohne dass sie Typen deklariert haben. Also wenn sie eine Funktion schreiben,
sagen aber nicht, was die Argumente sein sollen,
aber im Rumpf der Funktion werden die Argumente addiert,
dann macht eben das Typ Kalkül,
was in Miranda Häskel drin ist, leitet daraus ab,
dass die Argumente der Funktionen vom Typ Zahl sein müssen,
ineinander.
Ja, und sie kriegen schon zur Übersetzungszeit eine Fehlermeldung,
wenn es nicht gut gehen kann.
In Racket Skim und so weiter wird nicht einmal das gemacht,
erstens, weil es nicht notwendigerweise eine Übersetzungszeit gibt
Das Programm wird schlichtweg im Interpritte ausgeführt,
also die Übersetzungszeit entfällt schon mal, außer sie bestellen es,
um Effizienz, um den Kurs zu optimieren,
dann können sie die Programme übersetzen und sie schneller kriegen.
Und der Zwang, dass man nur solche Funktionen definieren darf,
bei denen das Typkalkül mitkommt, schränkt sich unglaublich ein,
was sie überhaupt erlaubt sind zu sagen und zu denken im Programm.
Es ist im, Miranda, also viele die simpelsten Beispiele,
die in jedem Lisbuch vorkommen, wie zum Beispiel eine Funktion,
die die Schachtelungsliste eine Schachtelungstiefe einer Liste bestimmt,
können sie schlichtwegseln taktisch in Miranda nicht schreiben,
Weil das Typ Kalkül sagt, Moment,
ich habe als erstes Argument jetzt eine Liste der Schachtelungstiefe 6 und vorher war es 7.
Das ist ein Typfehler, die Schachtelungstiefen sind verschieben.
Und die wollen sie dann rekursiv mit derselben Funktion sich herunterhangeln,
um festzustellen, was die Schachtelungstiefe ist.
Es geht nicht. Also es gibt viele Funktionen,
die sinnvoll sind, wo aber man kein Typ Kalkül programmieren kann.
Und das ist dann auch verboten. Also sie müssen wirklich wissen,
weil statischer, latenter Typisierung,
wo das Typ Kalkül mitkommt oder nicht, um zu wissen,
was sie schreiben und denken dürfen.
Und deshalb ziehe ich
diese Familie von Sprachen vor
Gut, sie latente Typisierung ist dynamische oder wird wie Chiping
genannt und das ist eben das,
was wir hier in diesem Semester genießen werden und das Manifeste ist das,
was sie eben in Java haben, ne?
Gut. So. Über die Typen können wir eins festhalten,
das ist versprochen und garantiert. Wenn wir Objekte haben,
können wir sicher sein,
dass egal welches Objekt wir erzeugt haben oder konstruiert haben,
es nicht mehr als eins dieser Typ-Prädikate erfüllt
Ja. Also wenn es Bulien ist, dann ist es
auf keinen Fall Symbolcharakter weg, der Prostiter und so weiter.
Wenn es number ist, ist es keins der anderen.
Es kann vorkommen, dass sie etwas konstruiert haben, was keins davon erfüllt.
Ja, aber hier steht hier auch nur mehr als eins.
Also heißt nicht genau eins, sondern mehr eins. Es gibt Werte,
für die keins dieser Prädikate zutrifft und die haben dann eben einen anderen Typ.
Aber dieser Typen hier sind es jungt. Generische Funktionen,
sie wissen von den generischen Klassen in Java,
was damit gemeint ist und ähnlich verhalten sich eben auch die generischen Funktionen.
Zum Beispiel die Identitätsfunktionen, die ist ein Polymorph und vielgestaltig. Und wenn wir sagen,
Identität von irgendetwas soll, dass irgendetwas selbst sein bildet,
also alles auf sich selbst ab, dann haben wir damit eine Funktion,
die eben abwilde Zahlen auf Zahlen, Autos auf Autos,
so Listen auf Listen und so weiter.
Ja, das heißt, je nach Verwendung des Arguments hat die Funktion einen anderen Typ.
Und das ist mit generisch gemeint. Und wir werden im Wesentlichen,
also in diesem Semester über mit solchen generischen Funktionen arbeiten,
das ist genau der Grund,
weshalb wir Die mangelnden Typdeklarationen als Feature nutzen werden und nicht als Bug oder Makel.
Sie können und sind gehalten, Kommentare reinzuschreiben in ihre Funktionen,
die sagen, von wonach, wo sie abbilden.
Und da lohnt es sich, sich an die Notation zu halten,
die in den Theoriebüchern zum Funktionalen Programmieren verwendet werden.
In Miranda Racket könnten Sie das als Typ-Spezifikation wirklich reinschreiben.
In Racket geht es erstmal nur als Kommentar bei dem,
was wir bisher besprochen haben.
Also die Identitätsfunktion bildet eine Typ
Typen vom Typ Alpha, das ist eine Typ Variable-App auf andere Typ Variable,
während wir hier feste Typnamen hätten, ja?
Oder wir können sagen, die Vergleichsfunktion, Äquel, die bildet,
egal was der Typ von A und von B ist,
ist immer ab auf den festen Wert Bullyen.
Ja? Gut, okay, das braucht mir jetzt nicht
so wichtig. Ich möchte schnell mich an die Stelle hangeln,
wo wir tatsächlich über konkrete Datentypen sprechen.
Also wir haben in den Beispielen schon gesehen,
wir haben Datentypen-Number-Komplex, Real für reelle Zahlen,
wir haben Rationalzahlen,
wir haben Integerzahlen
Für jeden Zeilentyp gibt es auch noch ein eigenes Typ-Prädikat.
Also bevor wir rechnen, können wir prüfen, ist es
eine komplexe Zahl, in ihre elle Zahl und so weiter.
Der Wert der größten Intiter-Zahl ist Implementationsabhängig. Also das ist in Rackett
offen gehalten, so viel Speicher, wie sie direkt gegönnen.
So groß können ihre Intiter-Zeilen letztendlich werden.
Von der Sprachdefinition ist jedenfalls keine Beschränkung vorgesehen.
So, Verwendung von Typ-Prämpen, die Karten.
Wenn wir sowas schreiben an 3 plus 4 mal e,
dann ist das die komplexe Zahl mit dem Real Alter 3 und imaginär Teil 4.
Und natürlich das Typ Prädikat wird uns sagen, wahr?
Oder die natürliche Zahl 3 ist aber,
für die ist komplex, aber auch wahr,
weil eben natürlichen Zahlen eine Teilmenge der komplexen Zahlen sind.
Und 3 ist auch eine reelle Zahl.
Und wenn der imaginär Teil einer komplexen Zahl null ist,
dann wird das Prädikat Triel sagen, okay,
ein Verständnis eine reelle Zahl.
Diese Notation führt exakte Zahlen ein, mit dem wir
ohne Rundungsfehler weiterrechnen können und auch die sind DL.
Wir können Rationalzahlen schlichtweg schreiben, indem wir Zähler durch Nella
angeben und haben damit exakte Rationalzahlen.
Und kriegen dann eben auch den Vertrut zurück. Und dasselbe
gilt eben für Intitscherwende. Ganze Zahlen haben. Standardoperationen auf Zahlen,
da gucken Sie am besten auch in Ihre Recke Tankbuch rein. Übrigens,
wir haben noch von dem gedruckten Revised Report on Recket
das Handbuch noch ein paar Exemplare rumliegen,
wenn Sie das nächste Mal in Ihre Übungen kommen,
können und Sie möchten so viel Papier mit sich rumschleppen,
können sich gerne noch
das gedruckte Handbuch abholen
Also fünf vielleicht noch 20 Stück da oder so. Gut,
die Standardoperationen sind erstmal größer, kleiner und die Vergleichsoperatoren,
die muss man ja haben, plus mal durch auch.
Dann haben wir verschiedene Arten, wie wir ganz zahlig dividieren,
Quotien, Remainder, Modolo, probieren Sie mal aus,
für sich das unterscheidet Tim sichtlich das Divisionsristes.
Maximum Minimum, für Rationalzahlen können wir noch Zähler und Nenner
fragen. G-C-D, Greatest kommen an die Wüste,
also wir können größten gemeinsamen Teile raussuchen
CM liest Common Meen, kleinste, gemeinsames Vielfaches.
Das geht eben, weil wir mit exakten Zahlen rechnen.
Ja, abschneiden und runden. Floor and round and druncate
. Oder aus einer reellen Zahl, die impräzise ist,
ah, die Dezimalbrüche rauszuholen, rechnende Lies.
Exponent. Gibt mehr davon, aber das sind die Wichtigsten,
die man mal so braucht. Wir können fragen,
ob eine Zahl exakt ist, in einer exakten Darstellung und
das ist die Funktion, die heißt überraschenderweise exakt.
Ja, und Die ist eben False, wenn wir die Zahl einfach so notieren.
Damit haben wir eine Hundsnormale Fluting-Point-Zahlen in exakter Darstellung.
Aber wenn wir hier das kleine E einfügen, heißt das,
bitte bitte wähl für 1-0 eine exakte Repräsentation.
Und wenn wir ganz zahlige Zahlen dividieren, das Ergebnis auch exakt.
So, und haben wir als Beleg dafür,
dass die natürlichen Zahlen wirklich beliebig groß werden können.
Man, eine erste kleine rekursive Funktion. Wir wissen ja
, wie man die Fakultät einer Zahl rekursiv berechnet.
Das ist einfach die Ente Fakultät ist das Produkt aus der Zahl N und der N-Ersten Fakultät.
Und genauso können wir es rekordiv schreiben, indem wir eben sagen,
wenn die Zahl N gleich eins ist,
soll das Resultat eins sein und andernfalls multiplizieren,
wir enden mal der Fakultät der N-Ersten Zahl.
Und damit können wir locker und lässlich
die Fakultät von 200 aufrufen und kriegen ein exaktes Ergebnis.
Versuchen Sie es mal in Java, wie weit Sie damit kommen.
Wenn wir das jetzt in die indexakte Darstellung wandeln,
dann sehen wir, dass wir da eben im Racket
auch ein Problem hätten,
dann kriegen wir
den hübschen Wert
positiv unendlich
Bei Fakultät von 200, dass eben außerhalb des darstellbaren Bereiches,
aber bei 100 geht es gerade noch, da reichen die,
Flut in Pointzahlen. Oder wenn wir jetzt dasselbe Spielchen
auch noch mal mit 10 hoch 100 oder so machen,
dann kriegen wir jedenfalls auch schön lange und hohe Zahlen.
In exakt, okay. Weshalb führe ich diese Zahlen ein,
damit wir eins noch mal vergegenwärtigen können, was das Problem ist,
wenn wir Sehr große numerische Werte haben.
Selbst wenn sie eben inexakt dargestellt werden können,
wir können bei Berechnungen das Problem der Auslöschung kriegen.
Wenn wir inexakt rechnen und addieren mit der Funktion,
die uns ein inexaktes Resultat zurückgibt, dazu eins.
Und ziehen aber den Wert selbst nochmal ab. Würde man
eigentlich erwarten, dieser Wert minus sich selbst gibt als Ergebnis null.
Und wenn wir dazu eins addieren, haben wir das richtige Ergebnis,
Bustekuchen, wir kriegen null raus, Auswertungsreihenfolge bestimmt,
dass zuerst die Eins zu dieser riesigen Zahl addiert wird.
Und wenn wir die Zahl mit derselben unexakten Wert wieder von sich subtrahieren,
es ist ausgelöscht, es bleibt null übrig,
aber mit der exakten Arathmetik kriegen wir tatsächlich eins raus.
Wir haben noch ein paar spezielle Zahlen, die sind Ihnen sicherlich.
Und ja, war auch schon begegnet, aber die finden
wir in Recket genauso. Also wenn wir eins durch null dividieren,
kriegen wir als Ergebnis positiv und endlich raus,
wenn wir minus eins durch null teilen, wird es minus unendlich.
Wenn wir null durch null teilen, weiß Recket nicht,
was es machen soll und sagt uns not
the number
Weil number for not and number ist trotzdem true.
Und wir haben eins durch null, okay, dann kriegen.
Wenn wir nicht mit Flooting-Point-Zahlen arbeiten,
kriegen wir bei exakt der Division Division bei Zero als Fehlermeldung.
Dann, weshalb, weil in dem Fall findet er auch
keine Möglichkeit, mehr das darzustellen.
Wir sind wirklich jenseits dessen, was noch möglich ist.
So, wir können direkt vergleichen, solange wir exakt rechnen,
wird das Ergebnis tru sein, ja?
Oder versuchen, versuchen Sie diese Beispiele einfach mal selbst auszuprobieren,
ein Gefühl dafür zu kriegen
Ich meine, Sie positiv und endlich, negativ und endlich uns zu bekommen.
Vergleichsprädikate gelten in dieser Form geschrieben, gelten eben nur für Zahlen.
Es sind typspezifisch, ja. Und wir können aber,
Sie praktischerweise verwenden und gleich eine ganze Folge auf Sortierung zu vergleichen.
Hier in dem Fall kriegen wir einen Ärger,
weil es eben fürs Strings nicht verwendet werden kann.
Da brauchen wir das String kleiner, ja.
Wenn Sie ins Handbuch schauen, werden Sie feststellen,
dass Sie praktisch für jeden Datentyp eigene Vergleichsprädikate haben.
Da hat ein Typ Bullyen ein bisschen Gesondert behandelt eigentlich,
so wie es in vielen Sprachen aus Komfortgründen ist, angeführt,
wurde er ja um logische Werte darstellen zu können und es benannt nach George Bull.
Das ganz praktisch in Recket ist, dass wir,
ähnlich wie in C, jedes Objekt als Wahrheitswert verwenden können.
Wenn irgendwo ein Wahrheitswert erwartet wird,
dürfen wir jeden wohl definierten Recket-Typ Wert eines wohl definierten Recket-Typs
auch an der Stelle hinschreiben.
Und dann wird es
großzügig interpretiert als Tour oder False
Es ist allerdings ziemlich eingeschränkt. In C ist das viel
komfortabler gelöst und selbst in Kommendes komfortabler. In Recket,
egal was wir reinschreiben,
nur wenn da wirklich die konstante Falls als Ergebnis zurückkommt,
dann wird der Ausdruck als False interpretiert und alles andere gilt als True.
In anderen Listdialekten und auch in alten Recket-Standards war es noch so,
dass zum Beispiel null leere Liste auch als Falls gewertet wurde
oder sogar null, das ist nicht so in Recket.
In Recket ist wirklich alles wahr und Die Existenz reicht,
es wird als wahrgesehen. Nur der Wert falls wird zu falls ausgewertet.
Und wenn sie Truhen falls schreiben, das sind Konstanten,
die immer zu sich selbst evaluieren, es braucht nie gequotet zu werden.
Gut, ein Beispiel, wir werten Truh aus, evaluiert Citru,
falls, evaluiert zu falls, Quote falls, evaluiert zu falls.
Not true, evaluiert zu falls, nicht drei,
evaluiert zu falls. Warum? Drei ist etwas,
was nicht falls ist, ja, ist verschieden davon.
Also hat es den Wert to, Not two ist eben
falls, ja. Not list Sie. Die Liste,
evaluiert zu tun, negiert, auch falls.
Not falls what two, nordleere Liste falls und so weiter.
Ja. Das passiert von meinem nordleeren Not.
Not und mit dem Argument null, ja. Das,
was ich sagte, es ist in anderen Sprachen komfortabler,
da würde die Null als Folds gewertet werden,
ist hier nicht, die ist tot,
weil es eben nicht False ist.
Also nur das Zeichen Falls selbst wirkt wie Falls,
alles andere, egal ob da Null oder leere Liste
oder so steht,
wirkt als Truh
Unpraktisch. Gut, also wenn wir jetzt hier auswerten wollen,
If-Auto, ist das Symbol. Denn wenn wir eins haben
, wollen wir fahren, andernfalls laufen. Ja,
da würde fahren zurückkommen, weil eben Auto zu True evaluiert.
Es ist ja nicht falls und okay.
Gut, ja. Jetzt True falls, ja.
Okay, also gleich geht er nicht. Das habe ich
schon erklärt, braucht man die Filme so vertiefen.
Wir können Ruhe und Folz mit EQ mit Equal vergleichen oder
auch mit Equal
Also diese Beispiele sind im Skript,
damit sie einfach mal ein bisschen Beispiele kurz sehen.
Ich will es hier nicht vollständig durchgehen.
Was mir noch wichtig ist, sind die logischen Verknüpfungsfunktionen,
weil da ein paar Abkürzungen drin sind, die manchmal nützlich sein können.
Also sie erwarten mit Fug und Recht in jeder Programmiersprache
die grundlogischen Operationen, Konjunktionen, Dissunktionen, Negationen,
haben wir in Recket auch.
Die Konjunktion heißt End und wird als End geschrieben,
Dissunktion heißt Ohr und wird als Ohr
geschrieben und Negation als Not
Und das Schöne ist, das sind Special-Formen Operators,
die ihre Argumente schon ein bisschen vorauswerten,
ehe sie angewendet werden und sind nicht strikt.
Was heißt das? Wenn wir ein End von zwei Werten berechnen,
hört Dreckett mit der Berechnung auf, der Auswertung der Argumente,
wenn es schon sagen kann, nach dem Sehen des ersten Arguments,
ob der Wert überhaupt noch wahr werden kann.
Wenn Sie die Konjunktion haben aus einem Wert,
der falsch ist
Dann ist der Wert des zweiten Arguments völlig egal,
egal ob es Truhe oder Volks ist, das Ergebnis kann nie True werden.
Also wird die, es ist garantiert in Reggae,
dass die Berechnung aufhört, wenn man feststellt,
es ist das erste Argument false ist. Also,
in dem Fall, wo das erste Argument P. False ist,
kriegen wir auf jeden Fall, dass den Wert falls zurück,
selbst wenn die Evaluierung des zweiten Argumentes zu Rechenzeitfehlern führen würde.
Wir dürfen uns darauf verlassen, dass dann das zweite Argument
nicht ausgewertet wird
Beim Uder ist es so, dass wir, wenn das zweite Augument,
wenn eins der Argumente schon festgestellt wird als Tru,
das erste Shan Tru ist, dann wird das zweite Argument auch nicht mehr ausgewertet.
Wenn ich ein Uder habe von True und False,
ist das zweite Argument egal. Beim Not muss das zweite,
muss das einzige Argument auf jeden Fall definiert sein,
aber beim Uder können wir sparen,
wenn eins der Argumente schon True ist,
das erste schon Truhe ist, für zweite nicht ausgewertet,
beim End eben,
wenn das erste schon False ist,
wird das zweite
auch nicht ausgewertet
Also beim End- und Ohr haben wir die garantierte Auswertereihenfolge von
links nach rechts. Und das ist natürlich ganz praktisch,
wenn wir in der Bedingung irgendwelche Tests haben,
ob ein Arry-Element definiert ist oder so.
Oder wir testen wollen, ob wir durch null definieren können.
Nehmen wir mal an, wir haben eine Variable X
und geben dir den Wert null.
Und haben jetzt ein End, wo wir testen,
ist X größer null. Dann unfeststellen wollen,
ob eins durch X
größer ist als
Wir können es in diesem Fall, wenn X gleich 0 ist,
darauf verlassen, dass dieser Teil mit der Indivision nicht ausgeführt wird.
Oder ein anderer Fall, wir haben Typ-Prüfungen und wollen eben auf kleiner gleich prüfen,
aber müssen uns vorher vergewiss sein, ob dieses X,
X, X auch vom Typ Zahl ist.
Also wenn wir zuerst schreiben, Number X, X,
X und da wird schon festgestellt, ein Huch,
9, X, X, X ist keine Zahl,
dann können wir uns darauf verlassen,
dass das End jetzt schon den Wert falls liefert,
ohne dass das Zweite gemacht wird.
Okay. Und mit logischen Ausdrücken können da jetzt Funktionen
schon mal ein bisschen anders schreiben
Wir wollen mal feststellen, ist etwas denn ein Schaltjahr?
Ja, und mit logischen Ausdrücken können wir das in einen einzigen logischen Ausdruck schmeißen.
Erinnern wir uns, ein Schaltjahr ist es,
wenn das hier glatt durch vier teilbar ist,
aber nicht, wenn es glatt durch hundert teilbar ist,
aber doch, wenn es glatt durch 400 teilbar ist.
So, und da schreiben wir jetzt mal als logischen Ausdruck,
wir haben entweder ein Schaltjahr,
wenn es der Divisionsrest von Y durch vier gleich null ist,
es ist glatt teilbar,
aber nicht der Divisionsrest gegenüber
Gleich null ist. Oder wenn es auf durch 400 teilbar ist,
ist es auf jeden Fall ein Schaltjahr und so kann man es also übersichtlich schreiben.
Und in diesem Fall haben wir auch die garantierte Auswertereihenfolge.
Erst wird das gemacht und dann nicht die zweite Division,
wenn die erste schon wahr ist. Okay. Ja,
dann brauchen wir noch gelegentlich Zeichen und Zeichenketten,
aber die werden wir sehr wenig benutzen.
Im Wesentlichen werden wir schlichtweg mit Symbolen hantieren. Also wir haben einen Datentyp dringend,
das ist eine spezielle Form der Implementation, von Vektoren.
Mehr müssen wir darüber eigentlich nicht sagen. Wir haben den
Basisstaatentyp Character und der ist geordnet, wir haben Ordnungsfunktionen drauf,
Ordnungsrelationen, ja. Und zum Typ Charakter gehören,
wie sie es von ihren Programmiersprachen gewohnt sind, die sichtbaren,
drückbaren Zeichen, da sind die Formatierungszeichen drin,
sind Steuerzeichen drin, die man nicht sieht, ne?
Angabe von Charakters, die notieren wir so,
Gartenzaun backslash und dann das Zeichen, das wir haben wollen, ja?
Und Dass wir also jetzt einen Charakter, ein Objekt vom Tatentyp-Charakter,
das wäre die Variable, dass wir das jörtliche Symbol haben.
Manche Formatierungszeichen haben feste Namen, wie zum Beispiel Space oder Newline
, können wir so angeben.
Es ist geordnet. Also wir können mit Charakter Kleiner,
Charakter Less or Charakter, Greater fragen,
ob zum Beispiel kleinen A oder Großa größer ist als GroßZ in der Sortierung.
Und diese Sortierungsreihenfolge ist genau die, die sie in
der Zahlentabelle der Aske-Zeichen haben
Und sie können es auch direkt,
wenn sie denn Lust haben, als dreistellige Oktalzahl angeben,
wenn es denn sein muss oder hexadezimal oder als Unicode, ne?
Wenn sie jetzt eine Intita-Zahl haben, können sie daraus den
Charakter errechnen und umgekehrt, dass eben die Position in der Asgit-Tabelle.
Zum Beispiel mit Character to Intita können sie rauskriegen, ja,
das große A steht im Asgit-Code anstelle 65 oder wenn sie
die Oktalzahl 077 angeben, das wäre eben die Asgit-Stelle Welle 63.
Die Umkehrfunktion wäre Inti-Chat-to-Character und kann auch mal fragen,
bist du überhaupt ein Charakter, ja?
Okay. Okay, Inti-Chat-to-Charakter gibt uns die Zahl zurück. Ja,
und damit können wir Zeichen schrittweise fortschalten, zum Beispiel,
wenn wir den Nachfolger Buchstaben zu A haben wollen, nehmen wir Charakter-to-Intiture,
IDI an 1 drauf und machen dann weder Inti-Chat-to-Charakter und haben dann das B.
Das könnte nützlich sein fürs nächste Aufgabenblatt,
sich an diese Funktionen zu erinnern.
Also da setzt sich mal ein Lesezeichen hin,
der Seite im Skript
Okay. Ein Tier, das selbe Spielchen mit Unicord-Charakter
an, um andere zu kriegen. Ja,
das paar Sachen sind voreingebaut, zum Beispiel Charakter to downcase,
um daraus groß zu klein und klein zu groß zu machen.
Okay. Kommen wir mal zu den Listen.
Das sind jetzt die Datentypen, die wirklich auch interessanter sind
als das, was sie ohnehin schon kennen.
Wir können eine Liste zunächst mal, um es formal zu betrachten,
für Korrektheitsbeweise sehen, Winnejahr geordnete Sammlung von Werten und Objekten.
Sie ist linear und sie ist geordnet
Ja. Und in Recket sehr praktisch können wir
Werte von allen Typen gemeinsam in eine Liste tun.
Eine Liste muss nicht einen einheitlichen Basistyp haben. Weil Recket
hat kein Typ Kalkül, da ist es eben unwichtig.
So, für den Fall, dass alle Elemente einer Liste
vom gleichen Typ sind und keine Wiederholung drin vorkommen,
können wir auf Flüsten auch Mengenoperationen anwenden.
Ja, dann können wir es als Menge betrachten. Gut,
fangen wir mal an, uns die ersten Listen zusammenzubauen.
Die simpelste Art,
eine Liste mit
Endlich vielen Werten zu erzeugen ist, indem wir sie einfach hinschreiben und aufzählen.
Wir quotieren, Klammer auf und dann schreiben wir alles,
was in die Liste rein soll, in der Reihenfolge hin
visuelleste gebraucht wird, machen die Klammer wieder zu.
Und schon haben wir eine Liste. Oder wenn wir
es dynamisch zur Laufzeit machen wollen und zu dem Zeitpunkt,
wo wir das Programm schreiben, noch nicht wissen,
was es ist, können wir die Listfunktion nehmen.
Und dann eben sagen, bilde mir eine Liste aus all
den Argumenten, die ich hier aufgezählt habe.
Und hier können jetzt Ausdrücke stehen, die zur Laufzeit
die Werte erst berechnen
Um zu wissen, wie lang die Liste ist. Klar,
Längsfunktionen, die zählt das einfach durch und wir bekommen den Wert zurück.
Leere Liste, wenn wir nichts reinschreiben, ist sie leer.
Und die Länge der Lehrenliste ist null. Wir können testen,
ob eine Liste leer ist, indem wir die Nall-Funktion nehmen.
Nall-Quote 1, 2, 3 und dann kriegen wir False
zurück, wenn die Liste eben leer ist.
Nicht leer ist und wenn sie leer ist, kriegen wir tot zurück.
Gut, die leere Liste haben wir schon gesagt.
Sie ist Polymorph, leer im Hinblick auf jeden speziellen Typ.
Eine Liste, die die leere Liste enthält,
ist natürlich nicht leer, die hat die Länge
Das Quotzeichen ist wichtig, wenn wir Listen hinschreiben.
Wenn wir Diskutierungszeichen vergessen, dann werden Sie die Fehlermeldung kriegen.
Ich habe keine Funktion eins, die ich anwenden kann auf die Argumente 1 und 2.
Das ist die typische Fehlermeldung fürs vergessene Quotierungszeichen.
Die Lehrenliste erfüllt Kant der Standardbasisprädikate. Das ist weder ein
Paar, weil das nicht wenigstens ein Element drin.
Sobald ein Element drin ist, ist das Typredikat per True.
Gut. Und wenn wir Die Listen, die direkt hinschreiben,
brauchen wir das Quotzeichen? Ja. So. Zum Beispiel Quot,
Sinus 3 ist zunächst mal eine Liste, ja?
Ein Stück Kot. Und die Länge von dieser Liste
ist zwei, ja? Okay. Nur zur Erinnerung,
also wenn immer wir klammern, immer schön in Paaren,
ja, haben uns hier Charlie Braun und Sally vorgemacht.
Okay, wie arbeiten wir jetzt mit Listen?
Also wir können sie zusammensetzen zur längeren mit Appent,
ja, wenn wir zwei Listen haben,
schreiben wir Appent und wir kriegen die längere,
die Zusammensetzung und für Korrektheitsbeweise, über Listenfunktionen ist der folgende Zusammenhang wichtig.
Die leere Liste wird für Appent wie ein neutrales Element.
Also wenn wir irgendeine Liste Access haben,
man hängt die leere Liste dahinter, ist dasselbe,
als wenn wir erste Lehrerliste nehmen,
die mit Appent für die Liste selbst setzen und es kommt eben die Liste selbst raus.
Das ist garantiert, können wir uns darauf verlassen. So,
für die Konkartonation mit Appentlisten zusammenzusetzen, gilt praktischerweise auch,
dass sie assoziativ ist. Also wir können es in beliebiger
Klammerung machen. Also erstmal XS und Y zusammensetzen,
dann das Set dahinter setzen, ist dasselbe,
als wenn wir erst die beiden letzten Argumente verbinden würden und
dann das andere davor setzen, ja.
Sie ist assoziativ, aber nicht kommunitativ. Ist hoffentlich auch klar.
Gut. Können wir uns überzeugen, indem wir diese Appenz mal ausprobieren?
Kommt tatsächlich Tu raus, ja. Okay. Ja. Frage,
ob wir jeweils nur zwei Argumente angeben können, nein,
Appent hat eine beliebige Stelligkeit,
sie können also beliebig viele Listen reintun,
das klappt,
Und wir können mathematisch es erpennt als Vereinigungsmenge interpretieren.
Also wenn wir Mengenoperationen machen wollen und die Listen entscheiden keine Wiederholungen drin,
dann können wir sagen, dass ein Appent die Vereinigung der
beiden Mengen im 1 und M2 darstellt, ja.
Und Member testet, ob ein Element in der Liste vorkommt,
wir können das auch als ein Enthaltenseins Testmengen theoretisch betrachten, ne?
Länge der Liste können wir mathematisch sehen als Mächtigkeit der Menge,
wenn wir es Mengen theoretisch interpretieren
Gut, also wenn wir fragen, Member-Funktionen anwenden zum Beispiel,
wir haben eine Liste mit allerhand drin und fragen,
ist fünf das Element der Liste, kriegen wir Falls zurück,
ja, Member A, ein Xes,
das würde uns aber da das A tatsächlich vorkommt,
praktischerweise nicht nur sagen, dass es vorkommt,
sondern auch sagen, wo es vorkommt.
Also die Member-Funktion ist so implementiert,
dass sie uns in der Liste die Teilliste geliefert,
die mit dem gesuchten Element beginnt
Und das ist auch der Grund,
weshalb der Name von Member kein Fragezeichen als letztes Zeichen hat.
Es ist kein echtes Prädikat, weil wir als Resultat eben
nicht nur wahr und falsch zurückkriegen, sondern es ist ein Semi-Prädikat.
Wenn es tatsächlich was drin findet, gibt es uns den
Wert hier zurück ab da, wo das Element vorkommt.
Und das wirkt aber als True,
sodass es nicht weiter stört, dass wir keinen Wahrheitswert zurückkriegen.
Also in meinem Beispiel, wir unsere Liste 1,
2, 3 und ein Symbol A und 4.
Das A taucht jetzt an der vierten Stelle
auf und wir kriegen eben die Teilliste ab der Stelle A
zurück als Resultat
Und wenn wir jetzt allerdings fragen, kommt die Teilliste 1, 2 drin vor.
In der Liste 1, 2, 3 kriegen wir Folds zurück.
Warum? 1, 2 ist kein Element der Liste.
Hingegen hier, wenn wir fragen, kommt 1,
2 in der Liste vor, die eine Unterliste 1,
2 hat, dann würde das klappen.
Und wir würden tatsächlich diese Stelle finden, ja?
Ja, was hat er jetzt? Autos wurde der Vorwärts
noch gesagt an Position 3. Moment hier an der Stelle,
nee, das heißt nicht an Position 3, das heißt, die Stelle
Ab der Stelle, der Teilliste, die mit 1,
2 beginnt, enthält das Argument 1 Teilliste 1, 2,
3. Diese Liste, also wir bekommen den Rest der Liste zurück,
ab der Stelle, wo das gesuchte, das erste Element ist,
der ganzen Schwanz, ja.
Also es sagt uns nicht,
an welcher Stelle es vorkam, sondern es gibt uns,
es schmeißt alles weg vor der Stelle,
wo es gefunden wird und wir kriegen die Teilliste zurück.
Dass es deshalb so gemacht worden,
weil das Element kann mehrfach vorkommen und es könnte sein,
dass wir die anderen Stellen auch noch finden wollen,
dass wir rauskriegen wollen,
wie oft es vorkommt
Ja, wenn wir jetzt unsere Listen verlängern wollen,
wir haben schon appent gesehen, das nimmt einfach zwei Listen und Papiere zusammen.
Ihre Übungshelfer werden Ihnen sagen, Leute, verwendet kein Appent,
das ist ineffizient, Rechenzeit, unökonomisch und so weiter.
Ziemlich oft braucht man es, ja. Also wenn es sinnvoll ist,
machen Sie es, der typische, effiziente Weg.
Listen zu verlängern besteht daran, dass man die Rekursiv konstruiert.
Indem wir eine bestehende Liste nehmen und wenn wir was hinzufügen wollen,
einfach als erstes Element am Anfang ein weiteres,
der zusetzt ist.
Die effizienteste Art,
was da reinzukriegen
Weil Pent muss erst mal die Liste durchsuchen, Hauptjahr,
wo ist hier das Ende und dann nimmt ihn was ranhängen.
Während die Funktion Cons, die nimmt ein Element und fügt,
erzeugt eine neue Liste, die mit diesem Element beginnt.
Also wenn wir schon,
wenn wir die leere Liste haben und wir sagen Cons,
eins mit dieser leeren Liste, kriegen wir eine zurück,
die jetzt genau die eins enthält.
Wenn wir dann noch eine drin haben wollen,
sagen wir Cons, wann, für das Resultat von Cons,
wann und wir kriegen sie und so weiter.
Ja, also per Konsoperation wird immer ein Element hier vorne reingefügt.
Und ein Test, ob etwas mit der Konsoperation konstruiert wurde
als Konstrukte, ist eben die Funktion Peer.
Die sagt uns, ob ich eine Liste habe oder etwas habe,
was mindestens ein Element hat, was vorne mit Cons angefügt wurde.
Deshalb ist Peer eigentlich das Prädikat, mit dem wir in
Recket prüfen, ob etwas eine Liste ist.
Weil wir davon osken, dass alles mit Cons konstruiert wurde.
Und das nimmt diese Paare auseinander. Also syntaktisch heißt etwas,
was Cons erzeugt ein Paar. Wir können Cons auch einfach
nehmen, um Tuppel zu erzeugen.
Zwei Dinge nehmen sie mit Cons zusammenpappen und wir haben
ein Tuppel
Das ist also ein ganz allgemeiner Konstruktor. Und das,
was Cons zurückliefert, ist eben so ein taktisch ein Paar und
deshalb wird die Per-Funktion uns sagen, jupp, ist eins.
Also wenn sie für beliebige Dinge, wo das Zweite keine Liste ist,
einfach konzern, dann kriegen sie genau diese schöne mathematische Tupenotation.
X.1, ja. Und dieser Punkt hier ist die Infix-Notation für die Cons-Funktion.
Das ist eine der wenigen, die man als Infix schreiben darf.
Okay, also wenn wir Aber als zweites Argument eine Liste haben,
dann würde eigentlich es so geschrieben werden müssen.
Nur das will echt kein Mensch lesen müssen.
Und deshalb haben wir den so in taktischen Zucker,
dass dies hier in Reckett netterweise dargestellt wird, in der flachen Notation.
Wir haben ja so schon genug Klammern. Also,
ich habe hier im Skript ein paar Beispiele,
wo sie es sich mal angucken können,
dass, ich will mal lieber zu den Zugriffsfunktionen kommen,
also wir haben jetzt gesehen, wir können Listen schreiben,
indem wir einfach Klammer aufmachen, Relemente rein,
Klammer zu.
Wir können mit Appent bestehende Listen zu einer Neuen verbinden
Wenn wir einen Wert haben und eine Liste kriegen wir den Wert
rein mit Cons, der wird immer ganz am Anfang gesetzt.
Und wenn wir eine Liste haben und wollen irgendwie darauf zugreifen und
Elemente wieder rausnehmen, dann haben wir zwei schöne Funktionen.
Die heißen K und Kütter. K. Gibt es auch unter
dem Namen First. Die liefert uns das erste Element der Liste.
Und Kütter ist die Funktion, die uns den Rest ab dem ersten Element liefert.
Gibt auch die Funktion namens Rest. Historisch heißen die Funktionen
aber so und ich verwende sie Sie gern in der historischen Form.
Also das erste Element, eine Liste, die wir erzeugen,
wenn wir einen Cons machen aus X und einer anderen Liste,
ist natürlich das X. Der Rest einer Liste,
wenn wir sie erzeugen mit Cons X und X ist,
ist das zweite Element, eben der Listenrest.
Und das erste Element der Lernliste und der Rest der Lernliste
liefert im beiden Fällen das undefinierte Element,
um nicht zu sagen eine Fehlermeldung.
Und die Liste selbst X ist, ist dasselbe,
was wir bekommen,
wenn wir ihr erstes Element nehmen und ihren Rest nehmen und
das mit Cons zusammensetzen
Tja. Für Kaskaden von K und Kunst,
da können Sie jetzt mal Sprechübungen machen und Vokabel lernen.
Und damit werde ich Sie dann auch ins Wochenende entlassen.
Es gibt Kurzformen, wenn wir Kaskaden brauchen für viele K
von K, von K, von K, ja.
Das erste Element vom ersten Element, können wir kurz schreiben mit K.
Das erste vom ersten, vom ersten können wir als Kara
und da geht bis zu 4as hintereinander, ja?
Oder wenn wir den Rest, den Rest vom Rest brauchen,
können wir sagen, küttert oder küttert,
Aber das erste Element vom Rest ist der Kader und richtig gut ist,
finde ich, dadda.
Also die Leseübung ist, jedes A steht für eine Anwendung
der Funktion K und jedes Stil für eine Anwendung der Funktion Kitter.
Und bis zu vier dürfen sie in dieser Kurzform schreiben,
also üben sie ein bisschen sprechen.
Bis zum nächsten Mal,
dann danke