Ja, ich möchte mit Ihnen heute besprechen, wie wir die funktionale Programmierung,
die objektorientierte Programmierung mit der funktionalen Programmierung verheiraten können.
Und dazu werde ich mit Ihnen etwas durchsprechen. Das nennt
sich das Commonless Object System. Das ist eine Erweiterung,
die ist ein Teil der Standards von Commonless,
also der Liz Standard schlechthin und ist schon mindestens 20 Jahre alt,
ist aber eine wunderbare Art,
wie man editionale Programmierung und objektorientierte verbinden kann.
Und es gefällt mir deutlich, deutlich besser, als das,
was die Recket-Autoren direkt in Recket eingebaut haben.
Was wir in Recket eingebaut haben, das ist eins zu eins exakt das,
was Java macht. Und ich möchte sie nicht damit langweilen,
dass ich ihnen erkläre, wie man das,
was Java direkt kann, in Recket machen kann.
Das kennen sie und wissen, wie es geht. Ja,
was wir hier heute besprechen werden, ist eine Erweiterung,
die vor allem die Stärken des funktionalen Programmierstils ausnutzt.
Und eben nicht imperativ ist
Direkte Versionen für Objektorientierung ist dazu da, dass man
imperatives Programmieren beibringt. Und ich möchte ihnen hier zeigen,
wie sie objektorientiert funktional programmieren können.
Kommt meine Stimme eigentlich hinten an? Ja. Okay,
Gott. Können wir das
an dem Teil
etwas lauter stellen
Probieren wir es mal so. Mal sehen, ob es rückhabdlungsfrei hinkriegen.
Okay. Ja. Und was ich vor allem gern am
objektorientierten Programmieren im Racket mithilfe von Zielers zeigen möchte, sind zwei Dinge.
Das eine ist, wie machen wir Mehrfachvererbung und das andere,
wie machen wir? Und wie erweitern wir ererbte Methoden?
Ja, das sind beides Dinge,
die im Funktionalen Programmierstil wunderbar gehen, wie es sehen werden.
Vielleicht überlegen Sie noch mal kurz,
warum Ihnen eigentlich in den ersten beiden Semestern objektorientiertes Programmieren
als die Basis eines gesunden Softwareentwurfs
verkauft wird
Das, womit objektorientiert gepunktet hat, ist zum einen,
dass man, wenn man es richtig macht,
gut lesbare Programme bekommt, wo man eins zu eins
den Entwurf fürs Programm auf die Problemdomäne abbilden kann.
Man sieht ein Problem,
beschreibt es auf der Basis der Objekte in der Domäne,
schafft Klassen, die das widerspiegeln und hat dann ein Modell
der Domäne und schreibt dann Methoden und schreibt es auf einen relativ hohen Level.
Diese Ebene erfordert überhaupt nicht, dass wir in einer Welt
sind, wo wir zustandsorientiert und imperativ programmieren.
Das geht praktisch in allen Programmiersprachen
Nur, wenn man den Zustand eines Objektes ändern will,
dann muss man eben imperativ werden. Aber diese ganze Stärke,
dass wir Klassen definieren und lesbare Programme hinkriegen,
das können wir in Recket auch, kein Problem.
Das Zweite, wofür Objektorientiertheit steht, ist,
dass dynamisch zur Laufzeit anhand der Klasse des Objektes entschieden wird,
was zu tun ist, diese dynamische Auswahl von Methoden,
werden wir auch sehen,
kein Problem.
Das Dritte ist die Erweiterbarkeit von bestehenden Code
Mit objektorientiertem Ansatz wirklich gepunktet hat, ist,
dass man seine Klassen hat und spezialisierte Klassen schreiben kann,
denen es einen Tick anders machen,
aber mehr auf den Code mehrfach verwenden.
Und das verspreche ich Ihnen, können wir hier in Zelos
noch in Klassen besser als im Java.
Das ist eigentlich der Punkt, der mich am meisten begeistert bei Zelos,
dass das Erweitern bestehenden Kodes so elegant geht.
Das Thema Methodenkombination. Und dass wir wirklich eine echte Mehrfachvererbung
haben und nicht Klimmzüge damit machen müssen,
dass wir Interfaces implementieren, nur weil wir von zwei Klassen
üben müssen
Erben müssen. Also zum Nachlesen gibt es zwei Bücher,
die ich Ihnen empfehlen möchte, wo das wirklich unglaublich gut beschrieben wurde.
Das eine ist das Buch von Sonja Kinny, Object oriented
programming and commonlos, a programmers guide, to cillos.
Also das ist so ein schmales Heftchen, ja,
liest sich richtig gut und erklärt es vernünftig und man kann
es einfach mal so abends zum Einschlafen lesen.
Vermutlich kann man da nicht einschlafen, weil es so spannend ist,
aber es ist jedenfalls ein gut lesbarer Schmöker.
Ich habe ein paar Beispiele da rausgenommen und das andere,
die Kurzform für total ungeduldige, ist das Buch von Peter Graham,
sie kommen in Lisplas, ist die Einführung, wie programmiere ich in kommendes.
Und der hat ein wunderschönes Kapitel über Zehlers,
was ist noch kürzer und knackiger erklärt,
als wenn man das ganze Buch von der Sonja Kini liest.
Und die Beispiele, die ich jetzt zeige,
die stammen aus dem Buch von Peter Graham.
Also da kann man es nachlesen, ja.
Okay. Der Zehlers ist ein Teil von Common Lisp,
ja, das ist mit das Herzstück der Sprache kommendes.
Und deshalb brauchen wir, um es verwenden zu können,
eine andere Sprache. Wir schalten jetzt unser Racket einfach um
auf die Sprache des Windel und können damit in Kommenlist programmieren.
Also das ist das, was wir jetzt tun müssen,
um es benutzen zu können, Requires,
Windel und vor allem eben auch Zählers verwenden.
Ja, Misk. Okay, was haben wir dann? Um jetzt anfangen zu können,
Klassen zu definieren, dann verwenden wir eine Special-Form,
also der neue Syntax, wir können jetzt Klassen definieren,
wir schreiben Klammer auf DevClass und damit können wir loslegen und eine Klasse einführen.
Wenn wir, also die Klassendefinition Zelos ist anders als eine Klassendefinition in Reckett,
in Java. Wenn sie in Java eine Klasse definieren,
dann schreiben sie in dem Paket, wo sie ihre Klasse definieren.
Erstens rein, welche Attribute hat das Objekt?
Und zweitens, welche Operationen sind auf dem Objekt möglich?
Ja, mit Death Message. Das ist in Recket anders.
In Reckett beschreibt die Klassendefinition die Attribute des Objektes und sonst nichts.
Die Operationen, die darauf gemacht werden, die beschreiben wir
unabhängig von den Klassen. Warum erkläre ich später? Aber
unser Death Class, das beschreibt Hier eben die Slots,
das ist der Name für die Attribute des Objekte dieser Klasse.
Mit Dev Generic, da können wir generische Funktionen beschreiben und
generische Funktionen entsprechen den Nachrichten, die sie und Operationen,
wie sie sie aus Java kennen.
Mit Dev Generic beschreiben wir eine generische Funktion,
die generische Funktion sagt, wie objekt, was geschehen soll,
wenn Objekte bestimmt der Klasse als Argument übergeben werden und was dann ausgeführt wird.
Und diese generische Funktion können wir spezialisieren und für viele,
viele Klassen
immer spezieller machen
Dafür der Methoden implementieren. Und Death Messet definiert die Methoden,
die diese generische Signatur für die Operation,
wo sie abstrakt beschrieben wird, dann implementieren.
Also das hier ist die abstrakte Definition der Methode,
wo nur die Signatur festgelegt wird.
Und das ist die Implementation für Argumente spezieller Klassen.
Das wird aber nicht zusammen mit der Klasse definiert,
weil eben anders als im Java gehört eine solche generische Funktion
nicht zu einer Klasse
Sondern wir sagen einfach, ich habe eine Funktion,
wenn ich ein Objekt einer Klasse so habe und ein Objekt einer Klasse so,
dann können wir das und das machen.
So, und jetzt können wir würfeln, in welche Klassendefinitionen schreiben wir es rein?
Wenn die Argumente von zwei verschiedenen Klassen sind,
schreibe ich das bei Klasse A rein oder bei Klasse B?
In Zählers hat man gesagt, schreibt es in einen Pfeil,
wo man dann noch sucht. Ja, aber es gehört
weder zu der einen Klasse noch zu der anderen oder zu beiden gleichberechtigt.
Es gibt keinen Grund, einen zu zwingen,
das nur in die eine Klasse reinzuschreiben und nur mit der einen zu vererben.
Sondern die werden unabhängig voneinander,
können wir jederzeit neue generische Funktionen für unsere bestehenden Klassen schreiben,
ohne dass wir Klassen neu definieren müssen.
Ja, so. Also wir haben jetzt neue,
drei neue Special Forms, Death Class, Death Generic und Death Messet.
Und dann bringt uns diese Sprache auch noch imperative Sprachelemente sowie Modifikaturen,
die man den Zustand eines Objektes ändern wollen,
wollen wir meistens nicht, aber es geht.
Und wir haben Schlüsselwortparameter, mit denen wir Argumentlisten verkürzen können.
Da werden wir auch noch
darauf eingehen
Nehmen wir mal als erstes Beispiel, so ein simples Übungsbeispiel,
wir wollen eine Klasse von Schlangen,
ein Führen mit Schlangen sind jetzt nicht diese Tiere gemeint,
sondern sowas wie First-in, First-out,
die einfach mal mit NQ and der Q-Elemente reinhängen kann,
wenn der Warteschlange oder Stacks oder sowas.
Also ganz allgemein wollen wir etwas haben,
worin man mir mit NQ Sachen reinhängen kann,
mit Deck-Queue rausnehmen, wo man viele Elemente auf einmal reinhängen kann,
das erste Element der Schlange neben feststellen kann,
ob sie frei, ob sie leer ist.
Wir wollen die Elemente aufzählen können und wir wollen sie
jetzt zurücksetzen und Warteschlange ist wieder leer. So,
und dazu würde jetzt in Relation stehen zu einer Klasse die Elemente
der Schlange beschreibt und da haben wir in diesem Beispiel gesagt,
das ist die Klasse top.
Das ist die Oberklasse, von der in Ziel das alles erbt.
Ja, und es können also beliebige Elemente drin sein.
So, in Ziellos schreiben wir sowas jetzt mit Dev Class,
ja, unsere Sprache ist aufs Windel gesetzt.
Wir sagen Dev Class, unsere Classic Y. So,
und jetzt müssen wir oft zählen,
was all die Attribute der Klasse sind und wie man darauf zugreifen kann.
Das machen wir, wird sie nicht überraschen,
indem wir eine Liste von Attributen angeben und diese Liste hat hier jetzt Items,
das sind die Elemente, die wir in die Warteschlange einhängen wollen.
Also damit mit DevClass Cue sagen wir jetzt,
wir haben eine Klasse. Das isolerisch Liste hier sagt,
von welchen Klassen wir erben, wir erben erstmal von Keiner,
sondern nur von Job und das ist implizit.
Es sind keine von uns vorgegebenen Oberklassen. Wir haben nur Das
eine Attribut Items, diesen Slot Items. Und jetzt haben wir
verschiedene Möglichkeiten zu sagen, was mit dem Geschehen kann.
Wir müssen erstens sagen, wie wollen wir darauf zugreifen?
Das geschieht hier alles über bestimmte Symbole, die mit einem Doppelpunkt beginnen.
Und das sind Keyword-Symbole, Schlüsselwort-Symbole, ein Symbol,
das mit einem Doppelpunkt beginnt, wird immer zu sich selbst evaluiert.
Das brauchen wir, das brauchen wir nicht quotieren.
So, und wir sagen hier, Schlüsselwort,
dieses Attribut hat eine Zugriffsfunktion, eine Accessor-Funktion, Q-Items.
Und das ist ein Rieder. Das heißt, wenn ich
die generische Funktion QItems-Verwände aufrufe, dann bekomme ich das Element Items zurück.
Ich kann es auch verändern, indem ich hier noch einen Weiter angebe.
Und da sage ich, das soll ZQItems heißen,
das ist eine Accessor-Funktion, mit der ich das Feld schreiben kann.
Ich kann sagen, wie es initialisiert werden soll.
Hiervor gebe ich vor, Inite Value, leere Liste.
Zu Anfang ist die Schlange leer, keine Items drin.
Und als Besonderheit, ich kann auch noch sagen,
bei den Slots, von welchem Typ sie sein sollen.
Und hier Sabi, der Typ ist Liste. Das heißt,
ich würde eine Fehlermeldung bekommen, wenn da jemand etwas Anderes reintun will,
den Wert so umsetzt, dass keine Liste drinsteht.
Und dann gibt es noch ein Feld, das nennt sich Documentation.
Ist von Kommenlisp übernommen. Und beim Ziel aus System,
man kann für jedes Attribut sagen,
wozu es denn da ist und ein Documentation-String reingeben.
Das Enttäuschende in der Recket-Implementation von Celos ist, dass diese Dinger ignoriert werden.
Man kann sie nicht benutzen. Aber sie stehen da und
ich würde für Patierbaren Code immer die Documentation-Strings da reinschreiben in der Form,
falls mein Ziel tatsächlich im Celos verwendet,
dann ist es schon richtig dokumentiert.
Dann können wir hier noch sagen,
Was soll das sein? Ja, man möchte gern ein Typredikat haben,
was feststellt, ist es ein Element der Klasse?
Und wenn ich hier Truhe sage, dann wird automatisch
das Prädikat Q erzeugt und auch ein Print da.
Damit wird automatisch Print Object implementiert, sodass ich es drucken kann.
Und dann kann ich hier die Klasse noch dokumentieren.
Documentation, the top class of all cuse.
So, also damit ist unsere Klasse für Schlangen definiert.
Und wir haben schon mal die Syntax gesehen.
Viel mehr kommt ein Sonntagslicht dazu. Im Wesentlichen ein DevClass,
nahm mit der Klasse die Oberklasse,
von denen
wir erben
Und dann hier die Elemente, die gelesen werden sollen,
oder eben Attribute der Klasse. Jetzt habe ich hier noch
diesen kleinen Stern verschwiegen. Dieser Stern sagt, dass alle Namen,
die bei der Klassendefinition eingeführt werden, von einem Modul,
was ich verwende, automatisch exportiert werden und zugreifbar sind.
Also, wenn ich innerhalb eines Moduls eine solche Klasse definiere,
dann wird veröffentlicht in der Schnittstelle der Name der Klasse Q und die ganzen,
die eingeführt werden, Q-Items und so weiter.
Ich musste nicht extra exportieren. Ja, mit der Klarstern,
sage ich, ich spare mir in der Schnittstelle aufzuschreiben,
was alles exportiert werden soll. Gut, jetzt ist noch die Frage,
wie ist es mit den Attributen? Wollen wir Attribute haben,
die jedes Objekt der Klasse hat und die nur einmal repräsentiert werden,
weil die für alle Elemente der Objekte der Klasse identisch sind?
Oder Klassenobjekte, ja, für jedes Objekt der Klasse hat
ein eigenes Attribut, ja. Wenn ich an sowas wie Q-Items habe,
Und ich verwende dafür doch dieses Schlüsselwort Location Class,
dann sind das statische Attribute,
die identisch sind für alle Objekte der Klasse.
Und die sind nur in der Klasse repräsentiert, mit Elocation Instance.
Dann hat jedes Objekt sein eigenes Itemsfeld. Und das ist
eben die Voreinstellung bei Default ist alles Elocation Instance.
Wir können globale Variable durch diese Klassenattribute ersetzen, wenn wir wollen.
Gut. Ja, also schauen wir uns noch mal die Sonntags an.
Also Dev Class, schreiben wir hin, mit ohne Stern,
den Namen der Klassen. Und dann können die jetzt
eine Liste von Oberklassen kommen, von denen wir erben.
Dann kommen beliebig viele Slots. Keine oder eine, jedenfalls im Klammern,
immer dieses sowie Items und dann die Akzessorfunktionen und all das.
Und dann kommen noch die Classkies so am Ende,
wie soll es einen Printer geben und so weiter. Okay. Ja,
wenn wir jetzt auf den Objekten in unseren Klassen Operationen machen wollen,
dann Definieren wir dafür eine generische Funktion, die beschreibt die Signatur,
also ähnlich wie es bei abstrakten Klassen ist, steht da erstmal nur,
es gibt es, aber nicht, es ist nicht automatisch implementiert.
Und wenn wir dann sie implementieren wollen,
implementieren wir es mit Death Message für spezielle Klassen.
So, Death Generic, zum Beispiel unsere Klasse,
sollte ja eine NQ-Operation haben.
Wir wollen in der Lage sein, was in die Warteschlange einzuhängen.
Schreiben wir Death Generic, der Stern sagt wieder,
automatischer Export im Modul NQ
Und das ist, das Ausrufezeichen soll hier deutlich machen,
dass wir einen Modifikator haben, das Objekt verändert sich,
wenn sich da was einstellt in der Warteschlange.
So, und hier steht jetzt,
die Klammern werden jetzt noch etwas brutaler und stärker geschachtelt.
Diese Syntags-Doppelklammer auf, sagt, dass wir hier eine Variable
haben, die von einer Klasse ist.
Und die Klasse ist Q, das ist die,
die wir mit DevClass gerade definiert haben.
Und der Parameter dieser generischen Funktion heißt Item hier
So, in meinem Deft-Syneric können wir beliebige Argumente angeben,
wenn wir mit dieser Doppelklammer arbeiten,
dann sind es eben Argumente vom Typ an der Klasse.
Wenn wir keine Klammern hier in der Argumentliste haben,
sondern einfach nur die Namen so oft zählen, dann sind es schlichte Variable.
Und auch die generischen Funktionen können Documentations drin haben.
Hier definieren wir jetzt mal das Protokoll für die weiteren Operationen in unserer iTube-Klasse.
Also wir haben das Decue, hatten wir eben beschrieben,
denn Ancue, Dann wollen wir noch ein Headcue haben,
MTQ in Your Marrate Cue, Intro Setcue.
Und die haben alle als Argument nur. Das stimmt.
Ja, ein Objekt der Klassik Y. So, wir nehmen
mal an, dass wir die Items durch eine Liste darstellen.
Ja, beliebig viele Items hängen da einfach an der Liste drin.
Und dann können wir alle weiteren Operationen einheitlich über Listen,
Operationen implementieren. Und nur das Einfügen hängt dann von der
Klasse der Schlange ab. Warum das Einfügen?
Entschuldigung,
also die
Mit Schlangen, die wir haben, sollten so von der Art sein.
Entweder ist es eine Warteslange, wo man sich immer brav finden anstellt,
ja, oder wir haben einen Stack,
da tut man immer mit Push vorne auf dem Stack drauf.
Das soll auch eine Schlange sein. Oder eben,
wir haben eine prioritätsgesteuerte Schlange, wo eben die,
die einen speziellen Ausweis haben, immer mal vorgelassen werden und
entsprechend ihres Ranges in der Schlange eingereiht haben.
Das heißt, das Einfügen hängt von der Klasse der Schlange ab,
das wollen wir spezialisieren, können die anderen Sachen sind gleich,
Denn, also das, was jetzt für alle Klassen gleich sein soll,
Head Cue, das erste Element der Schlange,
da rufen wir einfach Först auf, ja.
Death Mathe, Ted Cue, implementiert unsere generische Funktion Head Cue.
Und First gibt uns das erste Element, das kriegen wir mit QItems,
das war ja ein Akzessor. Gehen wir nochmal zurück.
Hierhin, ja. Wir hatten hier gesagt, eines,
eines der Attribute unserer Objekte sind die Items und dafür haben wir einen Reader,
das ist QItems, das ist also eine generische Funktion,
die uns die Elemente zurückgibt
Und den rufen wir hier auf, ne? Ja. Also
ein Protokoll. Also wir greifen mit Q-Items auf dieses Element Cute,
das Argument You zu und nehmen davon das erste Element.
Um festzustellen, ob unsere Warte schon lange leer ist.
Da fragen wir einfach, ob die Q-Items leer sind.
Auch Death Message MTQ, Death Message Reset You.
Gut, das soll einfach die Warte schon lange leeren.
Alle werden rausgeschmissen, wir machen die Mensa zu oder so.
Okay, also machen wir Set Q-Items, Bang.
Das ist der Writer, den wir vorgesehen hatten und
der setzt es einfach auf die leere Liste zurück
Und das Oftzählen der Elemente, das gibt uns einfach
mit Q-Items Q alle Elemente zurück und fertig.
In dem Fall ist es einfach. So,
wenn wir jetzt ein Element aus der Schlange rausnehmen wollen,
zum Beispiel mit Decue, verwenden wir die Kommenliss-Funktion Pop.
Die nimmt von einer Liste das erste Element und verändert die
Liste, sodass das erste Element rausgenommen ist.
Wenn wir eine Liste hier übergeben. Ah ja,
da muss ich ein bisschen mehr zu erzählen
NQ-Items. Wir hatten ein Encue gesehen als Operation,
wo sich ein Element anstellt und das soll eingereiht werden.
Okay, das könnten wir vorne einhängen oder hinten,
wie auch immer. Und wenn wir viele Elemente haben,
also wir übergeben eine Liste von Elementen,
die angehängt werden sollen, kommt man natürlich zum Appent greifen,
aber es soll ja die Einreihestrategie berücksichtigen.
Also machen wir ein Mapping mit dem NQ,
das ein Element reinhängt, über die gesamte Liste der Items,
die wir übergeben haben und dann kriegen wir die Cute zurück.
Gucken wir auf diese Argumentliste. Wir haben hier als erstes Argument
etwas stehen, QQ. Deshalb, diese Methode erwartet als erstes
ein Objekt der Klasse Q und ist für die Klasse Q geschrieben.
Wir haben ein zweites Argument, Items,
das erwartet ein Objekt der Klasse List.
Und nur wenn wir eine Liste übergeben,
wird eben diese Methode aktiviert.
Hängt von beiden ab
Ja, und jetzt gucken wir mal auch ein bisschen Vererbung,
verschiedene Unterklassen unserer Klasse erzeugen.
Und wir wollen ja Code wiederverwenden. Also unsere Klasse Q,
die wir eben geschrieben haben, Items, die ist insofern fertig.
Wir können Sachen reinhängen. Wir haben bei der Implementation eine Funktion verwendet.
Eine Operation verwendet, die erst noch generisch war.
Das N-Cube hat mir noch gar nicht spezifiziert, dafür existiert
gar keine Methode, aber wir haben es schon mal verwendet.
Ja, das geht also ohne weiteres.
Wo haben wir
das gemacht
Hier, ne? NQ existiert bisher nur als generische Funktion,
aber wir können es natürlich schon verwenden,
um Methoden zu schreiben und hoffen dann, dass es irgendwann mal jemand implementiert.
So, und jetzt spezialisieren wir das einmal auf Leave or Cute,
das soll eine Last in First Out sein,
also dass wir also das Deck, First in First Out,
Fifor Cute, das ist die klassische Warteschlange.
Für die müssen wir jetzt Encue spezialisieren,
für diese auch, für die Ranked Cue,
wenn jemand nach seiner Gabesoldungsgruppe in die Schlange rein darf,
müssen wir hier auch spezialisieren und dann noch eine Random Cue,
das ist vielleicht das Treffendste für die Mensa,
man landet irgendwo
So. Und diese müssen wir jetzt, diese Erweiterung müssen wir schreiben.
Wie definieren wir die spezialisierte Klasse? Na, DevClass,
Stern, Gliffocue, Erbt von Q. Es gibt gar keine neuen Elemente.
Das einzige, was wir, keine neuen Attribute,
Entschuldigung, das einzige, was wir machen müssen,
ist, dass wir das NQ tatsächlich mal implementieren.
Ja. Last in first out, also für so
einen klassischen Keller-Speicher, als Schlange.
Da nehmen wir einfach die Commonlist-Funktion, Push
Item auf das lange Q-Items und dann ist es geschehen.
So. Für die First in First Out Q, FIFA Cue.
Da führen wir uns effizient machen zu können,
ein weiteres Attribut ein. Und das nennen wir Tale,
das soll dann ein Zeiger sein auf das letzte Element der Warteschlange.
Ja? Und dafür sehen wir vor, eine Erzessor-Funktion,
FIFA Cue Tale und Writer,
dass wir das letzte Element auch verändern können,
ein Update machen, wenn jemand was einhängt, Set Fifi Cue Tale.
So. Der Initialisierungswert für diesen Schwanz da soll eben auch
die Lehreliste sein. Es ist weiterhin vom Typ List.
Und ansonsten müssen wir da nicht viel weiter tun. So,
wenn wir jetzt ein Encue machen für die FIFA Cue,
da müssen wir also diesen Chail da verwalten,
damit man schnell immer hinten am Ende der Schlange was reinhängen kann.
Also geht natürlich nur, wenn diese Q-Items nicht leer sind,
if not null Q-Items, set tail,
dann verenden wir mit der fertigen Kommenliss-Funktion Z Tail,
FIFA Q-Till, Und wenn wir das geschafft haben,
dann machen wir Set Q-Items, Q, New Cons.
Da haben wir eine neue Konzelle erzeugt hier oben,
wo wir einfach eine Liste mit dem Item gemacht haben, ja?
Und in allen anderen Fällen müssen wir noch Set-Fifiocu-Tale setzen auf
die neue Q, ja? Okay. So,
ja, wir haben, wenn wir dort ein Kommenless programmieren,
eine ganze Menge von Operationen auf den Klassen selbst und auf den generischen Funktionen,
sodass wir eine Metaprogrammierung darüber machen können.
Und das macht es uns leicht, das zu verwalten.
Also, wenn du eine Klasse definieren,
dann sind sie einfach Instanzen der Klasse, Klaas.
Und wir können das Klassenobjekt selbst fragen. Alles,
was wir über das Objekt wissen wollen, das haben sie in Java genauso.
Die definierten generischen Funktionen sind Instanzen der Klasse Generic.
Und das ist eine Unterklasse von Function. Das heißt,
alles, was wir gelernt haben darüber,
wie wir mit Funktionen arbeiten,
können wir auch
dafür machen
Generische Funktionen mit Mapping, über alle Objekte in der Klasse schicken und so weiter.
Und dann haben wir in Zelos eben sehr viele Meta-Methoden,
mit denen wir jetzt über die Klassen- und Methoden Dinge abfragen können.
Wir können mit Klaas oft jedes Objekt fragen, was dann seine Klasse ist.
Ja, ich rufe hier mal auf Make Random Q und Klaas of.
Und dann erfahren wir, ja, die Klasse ist Random
Q. Wir können zwei Klassen fragen,
ob die eine eine Unterklasse von der anderen ist. Ja,
wenn wir uns nicht sicher sind,
da kommt so ein Objekt daher und wir wissen nicht,
was wir damit machen können, was zulässig ist,
fragen wir doch erstmal,
bist du eine Unterklasse von der Klasse,
von der ich
die Operation verwenden möchte
Wenn ich frage, ist Random Tune und der Klasse von Q,
dann kriege ich eine Liste aller Oberklassen bis zu Top hin.
Und dann kann ich, also erstens ist die Antwort True,
weil das Objekt ja nicht die Antwort nicht false ist.
Und ich erfahre auch genau, welche Oberklassen die Klasse hat.
Ich kann zwei Klassen vergleichen, welches die Spezifischere ist.
Das ist Moore Specific, Random Q, ja.
Und erfahrst dann. Wir können die definierten Attribute abfragen.
Da hat jemand eine Klasse definiert und wir sollen es verwenden
und hat uns vergessen, die Dokumentation zu geben,
macht nichts
Kommendesfragen und das sagt es uns. Zum Beispiel mit Klaas
Slots kriege ich die Liste aller definierten Attribute der Klasse.
Ja, und ich erfahre auch, hat sie einen Rieder,
hat sie einen Reiter, hat sie einen Initialisierungswert und so weiter.
Also die Dokumentation ist praktisch eingebaut. Können wir beim laufenden Programm fragen,
ehe wir auf ein Feld zugreifen wollen, ob es überhaupt einen Rieder hat.
Weil wir das auswerten können. Ja, und wir können gucken,
was an generischen Methoden implementiert ist für das Objekt der Klasse.
Ja, wenn ich jetzt frage,
NQ, was haben wir denn da alles an Methoden?
Dann erfahren wir hier, okay,
Dass wir es einmal für random Ranked Cue haben,
dass es implementiert ist für random Cue, für FIFU-Cue und für Live-Occue.
Ja. Und wir sehen hier auch die Signatur.
Das ist das zweite, das ist immer ein zweites Element da sein muss.
Und das ist von der Klasse top. Das heißt,
da sind wir frei, alles zu übergeben.
Wir können die Stelligkeit abfragen mit Generic Ereggy. Übrigens können
wir uns die Mühe sparen, sollten wir aber nicht.
Mit Dev Generic die Signatur für eine Operation einzuführen. Also wenn wir sicher sind,
welche Apparat Signatur wir haben wollen, Sollten wir es dokumentieren,
bei der Klassendefinition,
dass wir separat auch für die generischen Funktionen eine Signatur angeben und dass damit für alle Anwender lesbar machen,
dass sie wissen, welche Operationen da sein sollten.
Damit haben wir erst mal abstrakt die Klasse definiert.
So, wo kriegen wir jetzt Objekte her?
Ja, einfach mit dem Konstruktor Make. Ja,
wenn wir Makes sagen und den Namen einer Klasse,
kriegen wir ein Objekt dieser Klasse.
Und wir können uns eigene Konstrukturen schreiben,
indem wir zusätzlich, wenn wir was Spezielles haben wollen,
Make Lifocue zum Beispiel ruft Make
Auf mit der Klasse Leeforcue als Argument. Das Make ist wichtig,
dass uns klar ist, Make ist keine Methode,
sondern es ist eine Funktion,
da die Klasse ja noch nicht existiert,
können wir es natürlich nicht von der Klasse abhängig machen,
sondern die Klasse wird als Argument übergeben.
Mick erzeugt also eine Instanz und ruft die generische Funktion Elocate Instance auf.
Damit wird erst mal ein Objekt erzeugt und es wird initialisiert mit allem,
was wir in der Initialisierung geschrieben haben, denn wenn Dev Class,
sagen wir ja, wie die Slots initialisiert werden,
das wird also erst mal initialisiert.
Und anschließend, und das ist das sehr Praktische,
und was sie ständig verwende, wenn,
sobald das Objekt in eine existiert, wird Initialize aufgerufen.
Und Initialize ist eine Methode, ja.
Und wenn wir jetzt für die Klasse initialize spezialisieren,
können wir weitere Initialisierungen da reinschreiben. Okay. So,
machen wir mal was ganz, ein ganz simples Beispiel.
Ja, muss jetzt nicht die richtige Zeit dafür,
aber der Klassiker ist, man erzeugt eine Klasse Eisbecher.
Also unser Eisbecher soll einfach zwei Kugeln von Eis
haben,
Und da können wir uns mal angucken, wie wir,
ob solche Objekte konstruieren können. Also bei der Definition der Klasse
haben wir schon gesehen, wir können die Falls vorgeben.
Ich mache mir meine Klasse, Dev Klaas, Eisbecher,
erbt von nix. Also Oberklasse ist top. Ich habe
einen Attributsorte 1, sondern einen Attributsorte 2. So,
und da habe ich als Indit Value, also den Eisbecher,
hä, das sind all die Eissorten, die ich nicht mag,
aber egal, der erste Indit Value wäre Erdbeer, der andere Schoko.
So, und wir haben dafür nur einen Reader,
das heißt, wir können es nicht verändern,
wir können es
nur essen
Wir haben ein Inite-Arc vorgesehen. Das soll es uns möglich machen,
im Konstrukt dort zu sagen, ob wir dir vordefinierten Werte ersetzen wollen durch andere.
Weil SO Wann ist eine Sorte 1 und das SO2 ist Sorte 2.
Wenn ich jetzt einfach nur so sage, make Eisbecher,
dann kriege ich die default inite Values, Erdbeer und Schoko.
Ja, ich habe Eisbechersorte, 1 ist Erdbeersorte 2,
Schoko kriege ich zurück. Wenn ich aber nur absolute Schokoladenhasserin bin,
dann kann ich sagen, make Eisbecher,
aber für Sorte 2, dieses zweite, ja,
dann sage ich Zitrone.
Ja,
mit diesem Kino
Wird, in der Argumentliste SO2 und übergeben des anderen Wertes,
haben wir jetzt einen Eisbärcher,
der hat fürs erste immer noch Erdbeeren fürs zweite Zitrone.
Wir können auch zufällig initialisieren,
wir bei der Klassendefinition können wir statt Innet Arc oder Innet
Value als dritte Initialisierungsmöglichkeit, einen Fang übergeben.
Ein Zang ist eine Parameterlose Funktion und wir übergeben hier
die parameterlose Funktion Random Kugel.
Was macht sie? Random Element aus Vanille-Adwehrschokopistazio Walnuss
Und bei Sorte 2 haben wir auch eine indische Liza,
den übergebendenselben, ja. Und haben hier trotzdem noch einen Inage-Art gelassen.
Also beides zusammen kann existieren. Wir können den indischelizer abbestellen,
indem wir trotzdem das Inage-Art verwenden. Ja. Okay.
Make-Eis-Becher 2, jetzt wird gewürfelt und da kriegen wir dann Walnuss-Erdbeer.
Ja, oder wir erzeugen einen anderen, nochmal aufrufen,
wird wieder gewürfelt, bis Taz für Vanille.
Also sie haben viele Möglichkeiten, schon in der Klassendefinition,
die Initialisierung zu beschreiben, plus eben,
dass wir mit Keywords hier Im Konstruktor mit Keyword-Arguments in der Parameterliste spezielle anwählen.
Das heißt, sie müssen in Zelos nicht für die jede Art von Argumenten,
die sie haben wollen, eine eigene Konstrukt der Funktion schreiben,
sondern es reicht in vielen Fällen einfach über die Argumente hier.
Diese Schlüsselworte dafür zu sorgen, dass Werte initialisiert werden oder nicht.
Eine andere Möglichkeit, was ich auch sehr spannend finde,
ist, dass wir bestimmte Funktionen zum Teil des Objektes machen können.
Das können wir in Japan nicht tun,
dass die Klasse selbst als Attribute Funktionen hat, vom Typ Funktionen.
Und das macht es aber im Funktionalen Programmierspiel sehr schön einfach.
Ich kann hier für die Klasse Ranked Cue sagen,
jedes Objekt hat eine eigene Funktion als Wert und diese Funktion sagt,
was der Rang sein soll für ein Objekt innerhalb dieser Klasse.
Rankp, man sieht hier Type Function. Wir haben gesagt,
es soll eine Funktion sein. Wir haben einen Rüder,
wir können es initialisieren mit einem Initialisierungsargument.
Und damit haben wir jetzt in die Klasse Frank Q mit eingebaut,
dass es, dass ein Objekt, was daherkommt,
eine Funktion hat, die seinen Rank feststellt.
Und dann abhängig von diesem Rang können wir beim allen Reihen
in der Schlange nach vorne oder nach hinten
Gereiht werden. Wir definieren mal für die Klasse Q
eine Möglichkeit, den Rang von Objekten zu vergleichen.
Das ist dir jetzt die Methode A. Less, ja.
Und was macht sie? Diese Methode guckt einfach nach für ein Element eins und ein Element zwei,
die beide vom Typ Q sein,
von Item in der Q sein sollen, ja,
dass geprüft wird, ob das der Junk für diese Art von Schlange,
für dieses Objekt größer oder kleiner ist, ne?
Also jede gerankte Warteschlange, die wir erzeugen,
hat ihr eigenes Rankprädikat und das wird genommen,
wenn sich Elemente in der Schlange einstellen und dann wird geguckt.
Also hier wird für die Q dieses Run-Rank-Pea genommen und das
auf Item Wand und Item too angewendet und die werden verglichen.
Und wenn wir jetzt einen Einreihen in diese Schlange machen wollen,
entsprechend des Ranges, könnten wir es zum Beispiel so machen,
dass wir, neues Element ist dazugekommen,
jetzt müssen wir die Schlange sortieren nach dem Rang der Elemente.
Ja, da rufen wir einfach sord auf,
hängen zuerst das neue Element vorne an sortieren es mit dieser Methode Arless für diese Q.
Und fertig. So. Und erzeugen wir mal hier eine solche Schlange.
Wir haben jetzt bisher ja nur wenig Objekte erzeugt.
Ranked. Ich mache eine, erzeuge eine neue Ranked Pue.
Übergebe als Rankprädikat die Identitätsfunktion. Also für eine Zahl hat
den Rank ihres Wertes. So. Und wenn ich jetzt hier
einfach mal NQ aufrufe, zuerst eine 3, deine 1,
deine 2,
eine 4,
Und dann nehme ich die Elemente, entsprechend ihres Ranges raus,
dann kriege ich sie, oh Wunder, in dieser Reihenfolge,
zuerst die 1, die 2, die 3,
die 4, weil jedes Einreihen natürlich dafür gesorgt hat,
dass die 3 hinter die 1 sortiert wurde und die 2,
4, die 3 und so weiter.
Ja, an. Anderer Punkt, der wichtig ist,
das ist die Mehrfachvererbung. Also das haben wir ja schon gesehen,
wenn wir Dev Class schreiben, nahmen wir der Klasse,
Klammer auf, Klammer zu und dazwischen stehen,
die Oberklassen, von denen wir haben.
Und wenn da wir parallel von vielen Klassenerben,
haben wir einen klassischen Fall,
der Mehrfachvererbung
So in Zehlers, verwenden wir diese Mehrfachvererbung,
um Aggregate zu modellieren. Das heißt, ein Objekt,
das Eigenschaften vieler Klassen in sich verbindet, wird,
überzeugen wir in Zehlers ganz typisch durch Mehrfachvererbung. Sagen wir,
wir haben einen Kühlschrank mit Eisfach, ja,
denn würden wir eine allgemeine Klasse Kühlaggregat haben,
wir würden vom Kühlschrank erben und von der Gefriertruhe oder eben etwas,
was dieses Eisfach darstellt und haben dann ein Objekt,
was beides hat
Werden wir in den Übungen mal kräftig üben.
Also ich möchte sie bitten bei dem Aufgabenblatt 9,
was heute verteilt wird,
wo sie schon mal Klassen mit mehr Klassen definieren sollen,
kräftig zur Mehrfachvererbung zu greifen, selbst wenn sie ihnen gegen den Strich gehen sollte.
Und das zu üben, ja. Also Aggregate. Oder wenn wir
mit einer Mix-In-Klasse eine bestimmte Funktionalität dazu bringen wollen, ja.
Machen wir durch Mehrfachvererbung. Oder wenn wir wirklich ein Objekt haben,
was einfach die Eigenschaften mehrerer Klassen in sich vereinigt.
Sagen wir, Wir haben ein Objekt,
es sei ein Student oder eine Studentin, ja,
und wir haben eine Klasse, die die Angestellten einer Firma darstellt, so.
Und dieser Student ist eingestellt und ist, was weiß ich,
pro Kurator der Firma, der hat sich schon ganz schön hochgedient, ja.
Hat trotzdem aber noch die Eigenschaft, dass er oder sie Student ist.
Also kriegen wir ein Objekt mit Mehrfachvererbung, was beides hat, ja.
Und Wie wird vererbt, wenn wir jetzt eine Mehrfachvererbung haben?
Sie wissen ja, dass es bestimmte Dinge gibt,
die man beachten muss bei Mehrfachvererbung, zum Beispiel,
wenn dieselbe, wenn Methoden mit identischen Namen geerbt werden,
ja, von verschiedenen Klassen.
Welche sollen wir denn anwenden? Ja, oder wir haben Attribute,
viele Attribute, sind Attribute bei mit identischen Namen von den Oberklassen.
Welche erben wir? Ja, also eine Klasse in Celos, erbt alle Anwendbadenmethoden.
Ja, und sie erbt die Vereinigungsmenge aller Attribute der Slots, ja.
Wenn aber diese Oberklassen mit den identischen Namen haben,
wird dieser Slot selbst nur einmal vererbt,
aber die Akzessorfunktionen da drauf, die werden alle geerbt.
Also das ist vielleicht ein bisschen genereller, wie das Thema gerade ist,
so eine Verwerbung, aber wieso brauchen wir den Racket eigentlich klassen?
Ich kann es im Jahr schon stehen,
weil da haben wir keine Landerfunktion,
aber hier können wir ja noch einfach alle Funktionen einer Klasse
entheimnis zu tun und alle Variablen in der Klasse in einer Antwort,
weil das ganze Geld ist probiert.
Ja, aber wir möchten, also zum einen,
warum brauchen wir einen Racket-Klassen? Wir möchten Kot erweitern können,
ohne alles. Also wirklich Kot erben und erweitern können.
Ja, das ist wie aus selben Gründen mit Java haben.
Und das Zweite, was wir bei Klassen haben wollen,
ist auch das dynamische Aktivieren und Auswählen von Klassen.
Also das ist wirklich von Typ des Objektes abhängt,
ob ich die eine oder die andere Methode verwende.
Also es werden ein Beispiel noch sehen,
also kommen Sie nachher auf Ihre Frage nochmal zurück,
wenn Sie das immer noch nicht befriedigt sehen.
Das heißt einfach, es ist wirklich eine Hierarchie für zunehmenden
Abstraktionsgrad zum Beispiel, wenn ich Uber und Unterklassen habe
Nehmen wir an, ich schreibe ein Modul, das soll
uns in den Sternenhimmel zeigen. Und wir haben viele unterschiedliche Objektarten,
die den Sternenhimmel bevölkern, einen Kometenplaneten, innere Planeten,
äußere Planeten und so weiter. Wenn ich den Sternenhimmel zeichne,
möchte ich wirklich nur eins tun, das Objekt nehmen und sagen,
gib mir deine Position in Höhe und Breite,
damit ich den Klecks in meinem gezeichneten Sternenhimmel machen kann.
Aber die Bahnrechnung für jedes Objekt denkt von seiner Klasse ab.
Und dieses dynamische ausrechnende Position. Dass es richtig geschieht,
passt uns zur Klasse des Objektes. Das will ich natürlich
in der funktionalen Programmierung genauso haben, wie in der Java-Programmierung,
dass man wirklich nur sagt, gibt Positionen im Koordinatensystem,
horizontiniert das Gym, wenn ich Horizont Zynit zeichnen möchte.
Gut, also deshalb eine Klasse,
mein Planet würde zum Beispiel, wenn es ein äußerer Planet ist,
Bahnberechnungsmethoden für Planeten allgemein üben, plus erben,
plus dann die zusätzlichen Dinge,
die bei dem Großen äußeren Planeten wichtig sind
Und jetzt mal, das klassische Beispiel überhaupt, das ist ein bisschen absurd,
aber man kann sich ganz gut vorstellen, wie es gehen könnte.
Wir haben eine klasse Kleidungsstück. Und haben ganz schön gut modelliert
. Also irgendjemand hat uns eine Klasse für Jacketts modelliert,
ein anderer eine Klasse für Hosen. Und jetzt möchten wir
einen Anzug haben. Wenn ich einen Anzug besitze,
kann ich mit dem Anzug alles machen,
was ich mit dem Jackett machen kann, weil der ein Wisserkett ist dabei.
Ich kann alles damit machen, was ich mit der Hose
machen kann, weil die Hose ist auch dabei.
Und die Klasse deshalb typischer Weg, das in Kommen,
das zu machen, eine Zählersphäre,
wir erben schlichtweg von Jackett und Hose und haben damit unser Objektanzug.
Wir definieren DevClass Jackett. So, das hat eine Größe,
hat eine Initialisierungswertgröße 40 und der Accessor, die Accessor-Funktion dafür ist Größe.
Oh, erinnert mich dran. Wir haben bisher nur in
dem Beispiel einen Rieder gesehen und Reiter.
Wenn ich Rieder schreibe, dann bekomme ich Lesezugriff.
Wenn ich Reiter schreibe, bekomme ich Schreibzugriff. Wenn ich
sowohl Rieder als auch Reiter schreibe, bekomme ich beides,
aber ich kann es abkürzen und sagen, Akzessor,
wenn die Akzessor schreibt, dann existiert ein Rieder und ein Reiter.
So, der kann beides. Also jetzt haben wir hier die klasse Hose,
die hatte einen anderen Initialisierungswert und ich habe den Akzessor dafür größere Hose.
Devklaas, Anzug, Erbt von Jackett und Hose. Ja,
und wenn ich jetzt einen Weg anzug mache, dann sehe ich,
aha, ich habe einen Anzug und die Größe,
wir haben zwei Größenfelder geerbt,
die ist zunächst mal Größe 40. Ja?
Okay. Wenn ich jetzt nach dem Klaas-Lotz frage,
dann sehe ich aber,
dass die Größe in Wahrheit trotzdem zweimal existiert,
angezeigt wird mir immer
nur die erste
Ich habe dir einmal das Feld Größe mit dem Initialisierungswert 40 und dem Akzessor Größe.
Und ich habe einen zweiten mit dem Akzessor Größe Hose.
Also wenn ich nach der Größe der zweiten Klasse frage,
von der ich geerbt habe,
kann ich machen und muss nur den richtigen Akzessor nehmen.
Ich kann jetzt fragen, ist der Anzug eine Unterklasse von Hose, ein Zep-Class?
Ja, und dann kriegen wir hier Klaas Object im Claas-Top.
Und Hose ist Oberfläche, ist eine Oberklasse. Wir können fragen,
ist es eine, das Jackett, eine Instanz von Anzug?
Nee, ist der Anzug eine Instanz von Jackett so rum?
Und dann kriegen wir auch die Antwort, ja,
und übrigens, die anderen Klassen sind auch noch da
Gut, Zelos legt eine Präsidenzliste für die Klassen
an und die ist nach den folgenden Regeln geordnet.
Warum brauchen wir diese Präzedenzliste? Ja, eigentlich aus all den Gründen,
weshalb Ihnen in Jaba eingetrichtet wird, nehmen Sie kein,
es gibt keine echte Mehrfachvererbung, weil es zu kompliziert ist,
sondern wir machen lieber ein Interface, ja?
In Zelos ist man den Weg gegangen, dass man gesagt hat,
wir machen, lassen die echte Mehrfachvererbung zu,
aber wir legen eine Präzedenzliste für die Klassen an.
Das heißt, wir können für die Klassen ihre Präzedenz in
der Vererbung abfragen und kontrollieren und wissen, wie geerbt wird.
Wenn wir uns hier mal jetzt den Anzug,
DevClass-Anzug anschauen, da stehen hier zwei Klassen.
Wir müssen die in einer Reihenfolge nennen.
Ich kann sie nicht gleichberechtigt auf dieselbe Position schreiben.
Das heißt aber auch, das Jackett hat Präzedenz vor Hose.
Wenn wir irgendwo Dinge doppelt erben, wird als Default immer
zuerst das von Jackett genommen und dann in zweiter Linie von Hose.
Und das ist in der Klassenpräzedenzliste geregelt.
Jede Klasse hat erstmal Vorrang vor ihren Oberklassen
Etwas neu modelliere, neue Attribute einführe, neue Slots,
dann wird der Slot gewinnen in der Klasse,
die ich gerade erweitert habe.
So, wie es in Java auch ist.
Jede Klasse legt die Präzedenz der direkten Oberklassen fest.
Das ist die Liste, die wir eben gesehen haben.
Ja, Jackett Hose, in der Reihenfolge wäre die Präzedenz geregelt.
Und außerdem darf man Klassen nur so definieren,
dass eine strikte Ordnung festgelegt werden kann.
Wenn das Recht jetzt nicht möglich ist, weil wir dreimal
im Kreis erben, dann ist es nicht zulässig.
Es muss eine strikte Ordnung mit Hilfe dieser beiden Regeln
da sein
Und wenn das existiert, dann haben wir eine legale Klassenpräzedenz.
Ja. So Methoden mit höherer Präzedenz,
überladen und verschatten die Methoden der geringeren Präzedenz.
Nehmen wir ein Beispiel, ich habe hier vier Klassen,
MA, MB, MC, MD.
Ich erbe hier, definiere eine Klasse A,
die erbt von MA und MB und eine Klasse MCD,
die erbt von den beiden.
Und dann habe ich hier noch eine, die erbt wiederum
von den beiden. Da ist die Klassenpräzedenz geregelt.
Also was von MAB geerbt werde, hätte Vorrang von dem,
was von MCD geerbtet,
wenn ich hier bin
Und hier wurde MA vor MB-Rezedenz haben und hier MC vor MD.
Ja? Wir können es mal direkt so hinschreiben.
Klaas MA definiert MB, MC, MD, ja.
Und dann diese Klassen, die von A und B
erben und von MD und MC.
Und dann die letzte Mehrfachverarschung. So. Ja. Und der Klassenpräzedenzkraft,
den können wir abfragen. Das ist die Methode Klaas,
CPL,
ja,
Claas Precedent List
Wenn ich das jetzt mal für diese Klasse MA BCD aufrufe,
dann kriege ich die Liste aller Oberklassen aufgelistet in der Reihenfolge,
wie sie Präsidents haben und die geringste Präzedenz hat ganz hinten die Klasse top.
Also das ist strikt geregelt, wir können es immer rauskriegen,
was wir wie erben, da braucht man keine Angst vorzuhaben.
Ja? Okay. Wenn wir auf mehreren Wegen erben können,
dann wird es langsam ein bisschen kritisch. Also hier,
das ist natürlich möglich, dass man es so schreibt,
dass diese Klassen alle von der erben,
aber es gewinnt dann der direkteste Weg auf dem Weg der
höchsten Präzedenz.
Wir erben es damit nicht vierfach
So. Ja. Das ist das, was rauskommen würde.
Hier? Wieso kommt das raus? Jedenfalls in der Reihenfolge verarbeitet wird,
man geht bei jeder Klasse zuerst nach den Oberklassen und landet da.
Ja. Also deshalb muss man sich einfach überzeugen.
Einfach mal aufrufen und anschauen, dass man da programmiert hat.
Dann weiß man es, ne? Ja,
also die, in dem Fall würde Dieses MCD abt ja
von dem und das von dem und das von dem.
Und es wirbt aber auch von dem und der das zuletzt ausgerechnet wird,
würde in dem Fall einfach der Weg,
der von hier gekommen ist, Vorrang haben von dem,
der hier möglich wäre.
Also es ist nicht trivial, aber wir können uns eben überzeugen.
Ich würde es vermeiden, das so zu erben.
Ja. Gut, es ist eigentlich nur dann wichtig,
wenn wir das machen, was ich jetzt ansprechen möchte.
Die Frage ist, Ich habe es eben falsch dargestellt, ja.
Was wir hier sagen, der Punkt ist jetzt wirklich wichtig.
Wenn wir erben, wir erben es auf vielen Wegen
und wir erben viele Methoden, ja.
Und wenn unterschiedliche Ausprägungen der Methoden desselben Namens existieren,
dann ist zunächst mal die Frage, welches wird angewendet.
Ja, das ist das eine. Aber In der Zählerswelt
stellt man sich nicht eigentlich die Frage, welches wird angewendet,
sondern wie bestimme ich, welches angewendet wird.
Und will ich nicht vielleicht alle anwenden. Ich meine,
das ist doch verschwenderisch. Ich erbe drei Methoden.
Warum soll ich wie in Java nur eine davon nehmen?
In Feders kann ich sagen, prima,
ich habe drei geerbt, ich nehme alle drei.
Und in welcher, und die Frage ist,
in welcher Reihenfolge wende ich sie an?
Und wie kombiniere ich das geschickt? Also,
wenn ich für ein Objekt viele Methoden geerbt habe,
da hat sich ruhig immer die Mühe gemacht,
die alle zu schreiben
Deshalb soll ich mich für eins davon entscheiden.
Und gelegentlich kann es wirklich sinnvoll sein,
dass ich alle ausführe und das alle ausführen, geschickt kombiniere.
Können Sie auf dem Übungszettel, den Sie heute kriegen,
auch üben, ja? Okay. Was mir das ermöglicht,
das geschickt zu machen, das ist, dass wir Methodenkombinationen haben.
Ja, das ist eigentlich das A und O.
Kommen wir mal wieder zu unserem Anzug zurück. Wir haben da
einen Feldpreis, ja? Das wäre dann wirklich absurd,
wenn ich für den Preis eines Anzugs mich entscheiden muss,
verkaufe ich den ganzen Anzug zum Preis der Hose oder zum Preis der Jacke.
Beides wird gleich dämlich, ne? Der Anzug als Ganzes
sollte teurer sein. Und deshalb,
was man in der Zählungsfeld macht, ist einfach,
dass man sagt, nimm beide Preismethoden und addiere die Preise.
Ja? Oder da möchte jemand eine Auskunft haben über die Farbe des Anzugs.
Wir haben jetzt die Farbe der Hose geerbt und wir haben
die Farbe der Jacke geerbt. Wenn ich Auskunft gebe,
der Anzug ist grün, weil die Hose grün ist,
dann ist es genauso falsch, als wenn ich sage,
es ist rot,
weil die Jacke rot ist.
Man müsste eigentlich
beide Farben zurückgeben
Und das ist das, was wir uns jetzt
nochmal bei Methodenkombinationen anschauen können, für die Kleidung.
Ja, wir haben hier eine klasse Kleidung, da ist die Größe drin.
Wir haben eine abstrakte Methode Preis, eine abstrakte Methode Farbe.
Jackett implementiert Preis und Farbe. Hose implementiert Preis und Farbe
. Und wir haben den Anzug. So,
und wollen jetzt für den Preis und Farbe haben.
Und das würde ich in Zielers
so machen
Wir sagen, der Preis verwendet eine Methodenkombination. In der Definition,
wo wir die Signatur für die Generic Function Preis festlegen,
Preis von Kleidung, da sagen wir Generic Plus Combination.
Was heißt, wenn es viele Implementationen für den Preis gibt, die ich erbe,
dann werden die Preise die Methoden alle aufgerufen und die Preise addiert.
Für die Farbe, da möchte ich für alle Kleidungsstücke
die Farbe haben, für alle Teile des Aggregates.
Da verwenden wir Generic Appent Combination. Das heißt,
die Farbe muss so implementiert sein, dass sie eine Liste
der Farben zurückgibt, Und die werden einfach mit Appellen verbunden.
Wenn ich jetzt hier sage, DevClass Jackett ist Kleidung,
Kurse ist Kleidung, Anzug ist Kleidung.
Und ich implementiere fürs Jackett eine Preisfunktion, Dev Massive Price 20, für die Hose 40.
Ich implementiere eine Methode für die Farbe, sei rot,
für die ist Jackett und blau für die Hose.
Und ich erzeuge jetzt einen Anzug, Preis,
Make Jackett, 20, Preis-Make-Anzug, kommt 60 raus.
Warum? Weil der Preis für Hose und Jacke addiert wird. Oder Farbe von Milk-Anzug,
da würde ich jetzt die Liste rot und blau zurückkommen,
weil für die Fischer-Kette kommt die Liste rot zurück für die Hose
blau und dank der Generic Appent-Combination werden eben die Resultate für die Farbmethode kombiniert.
Und das ist eigentlich das, was mir Besonderes und Spaß
macht beim Arbeiten mit Mehrfachvererbung.
Wenn von den Klassen viele Methoden geobbt werden,
Muss ich mich nicht für eine entscheiden und das nach schlechten Regeln.
Ich kann es, ja, aber ich kann genauso gut sagen,
nimm alle und kombiniere sie. Passend für das Problem.
Also das ist ein ganz typisch für den funktionalen Programmierstil,
dass man sagt, Methode, wieso?
Das ist doch eine generische Funktion. Ich kann Funktionen kombinieren
, ja. Und da beschreibe ich eben, wie sie
kombiniert werden sollen, wie deren Resultate kombiniert werden sollen und fertig.
Und kann damit sehr elegant die Erbschaftskonflikte lösen.
Ja, das ist also die eine Art Methodenkombination.
Wir verbinden einfach alle Methoden,
die wir geerbt haben
Und die Liste der Vordefinierten ist Generic Plus.
Also wenn ich Methoden kombinieren will, die ich geerbt habe,
die Zahlen zurückgeben, kann ich Plus nehmen oder Minus, ja.
Ich kann Minimum, Maximum, ich kann die Resultate zu einer Liste verbinden.
Ich kann Listen mit Appent zusammenfügen. Das hier ist destruktiv,
das verbindet sie wirklich, sodass muss man ein bisschen aufpassen.
Ich kann mit Beginn und End, das sorgt einfach dafür,
dass sie der Reihe nach ausgeführt werden.
Und wenn sie durch Seiteneffekte wirken, werden diese Seiteneffekte eben akkumuliert.
Und dann haben wir noch End- und Ohr für Williams
und man kann auch selbst seine eigene Methodenkombination schreiben.
Ja, das ist dann die Meisterklasse,
indem man seine eigene Methodenkombination definiert für diese Klasse.
Ja, was man auch gut machen kann bei der Methodenkombination,
das ist, dass man die Funktionen nimmt,
die die Accessor-Funktionen für die Slots sind.
Das sind ja auch generische Funktionen,
dass somit die auf die Slots zugreife, sind generische Funktionen.
Und ich kann die Accessor-Funktionen auch kombinieren und kann damit eben
den Zugriff auf die Slots durch Methodenkombinationen bestimmen
Also wenn ich das hier jetzt für den Preis mache,
ja, Deptaneric Price, und sagt, ja,
da ist die Methodenkombination plus.
Das Jackett hat einen Preis fürs Jackett.
Hose hat einen Preishose, Deptant Class Anzug, ja.
Dann können wir einen Preis für den Anzug erzeugen über Preis 400,
weil wir die alle hier addieren, ja.
Ach, 20 kam noch ein bisschen. Der nächste spannende
Teil für Methodenkombination ist eine ganz andere Art der Methodenkombination.
Also eben haben wir ja vor allem drüber gesprochen,
wie lösen wir Abschaffskonflikte. Wir erben viele, viele Methoden,
sollen uns für welche entscheiden oder wir sagen,
ach Gott, nehmen wir sie alle.
Und verbinden sie, indem wir eine Funktion vorgeben,
die die Resultate all dieser Anwendungen kombiniert.
Das andere betrifft Spezialisierungen von Klassen. Also wenn ich Klassen spezialisiere und habe dann eine Methode,
In der Oberklasse, die ist gut,
die tut schon fast alles zu 90 Prozent, was ich brauche,
aber ich möchte ein bisschen davor, ein bisschen danach machen.
Das ist der Fall,
wo sie in Java sagen würden, na gut,
ich rufe mit einem Supercall die Methode der Oberklasse auf und tue
ein bisschen davor und ein bisschen danach und habe eine neue Methode,
die die Alte verschattet.
Das kann ein bisschen unsicher sein, wenn sie nicht genau wissen,
wie weit sie eigentlich in der Hierarchie nach oben geben.
Und es kann sein, dass schon die Methode davor eigentlich die,
die sie meinen, verschattet hat,
indem sie auch ein bisschen was davor und danach gemacht hat.
Das kann man sorgfältiger und systematischer mit Eine andere Art von
Methodenkombinationen lösen, indem wir Vor- und Nachmethoden definieren.
Die Ergänzungsmethoden. Also bei den Ergänzungsmethoden können wir beim Ergänzen von geerbten Methoden
einfach nur den Teil separat beschreiben, der davor gestehen soll.
Oder den Teil separat beschreiben, der danach geschehen soll.
Und diese Teile werden auch mit vererbt. Und in der
Methodenkombination mit berücksichtigt. Und die sind kenntlich durch die Schlüsselwörter
before around and after. Das sind einmal die Vormethoden,
Dinge, die vorher gemacht werden, Dinge,
die nachher gemacht werden und Dinge,
die praktisch einen Supercall machen und das einhüllen.
So. Und dazu auch mal ein Beispiel aus dem Buch von Gremen,
was ich sehr schön finde. Wir haben eine Klasse von Menschen
, die reden. Ja, also zumindest meine Oberklasse,
die Klasse des Menschen, der redet, heißt Speaker.
So, und dann gibt es eine besondere Abart.
Im Gegensatz zum normalen Redner, der einfach alles so heraussagt,
wie es ist. Ja, so haben wir Intellektuelle.
Die sind ein bisschen vorsichtiger und machen vorher etwas und nachher etwas,
um das, was sie zu sagen, etwas abzuschwächen,
damit sie nicht angreifbar sind.
Und dann haben wir noch ein Diplomat,
der das noch weiter abspecht und der hat noch eine Methode Speak,
die noch ein bisschen mehr davor macht.
Das gucken wir uns mal an, ja? Also,
wir haben hier unsere Klasse Speaker, die Abt von nichts,
das ist die Uberklasse. Und die Methode Speak, ja,
für die Klasse Speaker, der kann einen Text sagen. Und dieser arme Mensch,
der redet einfach alles so raus wie in der Schnabel,
gewachsen ist, er zeigt das, was er sagen soll,
einfach an, ohne Vorsichtsmaßnahmen, nichts vorher, nichts hinterher, ja?
Also, wenn ich sage, hier, die feinen S,
make speaker, und dann sage ich es,
rufe ich die Methode Speak auf, speak ass,
I'm Hungry, dann wird er sagen, I am Hungry,
ja, jetzt nehmen wir mal einen Intellektuellen,
der schwächt es ein bisschen ab
Also man möchte ja nicht angreifbar sein,
sondern sag erst mal, lieber vorher, als sink.
Ja, und das können wir so machen,
wir erben die Methode Speak vom Speaker.
Hier ist sie ja, hier ist fertig da, ne?
Aber die Klasse intellektuell, die hat eine Vormethode.
Also bevor jemand spricht, wird das Gehirn eingeschaltet.
Und er sagt dann, I think, ja? Also Death Messed Speak before,
kennen wir, bevor ist wichtig, das führt dazu,
wenn immer die Methode Speak ausgeführt wird,
für ein Objekt der Klasse Speaker,
wird vorher dies gemacht.
I think
Und dann wird das Big ausgeführt. Und der ist jetzt
besonders vorsichtig. Hinterher sagt er auch noch, und übrigens,
Inzant Sense, ja, dann sind wir ganz sicher.
Also ein Speak hat eine After-Methode für einen Sprecher der Klasse Intellectual,
der, wenn der einen Text sagen soll,
wird hinterher auch noch in Samt Sense ausgegeben, ja.
Also wenn ich jetzt meinen Intellektuellen sprechen lasse,
einem hungry, die fine Eye, make intellectual.
Speak, I, I am hungry, I think,
I am hungry in some sense
Vorher wird es als Sink ausgegeben und hinterher in Sam Sense
und die Methode Speak wird genommen, wie sie ist.
Ich muss nicht irgendwie drinnen erstmal einen Text schreiben und da verklauselieren,
wo dann der Supercall gemacht wird. Der ist automatisch implizit,
das Speak der Oberklasse wird immer ausgeführt,
aber wir haben es einfach ergänzt und man sieht sofort gebündelt,
was wird vorher gemacht, weil es wird nachher gemacht.
Wenn wir jetzt noch den Diplomaten nehmen,
das ist eine verschärfte Form des Intellektuellen,
der sagt vorher auch noch ein paar Herbst,
damit überhaupt keine Zweifel mehr übrig bleiben.
Der macht jetzt einen Speak before, Diplomat des play per hups.
So. Und wenn ich jetzt den Diplomaten erzeuge und er soll sagen,
the world is round, per hups,
I think the world is round in some sense, ja.
Was passiert? Alle geerbten Vormethoden werden auch ausgeführt,
alle geerbten Nachmethoden werden ausgeführt und die neue Formmethode auch, ja.
Das heißt, man sieht im Code zum einen,
genau was vorher nachher gemacht wird, das ist ausgewiesen als einzelne Vor- und Nachmethoden.
Es ist nicht in der Methode selbst verwurstet in einem Code,
wo man irgendwie drin an einer Stelle dann noch den Supercall macht,
sondern geerbte primäre Methode wird ausgeführt.
Vorher werden alle Vormethoden ausgeführt, hinterher alle Nachmethoden,
die geerbt wurden. So, zunächst werden alle Anwendbaren
vor Methoden ausgeführt und wir beginnen mit der spezifischsten.
Ja. Dann wird die spezifischste Prittmehrmethode ausgeführt, ja.
Und danach werden alle Anwendbaren nach Methoden ausgeführt.
Beginnt bei der Allgemeinsten, ja. Also,
wenn wir hier mal so eine Klasse A haben,
die eine Klasse B, die von Klasse A erbt,
Klasse C,
ab von Klasse B und wir machen hier
einen Test für Klasse
Macht nichts weiter als ein Rightline-Klasse A. Ja,
und ich habe die jetzt vor Methoden eingeführt, Before und After,
die schreiben einfach nur vor, B nach, BVC, nach C.
Ja, dann können wir genau sehen, was geschrieben wird.
Wenn ich Teste von C mache, für ein Objekt der Klasse C,
das wird zuerst das VC, VB, dann Klasse A,
ausführender Nacht, B, dann Nacht C.
So können wir sehen, dass alle Vor- und Nach-Methoden ausgeführt werden.
Und ich finde, diese Methode sehr sicher, wenn es
um Initialisierungen geht. Also wenn wir spezialisieren, ja,
dann wollen wir ja sicher sein, dass bestimmte Initialisierungen ausgeführt werden,
die wir eingeführt haben, weil sie wichtig sind.
Wenn jemand mit einem Supercall einfach an unseren Initialisierungen vorbeispringt und super,
super, super von Aufruf, ja, drei Klassen drüber,
dann würden die Initialisierungen, die wir in unserer eigenen Spezialisierung geschrieben haben,
verschwinden.
In Zelos, da können wir sehr sicher sein,
wenn wir Initialisierung designen müssen,
in eine Vor- und eine Nachmethode reintun,
die werden auf jeden Fall ausgeführt
Es ist garantiert, dass das, was wir an Initialisierungen
gemacht haben und worauf wir in unserem Kurt vertrauen,
dass das nicht mehr verschattet werden kann.
Es wird auf jeden Fall ausgeführt und ist in den Verfeinerungen da, ja.
Das sind, also das in der imperativen Welt ein Problem ist,
sieht man immer dran, wenn man so mit Grafikpaketen arbeitet,
wie zum Beispiel, ja, bei Swing, ne?
Wenn ich da in die Dokumentationen reingucke, dann steht,
ja, rufen Sie es auf, das und das können Sie aufrufen,
aber bitte, bitte nicht vergessen,
einen Call super für Init für die Klasse muss geschehen,
sonst stürzt es ab
Wenn jemand nicht die Doku liest, hat er Pech gehabt.
Und wenn er den Supercall fürs Inded nicht macht,
ist ein Problem da. Wenn ich denselben Code in Zählers schreibe
, dann kann ich erzwingen, dass die Initiatisierung stattfindet.
Also diese Vormethoden werden alle, alle ausgeführt, die Nachmethoden auch.
Allerdings bei der primären Methode über die speziellste nur ausgeführt.
Und das heißt, die Initialisierung, die in den früheren
Varianten da sind, haben nicht stattgefunden. Deshalb alles,
was kritisch ist und an Initialisierung da sein muss,
in die Vormethoden stecken, die werden auf jeden Fall ausgeführt.
Da kann man
sicher sein
Okay. Also das ist für mich der wichtigste Unterschied beim
Supercall, wenn es um die Initialisierung von Objekten geht.
Wir können bei den Ergänzungsmethoden sicher sein, dass in keiner
der Schichten da drunter unsere Initialisierung abgestellt werden können.
Die werden gemacht. Außer jemand ignoriert unsere Methode und schreibt eine völlig neue,
mit einem neuen Namen, dann hat er aber auch selbst schuld.
Okay. Wenn wir das Ganze jetzt verwenden wollen,
Habe ich als nächstes ein Beispiel aus dem Bereich der Ereignisorientierten Simulation,
das werden wir in der nächsten Vorlesung ziemlich ausführlich besprechen.
Wir können schon mal reingucken, was es tun können soll.
Also Simulation ist eigentlich der Klassiker für die Objekte orientierte.
Eine Programmierung, wo es mit populär gemacht worden ist,
sind ein Simulat zum Beispiel. Und ich habe in hier
über eine Hierarchie von verschiedenen immer stärker spezialisierten Paketen ein System geschrieben,
was zeigt, die Stärke der Initialisierung mit Vor- und Nachmethoden,
was zeigt, wie wir mehrfach Werbung nutzen können bei der Vererbung.
Und was auch die Stärke zeigt,
dass wir bei Recket die Methoden nicht einzelnen Klassen zuordnen müssen,
sondern sie zwischen den Klassen geschrieben sind.
Ja, warum ist die Simulation so ein insgesamtes Beispiel fürs Ganze?
Also bei der Simulation haben wir ja ein Modell der Anwendungsdomäne,
in dem wir Experimente durchführen können.
Und das Sinn und Zweck ist, dass wir das verwenden,
um Vorhersagen machen zu können für die korrespondierenden Objekte in
der realen Welt
Macht man immer dann, wenn der Versuch in der
realen Welt nicht möglich ist. Sagen wir mal,
wir wollen simulieren, ob unsere Raummission zum Mars gelingt.
Wir können nicht erst hinfliegen und da das Experimentieren.
Wir brauchen eine gute Vorhersage, ob unsere Astronauten überleben werden,
den Flug dahin. Oder wenn es einfach zu teuer ist
oder wenn es zu gefährlich ist.
Und wir haben bei der Simulation im Computer eine Verschleißfreie Simulation,
weil dasselbe Experiment immer wieder wiederholen können und das vervielfachen können und anpassen.
Und das geht meistens schneller
So, das machen wir bei der Simulation. Wir entwerfen
ein Modell der Anwendungsdomäne durch Abstraktion und da müssen wir sicher sein,
dass alles, was tatsächlich wichtig ist, auch vom Modell erfasst wird.
Das ist eigentlich der wichtigste und kritischste Schritt,
dass wir nur Dinge vernachlässigen, die nicht wichtig sind und alles,
was wichtig ist, explizit auch im Modell aufnehmen.
Dann müssen wir unser Modell, was wir geschaffen haben,
validieren, dass der nächste kritische Schritt, ja,
es kann ja sein, dass wir Fehler gemacht haben.
Dann verwenden wir das Modell, um die Simulation zu machen,
kriegen Ergebnisse und dann machen wir die Rückübertragung auf die Anwendungsdomäne
Man macht auch gerne Simulationen mit konkreten Modellen. Zum Beispiel,
wenn Sie mal unsere Schiffsbauversuchsanschalt besichtigen, die haben im Maßstab 1
zu 100 Wachsmodelle von Schiffen und die können Sie im Windwellenkanal ausprobieren.
Und natürlich, das mit konkreten Modellen zu arbeiten,
hat den Vorteil, dass man bestimmte Dinge nicht vergessen kann.
Das konkrete Modell ist genauso konkret wie das reale Modell.
Man muss es
nur gut gemacht haben
Allerdings übersteht so ein Wachsmodell von einem Schiff, die Versuche nicht zu oft.
Und man muss sie immer wieder neu machen, ja,
die Modelle im Computer sind verschleißfrei, ne.
Die können wir beliebig oft reproduzieren. Das heißt,
wir können einen Millionen Computer dran setzen. Eine ähnliche Simulation
zu machen. Wir können sie meistens auch schneller modifizieren,
solange wir unser Programm noch verstehen, ne.
Und sie können die Experimente auch selbst protokollieren, ja.
Und so ein Klassiker der Simulation ist die Zeit diskrete Simulation.
Ja, und das ist das, was mein Simulationssystem auch kann.
So entlang eines Zeitstrahls haben wir Ereignisse Und immer,
wenn ein Ereignis eintritt, wird etwas getan,
der Zustand der Welt geändert, dann wird vorhergesagt,
wie der nächste Zustand der Welt aussehen wird.
Und wir haben dann wieder einen nächsten Termin, Termin, Termin, Termin.
Zum Beispiel verwendet man das gern für Kundenbediensysteme, wenn man eine Warteschlange haben.
Ja, jemand stellt sich irgendwo in einer Bedienstation an.
Und was so in Simulationen dann System können muss,
ist, dass es eben Ereignisse generieren können muss,
so was wie Kunde eins kommt, könnte eins,
zweit bedient, Kunde eins geht und so weiter.
Und wir haben eine lange,
rauenlange Warteschlangen auf
Ereignissen, die irgendwann eintreten sollen.
Ja, und zeitgetaktet wird immer geguckt im Simulationssystem.
Was ist das nächste anstehende Ereignis, was jetzt eintreten soll?
Die Welt wird entsprechend verändert. Es wird dafür gesorgt,
dass neue Ereignisse eintreten als Folge davon,
damit unsere Warteschlange von Ereignissen nie leer wird.
Das ist die Zeit diskrete Simulation. Man kann damit
manche Sachen nicht machen. Eigentlich alles, wo es
so etwas kontinuierlich ist und wichtig ist, ja.
Die Wege zwischen den Punkten, die werden einfach ignoriert
Aber die ersten Klassiker für solche Zeitdiskreten-Simulationssysteme,
die sind in der Welt der imperativen Programmierung erfunden worden und
habe hier mal aus einem uralten Pascal-Programm die Schleife übernommen.
Wir haben so eine Repitch-Schleife, wiederhole bis Simulationszeit abgelaufen.
Hohle aus einem Kalender mit anstehenden Ereignissen, das nächste Ereignis heraus.
Wenn das Ereignis Ankunft eines Kunden ist,
dann planen wir das nächste Kundenereignis.
Warum unsere Schlange von auszuführenden Ereignissen soll nie leer werden.
Das heißt, wenn wir einen Kunden bedienen,
müssen wir dafür sorgen,
dass wieder
ein neuer kommt
Wenn die Schlange leer ist, dann bediene den Kunden.
Ansonsten wird er in der Warteschlange eingereiht. Ja,
in der Bedienung abgeschlossen ist, verlässt der Kunde die Warteschlange
und wir nehmen den nächsten Kunden und so weiter,
falls die Schlange nicht leer ist.
Und das würde man wiederholen, wiederholen, wiederholen.
Ja. So, für ein solches System habe ich
in ein System geschrieben, in Zelos.
Sie finden das in der SE3-Bib unter dem Namen SIM und darauf
aufbauend verschiedene Ausprägungen. Das eine ist so ein Mensa-Szenario,
wo wir testen können,
Wie schnell ist der Durchsatz, dass Studenten schnell genug
hier Essen kriegen und dass sie noch verspeisen können,
solange es noch warm ist.
Da sind Spezialisierungen drin, wie eben Klassen von Vegetariern und nicht Vegetariern.
Und außerdem habe ich ja noch ein Bedienensystem fürs Einkaufen,
mit Asterix und Obelix, das nennt sich Arimurika-Szenario.
Das können wir an diesem Beispiel alles sehen,
wie man die Bedienung von der Klasse der Objekte
abhängig macht
Als Basis dafür, dass man so ein System lebendig hat
, brauchen wir ein bisschen Statistik. Und das können wir
heute vielleicht ja noch besprechen in den letzten fünf Minuten.
Dann sind wir das nächste Mal dafür gerüstet,
dass wir direkt in die Systeme reinschauen können.
Also wenn wir große Mengen gleichartige Objekte haben,
dann möchte man die nicht alle explizit einzeln modellieren müssen,
sondern eher in einem statistischen Programm eben viele,
viele Ausprägungen erzeugen, die alle leicht variieren.
Und auch die Simulationsereignisse als Zufallsgrößen wollen wir auch durch Verteilungen
beschreiben, damit wir auch die automatisch erzeugen können
Eine einfache Art, etwas zu machen, wäre eine Gleichverteilung zu nehmen,
dann wird das System allerdings ein bisschen langweilig,
aber wir nehmen ja die Gleichverteilung als Basis für alles.
Also bei der Gleichverteilung, das können wir nehmen für Ereignisse,
zu jedem Zeitpunkt gleich wahrscheinlich auftreten können.
Beispiel, ob ein Kunde kommt. Da hätten wir
eine Wahrscheinlichkeitsdichte P von XSC einfach konstant. Wenn wir,
würde auch ein Zerfall des Atomkerns, könnten wir so modellieren,
zu jedem Zeitpunkt ist Gleichverscheinliches und Kernchen zerfällt.
Das Interessante ist jetzt, Die zeitliche Verteilung der Abstände zwischen
gleichverteilten Objekten. Selbst wenn zu jedem Zeitpunkt ist,
gleichwahrscheinlich ist das ein Ereignis eintritt, können die Abstände variieren.
Und das können wir durch den negativen Exponentialverteilung beschreiben.
Also eine Wahrscheinlichkeitstüchte, die so aussieht, irgendein Mittelwert,
mal eh hoch-minus-myth, mal Tee, wobei Tee die Zeit ist.
Ja, und das Mühe ist größer als null.
Und diese Form einer Verteilung hat eine typische Halbwertszeit.
Also je länger wir warten, desto wahrscheinlicher wird es,
dass das Ereignis eintritt.
Und die Wahrscheinlichkeit fürs Ereignis verdoppelt sich mit konstanten Abständen.
Wenn ich negativ verteilte Ereignisse ziehe, dann bekomme ich so diesen typischen Verlauf.
Also das ist die Wartezeit zwischen zwei Ereignissen.
Die wird immer größer, größer, größer und dem warteten
die längeren Wartenzeitenden sind weniger wahrscheinlich als die kurzen Wartezeiten.
Also diese Verteilung nimmt in einem Simulationssystem,
wenn zu jedem Zeitpunkt ist gleich wahrscheinlich,
ist es ein Kunde kommt und ich möchte die Wartezeit zwischen
zwei Kundenereignissen haben,
dann ziehe ich sie mit dieser Negativ-Exponentiellen Verteilung
Wenn ich Ereignisse habe, die im Mittelwert immer gleich lange dauern,
aber leicht variieren können. Also so stellen Sie sich vor,
Sie stehen mit Ihrem Mensa-Tablett in der Schlange und
jemand klatscht in den Eintopf auf den Teller.
Im Mittel geht das immer mit demselben Schwung und mit derselben Zeit,
aber es variiert leicht. Und dann nimmt man gerne die Normalverteilung,
sagt im Mittelwert dauert es einen Topf in die Badewanne klatschen,
eine Minute oder so und plus minus.
Dann nehmen wir eine Verteilung von dieser Form und beschreiben es
durch den Wert und die Varianz.
Und die würden so aussehen ungefähr. Von der ganzen Glockenkurve.
Für 1000 Normalzeitscheite Ereignisse gezogen. So, wie kriegen wir
gleich verteilte Zahlen? In Recket haben wir ja die Funktion
random und die ist ganz gut.
Mit der können wir zufällige Zahlen aus dem Intervall ziehen.
Man kann gleich verteilte Zeilen, auch mit Zahlen, mit der Konkurrenz.
Da gibt es einen ganz kleinen Tick leiser.
Ich habe Mühe, dagegen anzuschreiten. Wir können mit der Konkurrenzmethode
sie selbst erzeugen. Das würde ich immer dann tun,
wenn man feststellt, dass der eingebaute Random-Generator nicht gut ist
und nicht echt gleich verteilt ist
Also man, der basiert einfach darauf, dass man zwei
große Primzahlen hat und mit denen nach dieser Formel die beiden Faktoren hier miteinander vermanscht.
Ja, wenn wir eine andere Verteilung brauchen, sagen wir,
wir haben Normalteil der Ereignisse, brauchen aber die Umkehrfunktionen dazu,
dann können wir dichte Funktionen transformieren.
Also wenn wir eine dichte Funktion haben,
die integrierbar ist und wofür Unbestimmtes Integral, die Umkehrfunktion existiert,
dann können wir aus Normalfeld, aus Gleichverteilten Zufallszahlen jede andere Dichte ableiten.
Und das habe ich Ihnen auch eingebaut in das, in die SE3,
ja, wenn Sie jetzt negativ exponentiell verteilte Zahlen haben wollen,
dann betrachten Sie die Umkehrfunktion des Integrals,
das wäre nämlich dieser Verlauf hier.
Und wenn man da jetzt mit gleichverteilten Zeilen reinkriegt,
dann ist das, was herauskommt, genau die Original,
eine Verteilung,
die der Originalfunktion entspricht.
Das Basissystem werden wir das nächste Mal besprechen.
Wir werden sehen, Klassen für die Akteure,
die in der Simulationswelt tummeln, die wir später spezialisieren werden.
Wir werden eine Ereignisquellen haben, wir werden Ereignisse haben,
Kalender, Protokollfunktion und Dienstleistung und dann mal sehen,
was man damit alles machen kann.
Für heute, danke,
tschüss für die Aufmerksamkeit