Hallo, herzlich willkommen zur siebten Vorlesung SE1. Das ist ein kleines Projekt,
was ich gemacht habe, um zu zeigen,
was man mit Turtle Graphics für sensationelle Dinge tun kann.
Das zeichnet hier so relativ einfach, definiert so ein paar Spiralen.
Das geht natürlich viel, viel besser.
Und deswegen startet diese Woche nämlich auch der TGCC
2013. Jetzt fragt ihr euch sicherlich, was ist das denn?
Das ist der Turtle Graphics Creativity-Contest
Denn ich denke, ihr könnt das
deutlich besser als ich mit so ein paar Spiralen.
Das habe ich die letzten Jahre eigentlich immer gemacht,
dass ich hier so einen kleinen Wettbewerb gestartet habe vor Weihnachten.
Und bei diesem Wettbewerb geht es darum,
dass ihr Turtlegraphics möglichst kreativ verwendet, um irgendwas zu zeichnen.
Die Teilnahmebedingungen dazu, die werde ich ab morgen im Kommensee haben,
dann gibt er wahrscheinlich so zwei Wochen.
Und dann werden wir vor Weihnachten dann auch noch so eine Preisverleihung haben.
Nur, dass sie mal so einen Eindruck bekommt,
was frühere Gewinner so gemacht haben.
Hier ist zum Beispiel ein
sehr schönes Projekt
Jetzt muss ich überlegen, wie war das hier, New Pong?
Ich glaube, so war das. Da muss ich hier
irgendwie sagen, starrt. So, und dann kommt hier
sowas. Ich weiß nicht, ob ihr Pong kennt.
Das ist eines der ersten Videospiele, die man früher auf
diesen Röhrenfernsehern gespielt hat. Links und rechts,
jeder hat so einen Controller und dann fliegt der Ball hin und her
und man kann so mit so einer relativ groben Grafik hier Pong spielen.
Das ist jetzt hier, ohne dass ich was tue,
ja, das läuft von alleine. Sensationell. Die haben das noch so ein bisschen,
also das ist ein Wettbewerbsbeitrag gewesen vor ein paar Jahren und die haben das so gebaut,
dass, oh, jetzt hat der eine verloren,
dass in der Mitte tatsächlich so ein Gravitationsfeld noch mit reinkommt,
also der Ball noch so ein bisschen abgelenkt wird
Also ihr seht, man kann mit dem bisschen Turtle-Graphics-Zeug,
was ihr da so bekommen habt, relativ hübsche Sachen machen.
Und wie gesagt, die letzten Jahre haben wir da
immer nette Beiträge bekommen. Und wenn ihr nebenbei einfach Lust
habt, sowas zu machen, dann seid ihr willkommen.
Wie gesagt, die Wettbewerbsbedingungen werden im Kommen sie stehen.
Es gibt immer was zu gewinnen. Meistens ist es
Niederegger Marzipan. Also wir reden natürlich jetzt keinen Marzipan-Markt,
dann ist es wieder schwierig. Aber ja, gut,
das ist das eine. Dann haben wir das nächste Problem,
kein Problem.
Was ist das nächste Thema
Viele von euch haben den Online-Selbsttest inzwischen durchgeführt. Ja,
das heißt also, wenn wir hier ins Komsi schauen,
da, dann sieht man hier Selbsttest-Level 1,
360 Mal ist dieser Test ausgeführt worden,
was nicht heißt, dass 360 Personen ihn ausgeführt haben.
Aber das ist schon mal ganz prima, habe ich ja schon gesagt.
Dazu habe ich aber jetzt so ein paar Fragen.
Und dazu möchte ich mal hier Storesi wieder ins Spiel bringen.
Ich mache mal hier eine Abstimmung. Vorlesung 7 heute.
Und die Frage, die ich stelle, ist,
wie habt ihr bisher den Online-Selbsttest durchgeführt.
Also habt ihr den Online-Selbsttest zu Level 1
noch nicht durchgeführt und wollte es auch nicht tun.
Eine Möglichkeit. Zweite Möglichkeit, ihr habt den bisher
nicht durchgeführt, wollt es aber noch tun?
Das ist sozusagen für die, die es noch nicht gemacht haben.
Für die, die es gemacht haben, da interessiert uns,
ob ihr ihn durchgeführt habt, ob ihr ihn bestanden habt.
Da meldet sich jemand? Ja, wir haben die Frage genommen. Okay,
also er sagt, er konnte den Test nicht machen,
weil der Link rausgenommen war.
Der Link müsste jetzt wieder da sein.
Also inzwischen haben ihn auch wieder manche Leute
den Test durchgeführt
Ja, also C durchgeführt bestanden und dann auch was gelernt.
Also entweder was gelernt im Sinne von, oder waren auch
so ein paar Fragen drin, das wusste ich nicht.
Oder ich habe gelernt, dass ich eigentlich alles weiß.
Kann ja auch sein. Oder D durchgeführt, nicht bestanden,
aber etwas gelernt. Also das ist eher der Effekt,
oh, ich habe zumindest gelernt,
dass ich nicht genügend weiß, oder eh,
ihr habt den durchgeführt und es hat euch nichts gebracht.
Alles Möglichkeiten, die ihr jetzt auswählen könnt,
150 Stimmen habe ich schon zusammen, wunderbar.
Schauen wir mal, da kommen immer noch so
ein paar Stimmen rein.
Gut,
gut, gut
Ja, ich glaube, das reicht dann auch.
Vielmehr werden das meistens nicht so. 160, gut.
Ja, da kommt immer noch was rein. Gut,
wir halten das mal an und gucken einfach mal auf die Verteilung.
In diesem Fall gibt es keine richtige Antwort,
weil es durchaus Leute gibt,
die den bestanden haben und es gibt durchaus Leute,
die haben ihn nicht bestanden.
Also wir sehen ja nur ungefähr, wie viele Leute
ihn nicht bestanden haben. Ich glaube, es haben irgendwie
nur 44 Prozent der Teilnehmer den Test bestanden.
Was ziemlich erschreckend ist
Sollte nicht so sein. Also bisher nicht durchgeführt,
werde es aber noch tun. Immerhin ziemlich viele von euch
sagen das, sag ich mal, die absoluten Stimmen.
Also 55 sagen, machen Sie noch? Die meisten haben
ihn durchgeführt, bestanden und dabei was gelernt, prima.
Und dann gibt es noch welche, die haben ihn durchgeführt,
nicht bestanden, aber immerhin auch was gelernt.
Und dann gibt es zu zwölf, die sagen,
hat mir nichts gebracht.
Das ist auch sozusagen ganz normal in SE1. Wir wissen,
dass wir einen großen Range haben von Teilnehmern.
Und es gibt einfach Leute, die sind hier komplett unterfordert.
Es sind zum Glück nicht so viele. Das ist immer
ganz beruhigend. Gut, das ist einfach nur mal
diese Umfrage, die wollte ich zu Anfang mal machen
Wir natürlich immer auch wissen wollen, wie gut unsere Tests hier funktionieren für euch.
Gut, ab morgen wird es einen weiteren Test geben zu Level
2. Das heißt, wieder das Spiel,
wenn ihr mit dem Blatt 7 fertig seid, ihr es abgenommen habt,
setzt euch zu Hause in Ruhe hin und macht den Test.
Das ist dann für Level 2,
was so viel heißt wie nicht nur Level 2,
sondern auch natürlich Level 1 und Level 2.
Weil all das ja alles aufeinander aufbaut hier. Gut,
dann habe ich an dieser Stelle in der Vorlesung,
weil es jetzt so schön kalt wird und man fühlt sich so weihnachtlich,
weil es so ungemütlich ist,
habe ich immer ein bisschen
ein Gedicht vorgetragen
Das ist aber inzwischen aus verschiedenen Gründen langweilig. Erstens,
wer von euch kennt überhaupt noch diese Figur hier?
Wer weiß, wer da irgendwie angedeutet wird? Das ist Viko von Bülow,
der sich selbst gezeichnet hat oder auch bekannt als Loriot.
Aber ich denke, dass die meisten von euch Loriot gar nicht mehr kennen.
Ja, trage ich heute mal ein anderes Gedicht vor.
Wenn ihr das Gedicht sehen wollt,
guckt euch das Video vom letzten Jahr an.
Heute nehmen wir was anderes. Das ist ein interessantes,
ein interessanter Text, schon deshalb. Also auch schon,
dass das Loriot-Gedicht, was ich hier hatte,
war etwas morbide.
Dieses Schlaflied ist noch etwas morbider
Das ist so mobile, dass es tatsächlich
eine Zeit lang auf dem Index stand in Deutschland.
Also diese Scheibe von den Ärzten, wisst ihr wahrscheinlich,
Farin Urlaub ist Mitglied der Ärzte. Und das Lied,
was er hier geschrieben hat, ist tatsächlich auf der Platte
drauf gewesen, auf der ersten Platte der Ärzte.
Und diese Platte war 20 Jahre lang verboten in Deutschland.
Nein, nicht verboten auf dem Index, also nur für Erwachsene.
Und unter anderem wegen dieses Liedes.
Und wer den Text nicht kennt, wird jetzt...
Ja, also es ist gewöhnungsbedürftig. Also ich singe das
jetzt nicht. Du hast es ein Lied, aber ich...
Tut mir leid, meine Gesangskünste sind da nicht so
Toll. Schlaf mein Kindchen, schlafe ein,
die Nacht sie schaut zum Fenster rein. Der runde Mond,
er hat dich gerne und es leuchten dir die Sterne.
Schlaf mein kleines Träume süß, bald bist du im Paradies.
Denn gleich öffnet sich die Tür und ein Monster kommt zu dir.
Also man muss sich das so vorstellen, man trägt das
Lied zu seinem Kind vor, das Nacht einschlafen will.
Mit seinen elf Augen schaut es dich an und schleicht sich
an dein Bettchen ran. Du liegst still da,
bewegst dich nicht, das Monster zerkratzt dir dein Gesicht.
Seine Finger sind lang und dünn, werde ich nicht,
es hat keinen Sinn.
Und es kichert wie verrückt, als es deinen Hals
zudrückt.
Es ist nicht so schön, aber es wird noch blutiger.
Das wollen wir nachher analysieren. Wir machen nachher hier Gedichtanalyse.
So, gut. Aber jetzt erst mal zum Hardcore-Inhalt der Vorlesung.
In der ersten Hälfte werde ich was über Rekursion erzählen und
über die Begriffe Stack on Heep, was die bedeuten,
Laufzeit, Umgebung eines Java-Programms.
Nach der Pause werden wir uns dann angucken,
wie das mit Strings funktioniert in Java,
endlich mal ein bisschen grundlegend so ein paar Dinge erklären,
die ihr bisher immer so implizit zu sehen bekommen habt in
den Übungen,
aber jetzt werden wir
es mal klarstellen
Und zum Schluss will ich noch was zu regulären Ausdrücken sagen,
ein sehr mächtiger Mechanismus, den Java anbietet.
Also jetzt erstmal Rekursion. Bevor ich ein erstes Beispiel zeige,
habe ich hier ein noch allererstes Beispiel.
Ihr wisst, was das hier ist. Das nennt sich
ein Windows-Explorer-Fenster. Für diejenigen, die auf dem Macintosh arbeiten,
so sehen hier die Ordner aus, so angezeigt.
Und was man hier sehen kann, ist,
dass sich innerhalb eines Ordners, wie zum Beispiel hier vor diesen sieben Rekursion-Strings,
kann ich weitere Ordner haben.
Das heißt, innerhalb des Datentyps oder des Dateitypsordner können Ordner
liegen, also Ordner in Ordnern
Das ist toll. Das nennt man auch Rekursion.
Das ist eine rekursive Struktur innerhalb eines Dings von einem
bestimmten Typ können gleiche Dingselemente des gleichen Typs enthalten sein.
Es ist selten so, dass innerhalb eines Ordners derselbe Ordner enthalten ist.
Dass es also nicht eine Beziehung, Rekursion zwischen einem Objekt
und sich selbst, sondern zwischen Objekten desselben Typs.
Also Objekte, Ordner können in Ordnern geschachtelt sein.
Ein anderes Beispiel für Rekursion, das kennt ihr aus der Mathematik.
Ich bin kein großer Freund von mathematischen Beispielen.
Ich finde es immer angenehm, wenn man irgendwo,
sagen wir, realere Beispiele hernimmt.
Für viele von euch ist Mathematik real,
aber eben halt nicht für alle
Aber das ist so ein, sagen wir, gut nachvollziehbares Beispiel,
dass wir das hier einfach mal jetzt anwenden können.
Ihr kennt sicherlich die Definition der Fakultät einer Zahl.
Die Fakultät wird so geschrieben mit N-Ausrufungszeichen. Die Fakultät N-Fakultät
ist das Produkt aller natürlichen Zahlen von 1 bis N.
Vier Fakultät beispielsweise ist einmal, zweimal, dreimal, 4, 24.
So, das ist als mathematische Definition ganz einfach,
die Definition, die sieht so aus.
Die Fakultät der Zahlen 0 ist eins und die fakultiert an
der natürlichen Zahl N wird ein größer als 0,
ist N-mal n-2-2-2-1 Fakultät
Das heißt, die Definition ist selbstbezüglich oder auch rekursiv.
Das heißt, in der Definition hier an dieser Stelle
wird gesagt, ich definiere die Rekursion mit sich selbst.
Das ist erst mal komisch, aber es funktioniert ganz gut.
Das kennt ihr hoffentlich aus der Mathematik. Weil ja da tatsächlich dann
eine etwas kleinere Zahl genommen wird. Und dann irgendwann,
wenn diese Zahl immer kleiner wird, weiß man,
ah ja, jetzt kann ich auf die Zahl die Frage beantworten,
weil dann ist die Zahl Antwort ganz einfach.
Die faktilierte Zahl 0 ist ja eins.
So, das lässt sich direkt in Java so hinschreiben.
Man kann Rekursion in Java notieren
Ja, so sieht das hier aus. Also eine Methode,
Public Int, Fakultät bekommt als Parameter ein N, eine natürliche Zahl.
Ja, Betonung liegt auf natürliche Zahl. Also hier keine
negativen Zahlen. Dann wird hier ein Resultat definiert, eine
lokale Variable. Dann wird gefragt, ist N gleich Null?
Dann ist das Resultat gleich eins. Im sonstigen Fall sagen
wir nur, das Resultat ist natürlich endmal.
Hier steht ein Ausdruck, da wird etwas multipliziert,
nämlich die Belegung von N mit,
dem Ergebnis des Fakultätsaufrufs
Okay, hier ist also ein weiterer Aufruf der Fakultät.
Und das ist ein rekursiver Aufruf. Im Rumpf der Methode Fakultät
steht ein Aufruf der Methode Fakultät. Boah, das ist
erstmal komisch. Dass das funktioniert, kann ich euch zeigen.
Also es gibt ja hier natürlich das entsprechende Java-Projekt dazu.
Hier, so ganz einfach, das ist die klasse Rekursion.
Und da drin gibt es diese Methode,
genauso wie auf der Folie hier, Fakultät N,
eine natürliche Zahl als Parameter.
Und genau wie es auf der Folie steht
Und dann kann ich hier jetzt Fakultät berechnen und zum Beispiel
die Fakultät von 4 ist 24. Toll.
Oder die Fakultät von einer 3 ist vermutlich,
aber es ist defakuliert von 3. Einmal zweimal 3,
6. Deswegen ist die Fakultät von 4 auch 24,
weil 6 mal 4 ist ja 24.
So, das Ding macht also das, was es machen soll.
Rechnet die Fakultät richtig. Das ist erst mal nur,
dass ihr seht, dass es geht.
Man kann also im Rumpf einer Methode sich selbst aufrufen.
Wunderbar. Was wir uns jetzt mal ein bisschen genauer ansehen
und was ja komisch ist,
dass diese Methode sich da irgendwie selbst aufruft
Das ist erst mal schwer nachzuvollziehen, wir können uns das aber veranschaulichen.
Wir stellen uns vor, wir rufen das Ding jetzt
mit N gleich 3 auf, haben wir gerade gemacht.
Dann haben wir hier sozusagen eine Aktivierung dieser Methode.
Diese Methode wird aufgerufen mit N gleich 3. Also wie wir es gerade gemacht haben,
genau diesen Aufruf, den ich gerade gemacht habe,
dann kann man sich den Kontrollfluss jetzt so vorstellen,
der läuft hier so rein und wir stellen natürlich fest,
N ist 3, also es ist nicht gleich Null, also gehen wir den Els-Zweig.
Und an dem Els-Zweig müssen wir jetzt erst mal diesen Ausdruck auswerten.
Da kommen wir also dahin, wo dieser rekursive Aufruf steht.
Was müssen wir machen? Na gut,
wir müssen halt wirklich dafür sorgen, dass das Ding
jetzt erneut aufgerufen wird
Jetzt kommt es zu einer erneuten Aktivierung dieser Methode.
Also wird einfach diese Methode erneut irgendwie angelegt im Speicher.
Wir gucken uns das noch ein bisschen genauer an.
So, jetzt ist die nächste Aktivierung. Und da wird
aber jetzt Fakultät N minus 1 aufgehoben. Das heißt,
als aktueller Parameter wird übergebene 2. Also hier,
werfach aktiviert und hier ist N jetzt 2. Okay,
N gleich 2 heißt N nicht gleich 0,
also rutschen wir hier schön rein.
Ups, schon wieder rekursiver Aufruf.
Na gut, N minus 1, also nächste Aktivierung.
Wupp,
N gleich
Wir laufen wieder runter, Ende ist immer noch nicht null,
wieder zum rekursiven Aufruf. Und wir springen hier rein
und haben jetzt das vierte Mal diese Methode aufgerufen.
Die anderen Methoden sind immer noch irgendwie aktiv.
Die warten alle jetzt noch an der Stelle,
wo dieser Ausdruck ausgewertet wird. Weil hier,
der Ausdruck ist ja noch nicht zu Ende ausgewertet,
weil das Ergebnis ist ja noch nicht da.
Das gilt jetzt für alle. Die warten jetzt darauf,
dass hier ein Ergebnis kommt. So,
jetzt haben wir das erste Mal hier den Fall,
dass N gleich null ist, der Parameter ist null.
Also der formale Parameter ist null. Also,
Kontrollfluss direkt hier runter, iFN gleich null Result gleich 1.
Das heißt,
diese Aktivierung der Methode liefert jetzt als Ergebnis
Da kommt eine Eins zurück. Und die kommt genau in
diesen Ausdruck rein. Da steht jetzt also N,
N, wissen wir, von da oben ist eins.
Und von hier unten kommt auch eine Eins, also N,
M, N, also 1 mal 1 ist eins.
Führt dazu, dass hier eine Eins zurückgeliefert wird.
Ja. Da oben steht N gleich zwei,
N mal Fakultät, was liefert das jetzt zurück eine Eins?
Also zweimal eins. Das heißt, hier wird eine 2 zurückgeliefert.
Ja. Und dann ist hier einfach N gleich 3,
das Ergebnis des Aufrufs ist 2, also kommt hier 6 raus.
Und genau das Ergebnis, was wir gesehen haben.
So müsst ihr euch das vorstellen,
dass tatsächlich die Methode
mehrfach aktiviert wird
Wie das funktioniert, das ist nicht so ganz einfach.
Man muss da schon so ein bisschen, sagen wir,
hinter die Kulissen gucken, das machen wir jetzt.
Ich versuche euch jetzt so ein mentales Modell zu vermitteln,
damit ihr versteht, wie die Java Virtual Machine,
euer Programm ausfüllt, wenn ihr so eine rekursive Methode schreibt.
Was zur Laufzeit benutzt wird, ist ein sogenannter Aufruf-Stack,
ein Call-Stack, ein Aufrufstapel. Was ein Stapel ist,
das ist relativ einfach, da habt ihr ein Verständnis dafür, ein intuitives,
wenn ich was draufpacke, dann nehme ich auch immer von oben runter.
Also hier ist das Beispiel mit diesen Äpfeln hier in diesen Stangen,
wenn ich da jetzt irgendwie einen Apfel dazu packe und ich will den Ampel-Apfel rausnehmen,
da werde ich vermutlich den Apfel immer von oben runternehmen,
weil wenn ich ihn da durch dieses Stäbe durchquetsche,
wird er nicht mehr sehr lustig aussehen.
Gut, also das, was man zuletzt draufpackt,
wird als erstes wieder runtergenommen. Das ist ein Stapel,
auch in der Mensa, die Tabletts stapeln. So,
genau so was ähnliches wird von der Virtual Machine angelegt zur
Laufzeit für jeden, für jede Aktivierung einer Methode.
Und zwar braucht man dann natürlich die Rücksprungadresse zu der
vorigen Aktivierung und man braucht die lokalen Variablen.
Wir wissen ja, jede Aktivierung hat eigene lokale Variablen
So, und wenn dann die Methode zu Ende ist,
wird diese Aktivierung wieder abgeräumt.
Also es wird irgendwie so eine Datenstruktur auf diesen Stapel draufgepackt.
Nächster Aufruf, nächster Aufruf. Und dann wird das Ding wieder abgebaut.
Das können wir uns tatsächlich auch mal angucken. Hier sehen wir,
dass, können wir uns das so vorstellen, hier ist irgendwo dieser Aufrufstapel,
dieser Aufrufstapel existiert für jeden Prozess in einem Genre-System.
Wenn wir davon ausgehen, wir haben nur einen Prozess,
habe ich ja mal gesagt, für SE1,
gibt es also einen solchen Stapel, könnt ihr davon ausgehen.
Und da findet alles drauf statt.
Alle Aufrufe werden darauf abgelegt. Alle Informationen über Aufrufe
Ich hätte es also über den Aufruf gegeben,
dann war da hier eine Methode, Stackframe der Klientenmode,
die diese Methode, diesen Ausdruck enthält.
Also hier steht irgendwie 23 plus Fakultät von 4. Gut,
dann ist irgendwie die 23 irgendwie lokal gehalten.
So, und jetzt wird hier die Fakultät aufgerufen.
Das heißt, es wird ein Stackframe auf diesen Schapel gelegt.
Und da wird jetzt hier diese 4 übergeben,
wie wir es gerade auch schon gesehen haben.
Und weil wir eben gesehen haben, rekursiv wird die Antwort
nicht sofort zurückgegeben, sondern es wird das rekursiv weiter aufgerufen.
Also kommt das hier mit 3, mit 2,
mit 1. Und am Ende kommt irgendwann dieser Stackframe,
wo das Ding aufgerufen wird mit dem aktuellen Parameter
Und da wissen wir ja, da kann dann die Antwort
gleich geliefert werden. Also hier wird eine Eins geliefert,
als Ergebnis. So, dann wird das Deckframe wieder abgebaut.
Und das fließt jetzt hier in diese Berechnung rein.
Also ein Mal eins ist dann das Ergebnis, ist dann
hier eins. Gut, dann wird das abgebaut. Und
dieses Ergebnis fließt in diese Multiplikation, in diese Aktivierung rein.
Also einmal zwei, ist dann zwei.
Und dann dieses Ergebnis fließt wieder hier rein.
Da hat man hier die Sechs und dann hier die 24.
Und das wiederum wird dann irgendwo hier in diese Brechnung reingezogen.
Jetzt kann 23 plus 24
berechnet werden
Okay, das können wir uns auch nochmal in diesem Beispiel angucken.
Das ist nämlich auch wieder ein wunderbares Beispiel, wo man
die Rekursion, wo man den Debugger gut benutzen kann.
Hier mache ich jetzt mal einen Haltepunkt und lasse mir
jetzt nochmal die Rekursion berechnen, die Fakultät berechnen.
So, dann habe ich hier diesen Haltepunkt. Ja,
hier die Ausführung und dann sehe ich hier jetzt tatsächlich,
und das ist irgendwie etwas,
was euch bisher wahrscheinlich nicht so klar war,
aber das hier links, Das sind tatsächlich die ganzen Aktivierungen derselben Methode.
Also hier ist jetzt Rekursion.fakultät, also diese Methode, Fakultät einmal aufgerufen.
Das ist ein Stackframe. Und in diesem Stackframe ist N
gleich drei. Also N ist jetzt gerade drei.
Ich mache einen Schritt über, weil N ist ja nicht
gleich null. Also wird er jetzt genau hier hinspringen.
Genau wie auf der Folie eben angedeutet,
er steht jetzt hier und ruft jetzt die Fakultät rekursiv auf.
Okay, also mache ich jetzt einfach mal einen Schritt weiter.
Dann wird er diesen Rekursiven Aufruf machen und steht jetzt
bei der nächsten Aktivierung an dieser Stelle.
Jetzt steht er sozusagen, und wir sehen tatsächlich hier
formale Partner
Der lokale, der lokale Variable in Gleich 2. So,
und jetzt mache ich das wieder.
Ich mache einen Schritt über, wieder der rekursive Aufruf.
Und nochmal, ja, und jetzt haben wir langsam
den Punkt, jetzt ist N gleich Null.
Wir haben das Ding, also jetzt tatsächlich viermal aufgerufen.
Und wir haben tatsächlich jetzt auf diesem Stapel genau diese Aufrufframes,
also hier den für N gleich 3.
Da stehen wir mitten im Ausdruck, mitten in der Ausdruckauswertung,
hier auch mittendrin, hier auch mittendrin.
Und hier stehen wir jetzt an dieser Stelle, wo wir fragen,
ist denn N gleich noch ein N ist gleich noch?
Sehen wir hier. Der Parameter ist null.
Also kommt hier Result gleich
Result ist jetzt hier tatsächlich sichtbar und wird jetzt zurückgeliefert.
Und wenn wir jetzt einen Schritt machen, dann ist ein Stackframe hier abgebaut worden.
Und hier ist der nächste Aufruf. Die nächste Berechnung,
die jetzt hier mittendrin ist und so weiter.
Und dann sieht man hier, wie das sozusagen nach und nach
abgearbeitet wird. Und am Ende kommt das Ergebnis zurück.
Das ist die Art und Weise, wie in einem imperativen System,
in einem objektnotierten System, Methodenaufrufe auch rekursiv sein können.
Was ich letzte Woche erzählt habe, ist,
dass es sowas gibt wie die Lebensdauer von Variablen und
von Objekten
Und insbesondere für Variablen. Können wir uns das jetzt nochmal angucken.
Also die Lebensdauer ist einfach so eine dynamische Eigenschaft zur Laufzeit.
Wie lange existiert denn so eine Variable? Da haben wir
dieses Beispiel hier. Hier habe ich die lokale Variante Result.
Die habe ich hier auch und die habe ich hier auch,
die habe ich hier auch.
Die leben aber unterschiedlich lange. Ich habe ja gesehen,
die erste Methode wird aufgerufen. Und diese lokale Variable,
die lebt mindestens vier Methoden Aufrufe lang.
In diesem Fall, bei dieser konkreten Ausführung zur Laufzeit.
Weil wenn das ausgeführt wird, dann wird das,
dann wird das, dann wird das ausgeführt.
Solange lebt diese lokale Variable.
Für die ist Speicher reserviert in diesem Stackframe
Tatsächlich der Speicher in diesem Stackframe. Ja,
und diese hier, die lebt am wenigsten lange,
die wird nämlich nur genau einmal kurz aufgerufen,
da wird irgendwie gleich das Resultat 1 zurückgegeben und dann wird zurückgesprungen.
Das ist einfach auch nochmal,
dass man sich klarmachen kann, diese Frage,
wie lange lebt eine Variable, ist eine Eigenschaft,
die man nur zur Laufzeit entscheiden kann und dieselbe lokale Variable in unterschiedlichen Aktivierungen kann eben halt unterschiedlich lange leben.
Okay, soviel erstmal als erstes Beispiel zur Rekursion.
Ihr werdet in Übungen so einige rekursive Beispiele sehen,
euch die angucken und ihr werdet dann auch versuchen,
selber welche zu schreiben
Nur einfach als zentrale Aussage. Das ist etwas, was
in der Informatik sozusagen formal nachgewiesen ist. Alles das,
was rekursiv beschrieben werden kann, kann auch iterativ beschrieben werden.
Also iterativ im Sinne von mit einer normalen Imperativen Schleife.
Ich kann jedes iterative Programm mit Rekursion schreiben und umgekehrt.
Also solche Schleifen wie diese hier. Ihr seht hier,
ist keine Rekursion im Spiel. Die Methode Fakultät wird nicht nochmal aufgerufen,
sondern hier wird einfach in einer Vorschleife diese Fakultät berechnet.
Und das ist dann eben halt nicht rekursiv,
sondern einfach iterativ schön in mehreren Schleifen durchlaufen
Okay. Das ist wichtig, dass ihr das euch klar
macht und ich werde dann gleich ein bisschen mehr dazu sagen,
wann man Rekursion, wann man Iteration anwenden sollte.
Ich werde jetzt noch mal so ein bisschen euch,
nachdem ich einmal jetzt das Grundprinzip gezeigt habe, sagen,
was Rekursion allgemein bedeutet, Rekursion in der Programmierung tritt auf,
wenn eine Methode oder eine Prozedur M während der Ausführung ihres Rumpfes erneut aufgerufen wird.
Das ist klar, wenn man sich nur eine Methode selbst aufruft,
dann muss es irgendwie eine Bedingung geben,
wo das irgendwann mal endet,
weil sonst ruft die sich ja immer wieder auf,
ruft sich rekursiv immer wieder auf,
das kann dann
endlos so weitergehen
Also das muss irgendwann mal terminieren, es soll irgendwann mal aufhören.
Deswegen braucht man eine Abbruchbedingung bei jeder Rekursion. Diese Abbruchbedingungen
haben wir eben gesehen, bei der Fakultäter war das einfach,
wenn die Zahl klein genug ist, wenn es die Eins ist,
wenn es die Null ist, dann liefern wir die Eins zurück.
Das war die Abbruchbedingung, braucht man immer.
Und einfach nur formal als Begrifflichkeit, eine Rekursion ist direkt,
wenn direkt im Rumpf der Methode M steht,
es wird M aufgerufen, haben wir gerade gesehen.
Gibt aber auch durchaus die Möglichkeit, dass das indirekt passiert.
Also M1 ruft M2 auf und irgendwo im Rumpf von M2
wird wieder immer eins aufgerufen. Solche Strukturen sind oft sehr kompliziert,
sollte man ein bisschen vorsichtig mit sein,
aber es ist eben halt auch möglich
Und dann wird ja auch während der Ausführung von M1 wie
M2 aufgerufen und dann bleibt diese Aktivierung ja noch da.
M2 ruft jetzt wieder M1 auf,
also während der Aktivierung von M1 wird M1 wieder aufgerufen.
Gut, warum macht man sowas mit Rekursion? Naja,
die Grundidee ist einfach, dass man irgendein Problem lösen soll,
wie zum Beispiel die Fakultät berechnen und dann ein Teil des Problems
vielleicht schon lösen kann oder eben halt das Problem verkleinert und dann rekursiv wieder aufberechnen lässt.
So sieht die Grundstruktur aus,
wenn man sich das so als als als Methodenschablone vorstellt,
also irgendeine Methode mit einem Ergebnistyp,
die heißt Löser ein Problem und bekommt irgendwelche formalen Parameter
Wenn das Problem einfach lösbar ist, dann soll auch ein
einfaches Ergebnis geliefert werden. Das ist die Verallgemeinung dessen,
was ihr gerade gesehen habt. Ansonsten, okay,
ich stelle fest, das Problem ist noch nicht so ganz klein.
Also muss ich das Problem zerlegen in irgendeiner Weise und kann dann hier,
und das ist eben halt für den allgemeinen Fall durchaus denkbar,
dass man mehrfach das Ding rekursiv aufruft.
Also in diesem Fall hier wird vielleicht mehrfach Löseproblem rekursiv aufgerufen,
dann kommen da irgendwelche Ergebnisse zurück, Ergebnisse eins bis Ergebnis N.
Die werden irgendwie wieder zusammengeworfen und dann wird das Ergebnis geliefert.
So sieht die Grundstruktur aus
Gut, erst mal, dass ihr seht, das ist Rekursion.
Ihr werdet feststellen, dass ihr Rekursionen mögt oder dass ihr es nicht mögt,
je nachdem. Wenn ihr Rekursionen sehr gerne mögt,
dann solltet ihr auch wissen, dass es Fälle gibt,
wo man die Rekursion nicht unbedingt anwenden sollte.
Es gibt ja ein bekanntes Gegenbeispiel, das ist das mit den Fibonacci-Zahlen.
Die Fibonacci-Zahlen sind relativ einfach definiert. Ihr habt sicherlich mal von denen gehört,
vielleicht habt ihr auch mal hier dieses Buch gelesen von,
ich glaube, Dan Brown oder so.
Wer ist der? Da irgendwie der Davinci-Code oder so
Das sind die 500 Zahlen hier, die sind irgendwie so,
das ist so eine Zahlenfolge, die relativ schnell wächst.
1. 400 Schiedsal ist null, 2. final Schiedsal ist 1.
Ganz normale, rekursive Definition. In diesem Fall,
hier wird sie jetzt rekursiv, nämlich die Ente Fibunatschiedsal,
da gibt es sich aus der Summe der N-minus Ersten und der N-minus Zweiten.
Also die nächste ergibt sich aus den vorderen beiden, aus den vorherigen beiden.
Dritte 490 Zahl ist 1 und so weiter. Das ist eine rekursive Definition aus der Mathematik,
die können wir wieder direkt hinschreiben in einem Java-Programm,
ihr könnt schreiben, ja, Fibunatschi von N ist,
ich habe das hier mal mit einem Switch gemacht,
Also wenn NN1 ist, dann Return 0,
wenn es 2 ist, dann Return 1 und im Default-Fall,
wenn es also weder 1 noch 2 ist,
dann muss ich halt 400 4 von M-1 und 400 4 von M-1 2 aufrufen und das dann addieren.
Wunderbar. Nur ein kleiner Hinweis, hier steht ein Return,
deswegen muss hier kein Break stehen, bei dem Case, aufpassen.
Normalerweise steht da ja ein Break, nochmal das Nubel,
das nur der eine Fall ausgeführt wird.
Hier ist es so, dass das Return gleich auch
nicht nur das Switch verlässt, sondern auch die Methode.
Ja, das ist ein bisschen problematisch, das Ding hier.
Hat jemand eine Idee, warum das schwierig sein könnte,
warum da, wie jetzt irgendwas komisches.
Ja, genau. Bitte? Ja, kann auch mega gut sein.
Ah, okay, er sagt, Interject können auch negativ sein.
Ja, na, wir gehen jetzt wieder davon aus,
dass es hier um positive Zahlen, natürliche Zahlen geht, ja.
Wenn wir jetzt ein Boot hängen haben,
sehr viele Methoden, auch Gruppe generiert werden.
Ah, sehr guter Hinweis. Er sagt,
wenn wir ein hohes N haben, ein großes N,
dann werden hier sehr viele rekursive Aufrufe generiert
Das ist tatsächlich genau das, was wir hier jetzt
uns mal angucken können. Nehmen wir mal an,
wir rufen Fibonacci so definiert mit dem aktuellen Parameter 4 auf.
Also hier steht jetzt tatsächlich mal eine 4. Also
der formale Parameter ist belegt mit dem aktuellen Parameter.
Also wird hier ein Switch über die 4 gemacht.
Dann ist klar, es wird jetzt hier wieder,
es ist weder Case 0 noch Case 1.
Also wird hier erstmal Filogci von 3 aufgerufen. Fibonacci von 3
wird aufgerufen, na gut, dann wird Fibonacci von 2 aufgerufen.
Dann Fibonacci von 1 und Fibonacci von 0. Weil
bei 400 steht von 2 ist es dann ja so,
es ist Fibonacci 1 und Fibonacci.
Und dann nur wenn in diesen beiden Fällen wird es
nicht nochmal rekursiv aufgerufen
Ist die Antwort ja sofort da. Und hier,
Fibunatschi von 1 und für Fibunatschi von 2 wird das auch gemacht.
So, also hier und hier. Jetzt sehen wir für
jede rekursive Stufe, wird hier so grob gesagt, das verdoppelt.
Das stimmt nicht ganz, das ist nicht wirklich eine Verdopplung,
aber es ist tatsächlich so, Fibunatschi von 2 sind zwei rekursive Aufrufe.
Bei drei sind es hier 490 von 2, zwei rekursive Aufrufe.
Bei drei sind es hier, bup bup bup,
vier rekursive Aufrufe. Bei vier sind es dann schon 8
und bei Finatschi von 5, 13 und so weiter.
Das wächst also relativ schnell. Das werdet ihr in den Übungen
auch zu sehen bekommen. Man kann damit selbst so einen schnellen Rechner,
wie ihr sie heute habt, relativ schnell lahmkrimi,
ihr müsst da nur irgendwie eine Zahl angeben, die irgendwie,
was weiß ich, 30, Feminutchi, 30. Feminutchi-Zahl,
35., dann sind das schon mehrere Milliardenaufrufe, die da generiert werden.
Das ist also einfach viel Zeug. Und das ist auch ziemlich blöd,
weil hier irgendwie immer dieselben Sachen nochmal berechnet werden.
Also Feminutchi von 2 ist ja hier,
wird hier berechnet und hier aber auch nochmal.
Also immer wieder wird das Gleiche berechnet.
Und das zig Mal, ziemlich sinnlos. Das heißt,
wenn man das naiv so programmiert,
dann kann man sich relativ schnell ins Bein schießen
Ja, also aufpassen bei Rekursionen insbesondere,
wenn so eine Rekursion hier mehrere rekursive Aufrufe bedeutet.
Gut, aber ich will auch nicht zu stark vor Rekursion waren.
Das wäre ja Quatsch, wir wollen uns euch ja beibringen.
Und Rekursion ist einfach sehr, sehr wichtig.
Denn es gibt sehr viele Strukturen in der Informatik,
die rekursiv definiert sind. Wir haben eben schon gesehen,
so ein Ordner in so einem Dateisystem ist rekursiv.
In so einem Ordner, also die können wieder Ordner liegen.
Wenn ihr in einem Ordner sucht, ihr könnt ja auch
in dem Windows Explorer oben rechts in der Ecke sagen,
so such mal nach irgendwas,
dann wird üblicherweise auch der Ordner rekursiv durchsucht.
Der ganze Baum wird rekursiv durchlaufen
Also solche Baumstrukturen findet ihr ganz viel in der Informatik.
Syntaxbäume, Entscheidungsbäume, für Zeichnisbäume und so weiter.
Dann gibt es viele gute Sortierverfahren, die sind rekursiv definiert.
Die werden wir sogar noch in SE1 hier kennenlernen,
also zumindest eins davon, nämlich Quicksart.
Da ist es einfach die Definition von Quicksart ist rekursiv,
es ist rekursiv wirklich schnell. Das ist super. Auch auf Graphen,
wenn ihr sowas habt wie ein Navigationssystem, das irgendwie versucht,
die kürzeste Route zu berechnen von A nach B.
Da sind das irgendwie Knoten und dann hat man auch einen
Graphen und dann sind die Algorithmen, die darauf arbeiten,
auch oft rekursiv.
Das heißt, ihr werdet noch ganz viele Anwendungsbeispiele von Rekursionen
Und auch in SE1 werden wir noch ein paar sehen.
Nur von einem sehr ausgewiesenen Praktikaner Aussage hier, Rekursionsstärken und Schwächen.
Steve McConnell, also wer immer sich wirklich für Softwareentwicklung interessiert,
der sollte dieses Buch auch mal in die Hand nehmen.
Er steckt extrem viel praktische Erfahrung da drin. Code Complete
heißt das Buch. Ein sehr, sehr fähiger Kopf.
Ich habe den mal live auf einer Konferenz erlegt,
wirklich sehr eindrucksvoll. Der hat einfach gesagt, naja,
mit Rekursion kann man eine relativ kleine Menge von Problemen
sehr einfach und elegant lösen.
Dann gibt es eine etwas größere Menge von Problemen.
Da kann man zwar einfach und elegant
lösen,
aber schwer zu verstehen
Die Lösung wird einfach schwer lesbar. Und wir wissen ja,
wir wollen ja die Software auch lesen können,
weil sie ja möglicherweise auch noch in mehreren Jahren von Leuten irgendwie erweitert werden sollen.
Und für die meisten Probleme, wenn man versucht,
alles mit Rekursion zu machen, dann führt das zu sehr komplizierten Lösungen.
Also man versucht, alles mit Rekursion zu machen,
ist das keine gute Idee. Es gibt also Situationen,
in denen sich Rekursion als Lösung anbietet oder als gute Lösung.
Es gibt aber deutlich mehr Situationen, in denen man Rekursion eher nicht verwenden sollte. Gut,
jetzt habe ich relativ viel zur Rekursion erzählt und dabei habe ich schon angedeutet,
es gibt diesen Aufrufstapel,
also für jede Aktivierung einer Methode wird auf dem Stapel so ein Frame angelegt,
der die Informationen hält für so einen Aufruf.
Das ist hier mal insgesamt angedeutet, alles das,
was man so an Speicherstruktur braucht, wenn ein Java-Programm läuft.
Das heißt, wenn eine Virtual Machine euer Java-Programm ausführt,
dann sieht ungefähr, dass im Speicher so aus vereinfacht.
Es gibt irgendwo Speicher für den Aufrufstapel. Es gibt irgendwo
Speicher für den sogenannten Hieb, für die Halde oder den Haufen,
wie auch immer man das nennen will.
Den gucken wir uns gleich genauer an.
Und es gibt Speicherplatz für den eigentlichen Bitecode
Was ihr ja schreibt, ist Java-Code. Das wird übersetzt
in Bitecode und dieser Bitecode muss ja irgendwo im Speicher liegen.
Und mit der Computer, die Virtual Machine,
die einen Befehl nach dem anderen abarbeiten kann.
Das ist hier auch angedeutet und durchgängig auf den Folien hier immer grau,
wenn es ein Programm ist, blau,
wenn es ein Aufrufstack ist und gelb, wenn es ein Hieb ist.
Dieser Hieb, der ist dafür zuständig,
ich habe es hier eben schon ein bisschen übersprungen,
da werden dynamisch erzeugte Objekte abgelegt. Das heißt,
wenn ihr mit New in Java ein Objekt erzeugt,
dann der Speicherplatz für dieses Objekt wird wirklich hier aus dem Hieb so rausgeschnitten.
Da wird hier so ein kleines Stück rausgeschnitten und das wird benutzt,
reserviert für das Objekt. Die Speicherverwaltung von diesen kleinen Speicherfuzeln,
die da so rausgeschnitten werden, die kann man entweder selber machen,
in C-Plus zum Beispiel ist man meistens gezwungen,
das selber zu tun oder ein C, auf jeden Fall.
In Java passiert das automatisch. Ihr müsst euch darum nicht
kümmern. Das ist ziemlich cool. Einer der riesen Fortschritte,
den Java gemacht hat, dass da eine automatische Speicherverwaltung dahinter
steckt für diese dynamisch angeforderten Speicherbereiche. Also dieser dynamische Speicher,
der ist dafür da, dass zu beliebigen Zeitpunkten,
ihr könnt ja in beliebigen Punkten euer Programm ein New hinschreiben,
das ein Stück rausgeschnitten wird und dann wird nochmal da
ein Stück rausgeschnitten und dann muss das wieder freigegeben werden.
Also das kann in beliebiger Reihenfolge sein
Deutlich anders als beim Stack,
wo ja genau das immer streng passiert, dass der letzte Stackframe,
der aktiv ist, wenn der sich beendet,
wird der auch wieder abgebaut und es wird wieder der Alte aktiviert.
Und wenn ihr so eine Speicheranforderung von dem Hieb braucht,
dann wird das eben halt auch dynamische Speicheranforderungen genannt.
Da wird Speicher reserviert. Also wenn ihr eine Dirty Machine startet,
dann gibt es so bestimmte Parameter, mit denen man festlegen kann,
wie groß diese Speicherbereiche sein sollen.
Da gibt es Default-Werte, welche Voreinstellungen.
Aber das Ding kann volllaufen. Also der Speicher kann einfach
irgendwann belegt sein. Ihr habt so viele dynamische Objekte erzeugt,
es wurde nichts mehr freigegeben.
Dann gibt es
einen Out-of-Memory-Ära
Der ist relativ schwer, künstlich zu erzeugen. Also wir können das hier mal machen,
das machen wir vielleicht mal in einer späteren Vorlesung mit den Mitteln,
von denen wir bisher kennengelernt haben, ist es relativ schwierig.
Das heißt, wenn ihr selbst in einer Schleife nichts weitermacht,
als Objekte zu erzeugen,
schafft die Virtual Machines sozusagen parallel mit dem Garbage Cracker zu sagen,
naja, der hat zwar Objekte erzeugt,
aber die braucht er ja alle nicht mehr,
dann kann ich die ja gleich wieder abräumen.
Aber das kann einfach passieren, dass der Speicher vollläuft und
dann gibt es ein Out of Memory Acception.
So, das einfach nochmal so zum Vergleich,
also hier der Hieb und der Aufrufstack bei dem Hieb ist es so,
in beliebiger Reihenfolge werden da verschiedene große Stücke rausgeschnitten.
Also wenn ein Objekt nur zwei weil Exemplarvariablen hat,
dann braucht man halt wenig Speicher. Es gibt aber auch Objekte,
die haben 100 Exemplarvariablen, vielleicht nicht so geschickt modelliert,
aber dann braucht man eben halt mehr Platz für so
ein Objekt auf dem Hieb.
Das heißt, da sind wirklich unterschiedlich große Stücke, die da rausgeschnitten werden.
Okay, und bei dem Aufrufsdeck ist das aber sozusagen automatische Speicheranforderungen.
Das ist deutlich kalkulierbarer. Aber auch da kann es zu
einem Overflow kommen. Also das heißt, der Speicherbereich,
der für den Stack reserviert ist, kann ausgehen.
Das lässt sich relativ leicht erzeugen. Ich kann das
hier mal zeigen. In diesem Beispiel mit Blue Jay,
mit der rekursiven Definition der Rekursion
Wenn ich jetzt hier dieses Ding aufrufe, ich habe gesehen,
in der Definition, ich zeige es nochmal, eine natürliche Zahl soll übergeben werden.
Wenn ich jetzt eine negative Zahl übergebe,
zum Beispiel minus eins, dann wird der hier sagen,
naja, Ende ist minus eins, ist nicht gleich null,
also ruft er sich mit Fakultät N minus eins auf.
Das heißt, der ruft sich zuerst mit minus eins auf,
dann mit minus zwei, dann mit minus drei und so weiter.
Relativ viel. Und das mache ich jetzt mal hier,
ich gebe mal hier minus eins ein. Und dann wird es dazu kommen,
dass dann der Stapelspeicher, der Deck vollläuft,
weil wir zu viele rekursive Aufrufe von der Fakultät dann drauf haben.
Also hier zack, wupp
Ach nee, ich habe natürlich wieder den Haltepunkt hier hingesetzt.
Den muss ich mal wegnehmen.
Dann sage ich jetzt hier fortsetzen und jetzt kracht es.
Also das Ding wurde jetzt hier sehr häufig rekursiv aufgerufen.
Das kann man hier sehen. Es sind wirklich viele rekursive
Aufrufe. So, da so ein paar rekursive Aufrufe.
Um mal zu sehen, wie viele das sind,
könnte man jetzt mal so einen kleinen Trick machen.
Ich definiere mir hier in der Klasse einfach mal ein Private
Int Zähler. Und dann sage ich einfach hier,
wenn hier ein rekursiver Aufruf ist, dann erhöhe ich diesen Zähler.
Plus plus Erzähler. Das müsste eigentlich übersetzbar sein. Okay
. Und wenn ich jetzt hier das Ding nochmal beende,
dann kann ich jetzt hier ein falsches Projekt.
Entschuldigung, hier mir ein neues Exemplar erzeugen. Und jetzt
sage ich hier nochmal, mach doch nochmal diese Fakultätsberechnung mit minus eins.
Und dann wird er wieder auf diesen Stack-Overflow kommen.
Und jetzt kann ich mir hier von diesem Exemplar,
was ich gerade da angelegt habe, könnte ich mir jetzt anzeigen lassen,
welchen Zustand hat der Zähler?
Weil er hat ja immer jetzt mitgezählt und dann sieht
man hier
Also 5, 5.000 rekursive Aufrufe, dann ist das Ding voll.
Das ist relativ wenig. Da ist nicht viel Platz reserviert,
weil üblicherweise so tiefe Rekursionen nicht ausgeführt werden. Also,
dass die Rekursionstiefe, das wirklich mehrfach innerhalb einer Rekursion,
Wiederrekursion auch, also etwas rekursiv sich mehrfach aufruft,
das ist relativ selten der Fall.
Mit so einem typischen Java-Programm gibt es so Aufrufketten,
so vielleicht 10, 20, 30, irgendwie sowas.
Das eine Objekt ruft das nächste auf und das ist das
nächste und so weiter. Das sind ja alles Aufrufketten,
aber sowas kommt eher selten vor.
Immer wenn der Stack überläuft, könnt ihr davon ausgehen,
dass ihr irgendwo eine endlose Rekursion definiert habt.
In diesem Fall ist das
fast eine endlose Rekursion
Ja, weil das wird ja dann irgendwie am Ende wieder und so weiter.
Gut, ja, das ist soweit so schön dazu.
Dann einfach noch mal ein Beispiel, wie das in
dem Unix-System da ist. Also wenn ihr wisst,
Unix-Systeme kennt ihr, Ubuntu, Linux-Systeme sind im Kern-Unix-System auch.
MacOS ist im Kern ein Unix-System. Da ist das
so ganz clever gemacht. Da haben die gesagt,
wir haben einen gemeinsamen Speicherbereich und die laufen gegeneinander.
Also Stack und Heep. Das heißt,
die benutzen eigentlich denselben Speicherbereich, sodass für beide mehr Platz ist,
wenn also der eine mal mehr braucht, ist das nicht so schlimm.
Und wenn natürlich beide vier Platz brauchen, wird es knapp.
Die laufen also hier gegeneinander
So, genau. Und das einfach nur, dass ihr das
auch nochmal gesehen habt. Was jetzt nochmal ein bisschen klarer ist,
hier hat dir jemand vor ein paar Wochen mal gefragt,
wo liegen denn diese Objekte?
Wo ist denn der Speicher für die Objekte in einem Java-Programm?
Und jetzt wisst ihr die Antwort, der Speicherplatz wird ja
auf dem Hieb benutzt. Das heißt,
diese Dinger sind jetzt hier mit Absicht gelb eingefärbt,
weil diese Objekte liegen auf dem Hieb.
Der Speicherplatz für diese Objekte liegt auf dem Hieb.
Das ist ein Schnappschuss vom Hieb. Zeigt also hier so
ein Ausschnitt des Objektgeflechtes, das in dem Hieb liegt,
in der Virtual Machine
Hier ist das mit der Uhrenanzeige. Ihr erinnert euch daran,
die Uhrenanzeige hatte irgendwie zwei Exemplare der Nummernanzeige.
Das Java-Projekt habe ich hier auch dabei und kann daran nochmal
eine kleine Sache zeigen. Ich habe den Debugger mal zu.
Ach, den brauchen wir gleich auch wieder. Ich mache
hier nochmal ein neues Java-Projekt auf. Diese Zeitanzeige. So,
und da kann ich ja jetzt hier in der Uhrenanzeige,
falls ihr euch daran erinnert, wir haben also hier Exemplarvariablen,
Nummernanzeige und noch eine Exemplarware vom Typ Nummernanzeige,
das sind dann internen Referenzen,
wissen wir ja.
So, jetzt kann ich hier auch in dem Konstrukt
dann mal einen Haltepunkt setzen
Auch das ist was, was ganz normal eine Stelle,
wo man einen Haltepunkt hinsetzen kann.
Geht also auch in dem Konstruktor. Und wenn ich jetzt
mir eine Nummer an eine Uhrenanzeige erzeuge und sage,
die soll jetzt initialisiert werden mit 23 Uhr 59,
dann hält er jetzt hier im Konstruktor an.
Das heißt, das Objekt der Klasse Uhrenanzeige,
das Exemplar, ist jetzt schon erzeugt.
Der Konstruktor ist ja nur der Initialisierer. Das heißt,
ah ja, sehr schön, das führt dazu,
dass wir gleich kein Sturissi machen können.
Egal, müssen wir jetzt mal eben drüber hinwegsehen,
wird hoffentlich gleich wieder klappen. Jetzt stehen wir hier am Anfang
dieses Konstruktors und hier wird jetzt gleich aufgerufen, You-Nummern-Anzeige.
Also es wird jetzt ein Exemplar der Nummernanzeige erzeugt,
wenn ich jetzt Schritt übermache, dann sieht man hier momentan
diese beiden Exemplarvariablen hier, sind noch null.
Also da ist noch keine Referenz eingetragen.
Wenn ich jetzt sage Schritt über und diese Zeile wird ausgeführt,
dann ist jetzt hier tatsächlich eine Objektreferenz eingetragen.
Super, was das Schöne ist, ja,
auch wieder hier coole Sache im Debugger von Blue Jay,
ich kann da jetzt draufklicken.
Doppelklick und dann sehe ich schon hier tatsächlich diesen Objektinspektor von
dem neuen Objekt
So, und dann habe ich hier, wenn ich den
nächsten Schritt mache, also die nächste Nummernanzeige auch erzeuge,
dann wird hier die Objektreferenz auch gesetzt.
Jetzt haben wir also hier tatsächlich zwei Objektreferenzen,
einmal für die Stunden, einmal für die Minuten.
So, hier nebeneinander. Und wenn ich jetzt hier noch Sätze Uhrzeit aufrufe,
dann wird an diesen beiden Exemplaren hier auch gleich 23.59 Uhr gesetzt.
Ja, mach doch mal, mach doch mal.
So, lässt dich. Und wenn ich jetzt hier nochmal
sage, Schritt über, dann wird das tatsächlich ausgeführt.
Und dann sieht man auch bei diesen kleinen Objekten hier oben,
die sind jetzt auf 23.59 Uhr gesetzt. So,
und dann habe ich auch tatsächlich jetzt an dieser Stelle ich das jetzt hier zumache,
habe ich tatsächlich hier, jetzt hat es wieder geklappt.
Hier habe ich jetzt mein Exemplar und das ist dieses Exemplar hier und das hat jetzt die Belegung,
dass es jetzt zwei Referenzen hält,
wenn ich jetzt hier Doppelklick drauf mache,
der Objektinspektor nach vorne geholt und hier wird der nach vorne und das.
Also quasi das gleiche Objektdiagramm, wie auf der Folie,
nur jetzt hier in Blue Jay.
Also das habt ihr schon mal gesehen und das habe ich
hier auch noch mal so ein bisschen jetzt erläutert,
wie das denn zur Laufzeit dann auch aussieht.
Also was ihr gehört habe von mir,
es gibt diese Unterscheidung, Unterscheidung zwischen statischen und dynamischen Eigenschaften.
Und gerade bei Methoden und Feldern kann man sich das
noch mal ganz gut verdeutlichen. Also zur Übersetzungszeit,
wenn ihr den Quelltext anguckt, gibt es jede Methode nur einmal.
Wir stehen einfach einmal im Quelltext. Und die Exemplarvariablen stehen
auch nur einmal da. Im Quelltext einer Klasse.
Also auch hier bei diesem Projekt hier,
ich habe hier eine Uhrenanzeige als Klasse und eine Nummernanzeige,
aber zur Laufzeit habe ich zum Beispiel zwei Exemplare von der Nummernanzeige.
Aber sowohl die Methoden als auch die Felder sind alle
nur ein Fall, einmal definiert.
Zur Laufzeit gibt es für jedes Exemplar in der Klasse
einen eigenen Satz Zustandsfelder
Also auch dann logisch einen eigenen Satz Methoden. Das heißt,
man kann sich das so vorstellen, ich habe drei Exemplare,
nehmen wir mal an, einer Klasse zur Laufzeit,
dann sehen die logisch so aus,
hier sind die dreimal diese Felder und drumherum die Methoden,
also jedes Objekt hat logisch seine eigenen Methoden.
Einige von euch haben das sich vielleicht schon mal gefragt,
wo liegen denn diese Methoden? Naja, das ist wieder genau
die Idee. Bei der logischen Sicht ist es so,
ich habe tatsächlich jedes Objekt hat seinen eigenen Satz an Methoden,
die den Zugriff auf die eigenen Exemplarvarianten zulassen.
Aber das ist natürlich Quatsch, dass jedes Objekt den Code
für die Methoden hält. Das wäre eine ziemliche Platzverschwendung.
Also das ist im Speicher anders. Da gibt es
den Hieb
Und für jedes Objekt und dann wirklich nur für die Datenfelder,
für die Zustandsfelder, gibt es in dem Hieb hier diese drei Objekte.
Also nur die Felder werden in dem Hieb angelegt.
Und es wird aber eine Zusatzinformation bei so einem Objekt
mit abgelegt, wo denn die zugehörige Klasse ist.
Und das liegt in dem Teil, wo der Programm Code liegt.
Und in dem Programmcode hat man quasi dann hier so nur die Methoden.
Also die Methoden nur einmal da im Speicher. Und jedes
Objekt weiß ja immer, zu welcher Klasse er es gehört.
Und dann kann sozusagen zur Laufzeit, wenn das Zeug ausgeführt wird,
immer die richtige Methode auch gefunden werden.
Das ist hier an dem Beispiel, was ich gerade gezeigt
habe, auch nochmal zu sehen.
Also auf dem Stack war jetzt hier der Aufruf von dem Konstruktor
Ja, also wir waren jetzt so mit dem Kontrollfluss am Rumpfende des Konstruktors von Uhrenanzeige.
Ja, dann gibt es sozusagen hier eine Referenz auf das aktuelle Objekt.
Das ist eine exemplare Uhrenanzeige, enthält zwei Exemplarvariablen,
Nummernanzeige und Nummernanzeige und die haben alle außerdem noch eine Referenz auf den Bitecode,
wo dann die einzelnen Methoden beschrieben sind,
was sie wirklich tun sollen.
Das einfach nur, um euch das nochmal zu verdeutlichen,
was hinter den Kulissen alles so passiert.
Gut, und jetzt, nachdem ich das erklärt habe,
könnt ihr möglicherweise nachvollziehen, was ein Garbage-Collector macht.
Ich habe ja gesagt, der Garbage-Collector kümmert sich darum,
dass Platz auf dem Hieb, also Objekte,
die nicht mehr gebraucht werden, dass der Platz wieder freigeräumt wird.
Das ist eigentlich relativ einfach, wie der funktioniert.
Erstmal ganz wichtig, alle Objekte, also alles,
was ihr mit New erzeugt, alle diese Objekte liegen im Hieb.
Das ist eine Grundregel bei Java. Auf dem Aufruf-Stack,
da liegen ja die ganzen Referenzen auf die Objekte.
Also von dem Stack aus passiert ja alles. Der Stack
ist sozusagen die zentrale Kontrollstelle, wo jeder Methodenaufruf ausgeführt wird.
Und da liegen Referenzen auf Objekte in den Hieb rein. So,
und nur die Objekte, die vom Aufruf-Stack aus erreichbar sind,
spielen für die Programmausführung noch eine Rolle.
Alle anderen Objekte sind tote Objekte
Das heißt, wenn ihr in einer Methode,
lokal ein Exemplar zeugt mit New und das nur in der lokalen Variablen,
die Referenz darauf haltet, die Methode wird abgegebaut,
damit ja auch die lokale Variable weggeschmissen.
Und dann wird auch das Objekt irgendwann weggeschmissen, was im Hieb liegt.
Und dann ist das Vorgehen vom Gabelstreck da eigentlich ganz einfach,
in regelmäßigen Abständen geht er einfach durch den Stack durch,
guckt, welche Referenzen gibt es denn vom Stack in den Hieb rein,
verfolgt das alles rekursiv weiter und erfasst damit alle noch erreichbaren Objekte im Hieb,
markiert die und alle hinterher,
die nicht markiert sind,
die können weggeräumt werden
So funktioniert ein Garbage Collector,
nennt sich Markieren und Abräumen, beziehungsweise Mark and Suite.
Wenn ihr danach googelt, werdet ihr ganz viel finden.
Mark and Suite, Garbage Collection. Das war jetzt Hintergrundinformation.
Das ist jetzt die Zusammenfassung von dem, was ich bis jetzt erzählt habe.
Also rekursive Methodenaufrufe sind eine alternative Möglichkeit für Wiederholung.
Das werdet ihr jetzt auch in den Übungen in der Woche
so ein bisschen sehen.
Man kann das Gleiche rekursiv und iterativ schreiben.
Jeweils mit spezifischen Vor- und Nachteilen. Es gibt so Fälle,
wo man eben nicht rekursiv arbeiten soll.
Es gibt aber auch Fälle, wo es sich anbietet,
rekursiv zu arbeiten. Da geht es ganz viel darum,
wie verständlich und wie sicher ist denn das,
was man da so programmiert
Und hinter den Kulissen von modernen Programmiersprachen stehen halt immer
ein Aufrufsdeck, eher so diese Methodenaufrufe oder Prozeduraufrufe verwaltet.
Und der Hieb, der sozusagen als zentrale Struktur alles
dynamischen Objekte verwaltet. Gut. Und ihr wisst ja,
ich frage ja ganz gerne auch mal was mit Storesi.
Machen wir jetzt mal so eine kleine vor der Pause,
so eine kleine Storesi-Umfrage. Wir haben eine Storesi,
wo ist Storesi, wo bist du? Nächste Frage.
Das ist die Frage, welches Ergebnis liefert diese Methode,
wenn bei einem Aufruf als aktueller Parameter der Wert 3 übergeben wird?
Also ich rufe das Ding auf als Klient, Das heißt,
innerhalb dieser Aktivierung der ersten Aktivierung von M ist P3.
Und dann sieht man hier eine rekursive Definition.
Also hier, M wird in M wieder aufgerufen.
So, jetzt sollt ihr mir sagen, was kommt da
als Ergebnis raus? Kommt da A12 raus, B15,
C18 oder D121. Versucht mal mit dem,
was ich euch erzählt habe, nachzuvollziehen,
wie jetzt hier diese Methode rekursiv aufgerufen wird und was dann da berechnet wird.
Lasst euch in Augenblick Zeit, das ist nicht
so ganz trivial
Das ist üblicherweise auch eine klassische Klausuraufgabe. Dass sie so
eine rekursive Definition seht, dass ihr verstehen müsst,
was rekursiven Sohn ist und dass ihr im Kopf quasi ausrechnen könnt,
nachvollziehen könnt,
was würde passieren,
wenn das
jetzt wirklich ausgeführt wird
Ja. Ah, jetzt kommen hier ein paar mehr Ergebnisse
rein. Okay, entscheidet euch langsam für einen Wert.
So, bei 120. Ja, Gott.
Na komm, 150 Stimmen kriegen wir zusammen
Ja. Gut, das soll dann auch erst mal reichen.
Ich glaube, jetzt kommt da nicht mehr viel.
Doch, jetzt haben wir 150. Sehr schön. Wir gucken erst
mal auf die Verteilung. Hier gibt es jetzt eine richtige Antwort.
Wir gucken mal auf die Verteilung. Die Mehrheit,
die überwiegende Mehrheit sagt, es sind, es kommt 18 raus.
Und wenn ich jetzt hier die Stimmen in Prozent nah,
die Stimmen in absoluten Stimmen habe, 110 von euch.
Und das ist tatsächlich auch die richtige Antwort.
Also hier habe ich euch nicht auf Glatteis geführt,
sondern habe euch einfach das gezeigt, was hier schon in der Vorlesung drankam.
Relativ einfach, hier kommt drei rein. Und das heißt,
hier wird geprüft, Prüft FP größer als null.
Und es wird ja hier P um 1 erniedrigt jeweils aufgerufen. Das heißt,
es wird hier tatsächlich rekursiv, dass P einmal mit 3,
einmal mit 2, mal mit 1, aufgerufen,
aber größer als null und mit null nicht mehr.
Also das dreimal wird hier eine Berechnung gemacht und dann ist
es einfach so dreimal P, plus die rekursive Aufruf.
Also es wird am Anfang wird dreimal 3 gerechnet, dann zweimal 3 und dann einmal
3. Einmal 3 ist 9, zweimal 3 ist 6,
sind 15 plus einmal 3 sind dann 18. Deswegen kommt da
18 raus und das ist auch die richtige Antwort.
Prima, wenn so viele von euch das richtig verstanden haben,
ist das
wirklich super
Und dann können wir jetzt in die Pause gehen, fünf Minuten bitte.
Dann sind die fünf Minuten auch schon wieder fast rum.
Also würde ich euch bitten, das Sprechen wieder einzustellen.
Und die Aufmerksamkeit, die dann nach vorne zu wenden.
Ja, ich habe das Gedicht nicht vergessen. Ihr habt
ja schon gesehen, es hat einen gewissen morbiden Charme,
dieses Gedicht,
dieses Lied
Deswegen habe ich mir gedacht, ich mache das mal so
ein bisschen heimelig hier und zeige nochmal einen weiteren Beitrag von früheren Projekten, früheren Turtle-Graphics-Projekten.
So, ja, leise rieselt der Schnee und wir können
jetzt nochmal das Gedicht ein bisschen weiterlesen.
Du schreist, doch, du bist allein zu Haus,
das Monster sticht dir die Augen aus.
Dann bist du still und das ist gut,
es beißt dir in den Hals und trinkt dein Blut.
Ohne Blut bist du bleich wie Kreide, dann frisst es
deine Eingeweide. Dein kleines Bettchen vom Blut ganz rot,
die Sonne geht auf und du bist tot
Schlaf mein Kindchen, schlaf jetzt ein, am Himmel stehen die Sterne leihen.
Schlaf mein kleines Schlafe schnell, dein Bettchen ist ein Karussell.
Schlaf mein Kindchen, schlaf jetzt ein, sonst kann das Monster
nicht hinein. So, das Kind möchte ich sehen,
das dann noch einschlafen kann, aber ich bin da nicht zuständig.
Ja, sehr schön. Also ihr seht, es ist einfach,
ich dachte, das passt einfach so schön zur Weihnachtszeit hier.
Hört gar nicht mehr auf, hier zu schneien,
das ist ja schrecklich. Eigentlich wollte ich das jetzt,
hör auf aus, ja, sehr schön. Gut,
eigentlich geht es jetzt um Strings und um reguläre Ausdrücke.
Und wenn wir mit den regulären Ausdrücken gleich loslegen,
dann wird es ganz lustig.
Da kann man nämlich ganz coole Sachen rausfinden über Zeichenketten.
Also wir werden gleich ans Zeichenkettengenauer ansehen und reguläre Ausdrücke.
Und dieses Gedicht habe ich dann auch genommen,
um es jetzt mal so ein bisschen auszuwerten.
Ich zeige dazu ein weiteres Projekt, dass wir uns dann gleich mal näher ansehen werden.
Das ist dieses hier. Sieht ganz einfach aus.
Es gibt hier einen Zeileneinleser und dieser Zeileneinleser ist ein Objekt,
das nichts weiter macht als aus einer Datei,
das hier ist hier nichts weiter als eine Textdatei.
Ich habe hier einfach einen Textendeter auf und
jede Zeile hier ist eine Zeile in der Datei.
Man könnte auch sagen, es ist eine Zeichenkette.
Das ist also eine Folge von Zeichenketten,
wo jede Zeichenkette eine Zeile in diesem Gedicht ist.
So, und diese Zeileneinleser liest diese Zeilen ein.
Da muss man sich nicht genau angucken, wie das funktioniert.
Der Musterfinder, den wir uns jetzt ansehen werden,
der ist interessanter, der benutzt hier so einen Zeileneinleser,
macht sich also hier einen Zeileneinleser mit schlaflied.text.
Das ist einfach die Textdatei, in der dieses Schlaflied
jetzt drinsteht.
Der Text dieses Liedes
Und jetzt gibt es hier die Möglichkeit, bei diesem Zeileneinleser,
gibt es dann die Methode, gib nächste Zeile.
Es wird also eine Zeile rausgelesen, eine Textzeile aus dem Gedicht.
Das ist dann mit in einem String abgelegt. Und dann
wird hier gesagt, solange diese Zeile ungleich und alles,
also solange ich noch eine weitere Textzeile habe,
mach mal irgendwas mit jeder Textzeile, mit jeder Gedichtszeile.
Und zwar wird hier gefragt, if Zeile Matches Muster,
aha, Muster, das ist hier ein Parameter,
den wir übergeben an diese Methode.
Also ich sage hier irgendwie für jede Zeile im Gedicht,
wenn ein bestimmtes Muster zutrifft auf die Zeile,
dann soll diese Zeile
ausgegeben werden
Also jetzt keine Hexenkunst hier. Und ansonsten steht hier eben
Leser, gib nächste Zeile. Also dieses gibt nächste Zeile,
gib mir mal die nächste Zeile und kriege ich eventuell dann,
wenn das Gedicht dann am Ende ist,
also wenn ich sozusagen am Ende der Deckhack-Textanteil bin,
dann wird mir da auch Nall geliefert.
Also solange ich noch Zahlen bekomme, Weil kann ich hier
für jede Zeile was abfragen.
Das gucken wir uns gleich genauer an. Für verschiedene Möglichkeiten,
was man da so mit Strings machen kann.
Als erstes müssen wir uns aber mal systematisch angucken, was ein String ist.
Ein String in Java ist das Eine Ausprägung des allgemeineren Konzeptes einer Zeichenkette.
Also in allen vernünftigen Programmiersprachen gibt es so etwas wie Zeichenketten.
Ein Typ für Zeichenketten. Und eine Zeichenkette ist einfach erstmal
nur eine Folge von einzelnen Zeichen.
Hier ist ein Beispiel. Vierter Adventfragezeichen. Ganz einfache Sachen,
Anzahl der Zeichen wird auch ihre Länge genannt.
Also diese Zeichenkette zum Beispiel ist zehn Zeichen lang.
Dann haben wir hier die Indizes mit aufgeführt.
Also ich kann sozusagen auf die einzelnen Zeichen einer Zeichenkette zugreifen.
Jedes Zeichen hat einen Index
Informatik ist es so, dass diese Indizierung mit null beginnt.
Das heißt, das erste Zeichen hat den Index null.
Das ist etwas, da müsst ihr euch
sehr stark daran gewöhnen. Wenn ihr mit Programmierung umgeht,
das passiert eigentlich fast immer, dass das so gemacht wird.
Gut, konzeptuell ist es so, dass Zeichenketten unbegrenzen.
Das heißt, ihr könnt euch beliebig lange Zeichenketten vorstellen und
üblicherweise ist in der Programmierung es auch möglich,
beliebig lange Zeichenketten im Speicher abzulegen.
Beliebig, solange der Speicher nicht überläuft, ist klar.
Ihr habt ja gesehen, dass es da durchaus Begrenzungen gibt.
Eine Unterstützung für Zeichenkettenverarbeitung ist extrem nützlich bei fast allem,
was ihr mit Texten arbeitet
Das heißt also, Prosa verarbeiten, Quelltexte verarbeiten,
das ist allgegenwärtig. Also das ist etwas, was sehr
zentrales in der Informatik auf Zeichenketten irgendwelche Dinge zu tun.
In objektnotierten Sprachen, wie auch wie Java,
ist es üblicherweise so, dass die als Objekte modelliert werden.
Das heißt, wir haben ein Datentyp, Zeichenkette allgemein,
die Wertemenge sind einfach alle Zeichenketten, beliebiger Länge,
das ist eine ziemlich große Wertemenge.
Und dann gibt es so Operationen, wie gib mir mal
die Länge einer Zeichenkette oder gib mir mal von einer Zeichenkette
einen Teil oder gib mir von einer Zeichenkette ein Zeichen an einer bestimmten Position.
In Java heißt dieser Typ String,
den habt ihr schon kennengelernt
Diese Klasse definiert, wie alle Klassen in Java einen Typ.
Das heißt, ihr könnt Variablen vom Typ String deklarieren.
Dieser Typ String ist in Java spezial behandelt.
Der ist in die Sprache stark integriert.
Also er ist erst mal Teil der Sprachdefinition. Ja,
also es gibt tatsächlich hier auch eine Dokumentation der Klasse String.
Und die können wir uns auch ohne weiteres mal angucken.
Ich kann ja auf den Link klicken.
Und wenn jetzt hier das mit dem Netz klappt,
dann kriege ich hier auch die Seite angezeigt.
Und das ist so das erste Mal,
dass ihr jetzt die Dokumentation einer Klasse seht,
die nicht innerhalb eines Blue-Jay-Projektes definiert ist.
Bisher habt ihr immer dieses Zeug hier,
dieses Java-Doc generierte Zeug, gesehen innerhalb eines Java-Projektes.
Das liegt jetzt außerhalb des Java-Projektes. Die Klasse String ist
ja woanders definiert, in einem bestimmten Kontext Java-Lang.
So, Java-Lang String. Und hier ist aber genau
die Schnittstelle beschrieben, was ein String so ist.
Hier steht irgendwie String-Class-Represence, Character-Strings, All-Strings, bla bla bla.
Strings are constant, they're weldious cannot be change.
Das gucken wir uns gleich genauer an.
Hier sind einige Beispiele dazu, was man damit machen kann.
Dann gibt es hier ganz viele Konstruktoren, oje,
Und dann kamen hier so Methoden,
also sowas wie Char-Ed, einmal einem bestimmten Index,
gib mir doch mal das Zeichen in der Zeichenkette.
Oder vergleiche mal einen String zu einem anderen String,
sind die irgendwie größer, kleiner. Da gibt es ja
so verschiedene andere Sachen, sind zwei Strings gleich und so weiter.
Das ist erst mal so die Schnittstelle, die uns da angeboten wird.
Diese Spezialbehandlung ist in Java an zwei Stellen deutlich erkennbar.
Zum einen gibt es sowas wie Stringliterale. Ihr könnt tatsächlich ja
so eins dringendliteral wie Banane hinschreiben und der Compiler weiß, das ist eins dringend.
Also wenn ich jetzt hier eine Direkteingabe hier hinschreibe, Banane,
Dann ist das eins dringend. Ich kann tatsächlich mir hier,
sonst dringend dann wieder hier rüberziehen und sage hier irgendwie S1,
dann habe ich hier tatsächlich jetzt eins dringend,
habe hier ziemlich viele Methoden, die ich aufrufen kann.
Alles Mögliche hier noch mehr und dann noch mehr.
Also es sind ziemlich viele. Das ist schon ziemlich mächtig
. Und das ist das eine. Also der Compiler erkennt
diesen speziellen, diese speziellen Literale als vom Typ String.
Und ich kann den Operator plus anwenden darauf.
Ich kann also hier sagen, das sei jetzt hier
S1 kann ich ja mir anzeigen lassen, ist Banane.
Haben wir hier sozusagen links benannt.
Jetzt kann ich hier schreiben, S1 plus Moos.
Was kommt raus? Bananenmoos. Toll. Also ich habe
zwei Zeichenketten miteinander verkettet mit Plus. Das ist also der
Plus-Operator überladen. Er ist auch zuständig für die Verkettung von Zeichenketten
und nicht nur für die Addition von Gleitkammerzahlen und ganzen Zahlen.
Das ist eine Spezialbehandlung. Das ist der einzige Fall,
wo das geht in Java.
Das ist also ad hoc. Das ist einfach so,
in der Sprache kann man das Plus da so verwenden.
Ihr habt aber keine Möglichkeit für eure eigenen Typen,
diesen Plus-Operator auch zu benutzen
Andere Sprachen können das, Shava kann das nicht. Gut,
also das sind so die beiden Spezialbehandlungen, Datentyp String,
Wertemenge, wie das String-Exemplare, beliebiger Länge.
Und Operationen längs, conkett, substring und so weiter.
Was ihr schnell merkt, wenn ihr mit Strings umgeht,
ist, dass ihr teilweise auch in so einem String so ein Tüdelchen unterbringen wollt.
Also wenn ihr sowas haben wollt wie, bitte einmal A sagen.
Dann könnt ihr das versuchen mit System Out Print-Unterschied hier bitte einmal A sagen.
Dann sieht man aber, hier wird die Zeichenkette angefangen,
das Literal, und da ist es dann zu Ende.
Und dann steht hier irgendwie A,
und dann sagt der Compiler, nee,
A,
kenne ich nicht
Wenn ihr so etwas damit reinhaben wollt, in die Zeichenkette
mit reinhaben wollt. Etwas, was die Zeichenkette eigentlich begrenzt,
dann müsst ihr das mit so einem Tüdelchen machen hier.
Mit so einem bezahlender Escape-Sequenz. Und wenn ihr dieses Escape-Zeichen selbst
ins Trinkreihen haben wollt, könnt ihr das eben halt mit einem doppelten Backslash machen.
Dann ist der auch Teil der Zeichenkette. Das werdet ihr
auch in den Übungen wahrscheinlich nochmal genauer sehen.
Ich kann das hier auch nochmal zeigen. Ja, brauche
ich aber nicht. Was viel wichtiger ist, ist,
dass die Klasse String unveränderliche Zeichenketten definiert. Das heißt,
Ich habe zwar diese ganzen Methoden,
die man an einem String aufrufen kann, ziemlich viele,
aber diese Methoden ändern nie den Zustand an einem Zeichenkartenobjekt,
an einem String-Up hier.
String-Objekte sind unveränderlich. Wie so ein Diamant. Und das ist ziemlich cool,
das ist einer der Heldentaten der Java-Designer, neben vielen.
Die Sprache ist wirklich schön definiert, dass die Zeichenketten unveränderlich definiert haben.
Das heißt, wenn ihr diesen Infix-Operator benutzt hier,
plus dann verkettet ihr zwei Strings zu einem neuen String.
Ihr verändert nicht irgendwas an dem Originalstring. Hier ist so
ein klassisches Beispiel. Ich habe hier Schreibe hierhin S,
also S1, sagen wir mal, soll den Wert bekommen,
FCKW, so, ja, kann er das sein,
a value to a final, wow, Final,
das ist ja immer was ganz Cooles.
Final variable S1. Na gut, dann mache ich hier einfach
eine neue Variante, das ist dringend S,
ist gleich F-C-K-W-W. So. Ah, müsste eigentlich gehen?
Ja, das kann er. Also, er ist das FCKW.
Wenn ich jetzt sage S.2 Upper Case, Das kann ich hinscheiden.
Dann liefert mir der Aufruf eine neue Zeichenkette. FCKW,
wo eben halt alle Zeichen von Kleinbuchstaben in Großbuchstaben umgewandelt sind.
Aber das S ist unverändert. Wenn ich mich jetzt erst
anzeigen lasse, ist es immer noch FCKW.
Es wurde mir ein Neujahrstring geliefert. Ihr könnt Strings nicht verändern.
Und das ist ein Fehler, den viele häufig machen,
dass sie denken, dass man das könnte.
Also merkt euch wirklich, es dringend sind unveränderlich. Das zweite Problem,
was Java hat, mit Strings oder Problem,
was eben halt, wo man darauf achten muss,
ist, dass man die Gleichheit von Strings nicht mit Gleichgleich
testen sollte
Also sowas wie Banane, gleich gleich gleich Banane,
das kann der Compiler noch sehen, dass es dieselbe Zeichenkette ist.
Aber schon, wenn ihr sowas macht, hier,
Banane ist gleich New String Banane, das kann das hier mal zeigen.
Also ich habe jetzt hier S gleich Banane. Ja,
ach, jetzt habe ich was, was ich vorher falsch gemacht habe.
Ich habe hier keine Tüdelchen angegeben. Er ist gleich Banane
. So, jetzt hat er erst den Werk Banane.
Jetzt kann ich fragen, ist denn S gleich gleich New String?
Ich definiere mir einen neuen String mit Banane Banane
Ja, nee. Meine Güte. So. Ist das gleich
. Und die Antwort ist natürlich Falls,
weil da wird mir ein neues Objekt erzeugt,
New String Banane, liefert mir eine Referenz zurück.
Ich habe eine Referenz auf einen String,
die Strings sind von ihren Zeichenketten gleich.
Aber was verglichen wird an dieser Stelle, sind die Referenzen auf die Zeichenketten.
Das heißt, hier kommt Falls raus. Wenn ich wissen will,
ob sie wirklich dieselbe Zeichenkette repräsentieren,
dann muss ich sagen,
as equals
new string Banane
Dann vergleiche ich tatsächlich zwei Zeichenketten, indem ich Equals aufrufe.
Ich übergebe dem einen drin, eine Referenz auf einen anderen dringen.
Und dann werden wirklich die beiden Zeichenketten verglichen und nicht die Referenzen.
Deswegen ist es so wichtig, dass ihr Referenzen versteht,
weil allein schon an dieser Stelle kann man wirklich sich ins Knie schießen,
wenn man da was falsch macht.
Das ist in den Übungen teilweise schon passiert,
dass ihr tatsächlich das verglichen habt bei dieser Ist-Frage-Geschichte,
ob denn das die leere Zeichenkette ist.
Habe ich ganz häufig gesehen. If, meine Zeichenkette,
mein formaler Parameter, gleichgleich die leere Zeichenkette.
Das funktioniert teilweise, weil der Compagner das optimiert,
weil der intern irgendwie weiß, ah,
leere Zeichenkette kann ich irgendwie
Im Allgemeinen wird das nicht funktionieren. Also, merkt euch,
Strings in Java immer mit der Equals-Methode vergleichen.
Gut. Soweit Strings. Jetzt kommen wir zu einem Thema,
wo man String-Verarbeitung mal ein bisschen anwenden kann.
Denn wenn ihr euch überlegt, das, was ihr hier seht,
hier so eine Klasse, die irgendwie Quelltext enthält,
das ist nichts weiter als ein ziemlich langes Dring.
Man kann tatsächlich eine Klasse in Java, eine Klassendefinition eigentlich,
auffassen als einen sehr langen String, eine sehr lange Zeichenkette.
Das heißt, was ein Compiler macht,
wenn ihr eure Programme übersetzt,
der arbeitet auf Zeichenketten
Auf ziemlich langen Zeichenketten. Also hier, Quellprogramm wird
erst mal angesehen als eine lange Folge von Zeichen.
Dann gibt es die sogenannte lexikalische Analyse. Das ist etwas,
was man nicht wissen muss, aber das ist hilfreich,
so ein bisschen Hintergrundwissen zu haben.
Der zerlegt dann euer Programm in Token. Also diese
einzelnen Zeichen werden dann schon zu etwas reicheren Einheiten zusammengefasst.
Also der für menschenlesbare Quelltext wird durch einen Scanner in eine
Folge von Token zerlegt, das ist die lexikanische Analyse.
Und hinterher wacht dann ein Paser aus der Token-Form-Folge einen Syntaxbaum.
Müsst ihr im Detail nicht verstehen. Das ist aber das,
was wir uns jetzt hier ein bisschen näher ansehen.
Also hier Kleinsten aus Sicht der Grammatik,
wie hier so eine EBNF habt für Java,
da sind ja diese kleinsten Einheiten Token.
Also gültige Token sind zum Beispiel Next Item, das ist
so ein Bezeichner oder 3.1416. Das wird als ein Token angesehen,
also hier tatsächlich mehrere Zeichen bilden einen Token.
Oder auch hier kleiner gleich oder Wild, Schlüsselwörter,
alles Token. Und dann gibt es noch andere Token,
die sind eben halt nicht logische Token.
Also das zum Beispiel hier Rea Next, minus und Item.
Und die lexikalische Analyse eines Compilers macht nichts weiter
als diese lange Zeichenkette-Folge, zu zerlegen in diese Token.
Also wenn ihr sowas habt wie hier klares Konto,
dann macht die lexikanische Lyse sozusagen hier die Schnitte hin
Sagt, hier schneide ich, hier schneide ich, hier schneide ich.
Und dann sind diese einzelnen Einheiten dann die Token.
So, wunderbar. Jetzt kann man tatsächlich diese Token,
wie sie den aussehen können, definieren.
Also für einen Compiler geschieht das üblicherweise, dass man so
eine Beschreibung angibt, wie den Token aussehen können.
Und ein ziemlich häufiges Token ist ein Bezeichner. Ihr wisst,
ein Bezeichner ist ein Name einer Variablen oder einer Klasse, einer Methode.
Und die Definition ist relativ einfach. Wie darf so
ein Bezeichner aussehen? Ein Bezeichner besteht aus einem Buchstaben,
unter Strich wird auch als ein Buchstabe angesehen,
gefolgt von beliebig zu vielen Buchstaben und Ziffern
Und wenn wir jetzt eine Zeichenkette S haben und von der feststellen wollen,
ob sie ein gültiger Bezeichner ist,
dann fragen wir eigentlich etwas abstrakter,
ob S ein Element der Menge aller gültigen bezeichnet ist.
Also ich kann tatsächlich beschreiben, wie die Menge aller gültigen Bezeichner aussieht.
Und dann frage ich einfach, ist die konkrete, der konkrete Bezeichner,
den ich gerade untersuche, ist ein Element dieser Menge.
Und das ist tatsächlich hier einfach in Java hinzuschreiben.
S, Matches und dann Menge gültiger Bezeichner. Die Methode
Matches ist tatsächlich in der Klasse dring definiert und er hält
als Parameter eins dringend, der dann als reguläre Ausdruck aufgefasst wird.
Das ist also diese reguläre Ausdruck. So, ich kann
also mit einem regulären Ausdruck beschreiben, wie ein Bezeichner aussieht.
Dann gucken Sie das mal an, bezeichnen als reguläre Ausdruck.
Also hier sage ich jetzt einfach mal, es dringend,
Menge gültiger Bezeichner, soll so definiert sein.
Das ist die Definition für einen gültigen Bezeichner.
Ich habe das hier in einem Projekt drin. Ah,
guck mal, dass ich irgendwo habe, genau.
Menge gültiger Bezeichner. Kann ich hier mal so ein bisschen rauskopieren.
So und gehe jetzt hier mal in den direkten Eingabe und sage es dringend,
Menge gültiger Bezeichner ist gleich diese Definition hier.
Oh, funktioniert irgendwas nicht? A, das ist Z,
na no, was ist denn hier los?
Mehr gültiger bitte. Ich vermute, dass ich hier irgendwo
die eckigen Klammern mal eben richtig reinsetzen muss.
Machen wir das mal eben hier. Ich vermute es nur.
Ich hoffe mal, dass das jetzt irgendwie tatsächlich daran liegt,
weil das sind die einzigen Zeichen,
wo ich jetzt glaube,
dass es
da schief gehen kann
Ah, es ist der Unterstrich. Na gut, versuchen wir das mal.
Das tut mir leid, das ist jetzt etwas Blöde hier.
Das ist jetzt ziemlich kryptisch aus, was ich hier mache.
Den Stern nehme ich auch nochmal weg. Okay, also
es funktioniert nicht. Kann ich also nicht jetzt hier so live zeigen,
dass man hiermit die Menge der gültigen Bezahler beschreiben kann.
So, und ich kann aber dann tatsächlich bei einer Zeichenkette fragen,
er ist Matches, diese Menge gültiger Bezeichner und kann dann feststellen,
gehört er dazu.
Nehmen wir mal ein etwas anderes Beispiel hier,
das ist auch ein regulärer Ausdruck
Diese eckigen Klammern definieren eine Menge. Und zwar die Menge,
die aus dem Zeichen O und dem Zeichen A besteht.
Das heißt, dieser reguläre Ausdruck hier ist einfach Haar
gefolgt von Entweder O oder A und dann SE.
Das heißt, dieser Ausdruck definiert eine Menge,
nämlich von zwei Wörtern, entweder Hose oder Hase.
Das ist eine Menge von Zeichenketten, die ist durch diesen regulären Ausdruck definiert.
Und man kann auch hier so Bereichsangaben machen,
also zum Beispiel hier 1 bis 3,
wenn ich hier SE1 habe und dann ist in eckigen Klammern wieder so eine Menge definiert,
dann definiert das die reguläre Menge SE1,
SE2,
A bis Z definiert zum Beispiel alle Kleinbuchstaben von A bis Z,
aber nur als ein Zeichen. So, so kann man
also reguläre Ausdrücke definieren und damit Mengen von Zeichenketten definieren. Ja,
und dann haben wir sozusagen hier diesen regulären Ausdruck auch schon fast verstanden,
den ich gerade versucht habe, hier live zu zeigen,
hätte ich wahrscheinlich einfach an eintippen sollen.
Also A bis Z, A bis Z,
das sind also alle großen Kleinbuchstaben und ein Unterstrich,
das ist das erste Zeichen.
Das sind die möglichen ersten Zeichen, eines ist eines Bezeichners.
Und dann gefolgt von beliebig vielen Großkleinbuchstaben unterstrichen und Ziffern und
das beliebig oft. Das heißt, Postfix steht dahinter
ein Stern und der sagt so viel wie das,
was davor ist,
kann beliebig oft auftauchen
Okay. Alternativ könne ich statt dem Stern auch einen Plus
schreiben. Dann kann ich tatsächlich sagen, beliebige Wiederholung,
aber mindestens einmal. Oder ich kann ein Fragezeichen schreiben,
dass ich dann entweder einmal oder gar nicht.
Ein einzelner Punkt steht in einem regulären Ausdruck für ein beliebiges Zeichen. So,
jetzt können wir das ja mal so ein bisschen,
ja, wir können das hier mal so,
gucken wir uns das mal vielleicht lieber mit dem Beispiel an,
was wir hier haben.
Ich habe ja jetzt hier meinen Musterfinder. Ich mache mir
jetzt mal hier so ein Musterfinder und möchte jetzt hier sagen,
und jetzt wird ihm ja ein solcher reguläre Ausdruck übergeben.
Und ich möchte zum Beispiel, ich habe hier,
glaube ich, ein paar Beispiele dafür.
Man muss das mal so ein bisschen kleiner machen,
damit ihr das nicht schon alles seht.
Alle Zeilen, die die Zeichenfolge ein enthalten.
Ich möchte also in dem Gedicht jetzt ein,
ich möchte einen regulären Ausdruck übergeben, der für jede Zeile im Gedicht sagt,
matcht diese Gedichtszeile auf den regulären Ausdruck.
Und ich möchte mit dem regulären Aufdruck beschreiben,
dass die Zeile das Wort, die Zeichenfolge ein enthält.
Das heißt, was ich jetzt hier schreibe ist,
ich schreibe einen regulären Ausdruck, das kann ich ja mal vormachen.
Ich sage jetzt erstmal, versuche es mal mit ein.
Diese Zeichenfolge ist auch schon ein regulärer Ausdruck und da kommt nichts
bei raus. Man sieht, dir kommt nichts raus.
Machen wir mal was anderes. Ich zeige jetzt mal,
ich sage, es kann ein beliebiges Zeichen sein,
ein Punkt ist ein beliebiges Zeichen, gefolgt von einem Stern.
Also beliebig viele Zeichen, beliebige Zeichen, dann die Zeichenfolge
ein und dann wieder Punkt Stern. Damit habe ich sozusagen gesagt,
egal wo, dieses EEN steht in der Zeile,
dieser Ausdruck hier beschreibt alle die Gedichtszeilen,
in denen das Wort die Zeichenfolge ein drinsteckt.
Und dann kommt tatsächlich hier immerhin so ein paar Zahlen raus.
Mein Kindchen, Fans da rein, schlafe mein und so weiter.
Also das ist das eine. Gut, nochmal ein
anderes Beispiel, alle Zeilen, die mit dein beginnen.
Okay, wer kann mir mal sagen, wie ich da jetzt
den regulären Ausdruck hinschreibe? Hat mal jemand eine Idee?
Dein Punkt, der. Er sagt, dein Punkt,
Stern, das ist genau richtig, also dein Punkt,
Stern, also dein gefolgt von beliebig vielen Zeichen.
Und dann kommt da irgendwas raus, die zwei Zeilen,
wo das dein am Anfang steht.
Gott, nochmal ein anderes Beispiel, alle Zeilen mit einem
Vokal an dritter Stelle.
Wie würdet ihr
das machen
Alle Zeilen mit einem Vokal an dritter Stelle.
Hatte ich mal eine Idee? Ja. Okay,
probieren wir das mal aus. Also hier finde Muster,
dann sage ich hier, was hat er gesagt?
Du hast gesagt, zwei Punkte oder drei Punkte.
Der dritte, genau. Dann A-E-E-E-E-I-O-O-U-U-U-U-U-U-U-U,
das gebe ich ja als Menge an,
also ein Zeichen aus dieser Menge soll es sein.
Und dann wieder Punkt Stern
Dann habe ich jetzt genau gesagt, die ersten beiden Zeichen sind mir egal.
Das dritte Zeichen soll eins aus dieser Menge sein und danach wieder
beliebig viele Zeichen. Und dann kommen mir genau die raus,
wo jetzt hier an der dritten Stelle ein E,
ein E, ein E, da wo diese Vokale stehen.
Wunderbar. Und so kann man jetzt weitere Fragen stellen mithilfe von diesen regulären Ausdrücken.
Zum Beispiel alle Zeilen, die mit die Augen oder Sterne enthalten.
Und wie man das schreibt, da muss man halt ein bisschen mehr lernen über reguläre Ausdrücke.
Hier sind noch ein paar weitere Beispiele auf der Folie.
Also zum Beispiel hier Jeder String matcht sich selber,
dann kann man hier einfach so A, B und A,
B, das sind ziemlich blöde Beispiele.
Was ganz interessant ist, ich habe ja hier gezeigt,
in nah da oben war es, hier,
wenn ihr da guckt, da gibt es diese Methode Matches,
die habe ich ja gerade benutzt im Rumpf, Matches String Rack Apps,
Check X und Tales whether or not distring matches the given regular expression.
Und da gibt es eben halt tatsächlich einen Link und dann ist hier tatsächlich beschrieben,
wie man die Syntax von diesen regulären Ausdrücken angeben kann.
Das ist also relativ viel Zeug, was man da so machen kann.
Gibt es also auch zum Beispiel hier Backslash D sagt,
Das ist eine Kurzschreibweise für eine Ziffer,
statt eckige Klammern 0 bis 9 kann ich auch einfach backstage.de schreiben.
Das sind alles so Abkürzungen, aber man kann da
eben halt relativ viel und ziemlich mächtige Dinge tun.
Also ihr könnt dann tatsächlich auch in einer Zeichenkette nach einem
bestimmten Muster suchen und dieses Muster ersetzen durch was anderes.
Also zum Beispiel hier gibt es Unstring ein Replace first,
also das erste Auftreten von einer regulären Expression,
soll mit etwas anderem ersetzt werden.
Oder Replace all, ich kann tatsächlich in einer Zeichenkette sagen,
alle stellen, an denen eine reguläre Ausdruck matched,
soll ersetzt werden durch etwas anderes.
Also extrem mächtig, um Textersetzung zu machen auf Zeichenketten
Okay. In Java sehen diese regulären Ausdrücke,
haben wir ja schon gesehen, wie die aussehen.
Ich habe im Skript da so ein paar Folien dazu dazwischen,
die habe ich jetzt hier nicht vorgestellt,
weil es einfach ein bisschen knapp ist.
Die könnt ihr euch angucken,
wenn ihr es zur Vertiefung nochmal ein bisschen besser verstehen wollt,
was da so hintersteht, hinter diesen regulären Ausdrücken.
Wie gesagt, es ist ein reguläre Ausdruck,
beschreibt eigentlich nur eine Menge von Zeichenketten.
Und was man eben halt auch mit den regulären Ausdrücken notieren kann,
ist sowas wie entweder P oder Kuma.
Dann macht man so einen senkrechten Strich hier.
Entweder diese oder die andere Zeichenkette. Und man kann
auch Runde klammern
zum Gruppieren einsetzen
Ja, gut. Nutzung, kurz zum Kontext.
Also ihr kanntet bisher schon kontextfreie Grammatiken.
Also die IBNF ist eine Form von kontextfreien Grammatiken,
die mit so einer IBNF kann ich beschreiben, eine kontextfreie Sprache.
Also alles, was sozusagen gültige Java-Programme sind zum Beispiel.
Das ist eine Menge von Zeichenketten,
also die Menge aller Java-Programme ist eine Menge von Zeichenketten.
Und wenn ich in eine konkrete Zeichenkette habe,
dann kann ich für die feststellen, ist die denn Teil dieser Sprache?
Das ist mit diesen komischen regulären Ausdrücken genauso. Ich habe
eine reguläre Grammatik, also so ein reguläre Ausdruck ist eigentlich eine reguläre Grammatik.
Und die definiert beschreibt, eine reguläre Sprache und das
ist auch wieder nichts weiter als eine Menge von Zeichenketten.
Also zum Beispiel die Menge aller gültigen Bezeichner.
Also ob unter Strich Saldo jetzt ein gültiger Bezeichner ist.
Man fragt also für einen konkrete Zeichenkette, ist sie denn
ein Teil dieser Menge R? Sieht ziemlich ähnlich aus.
Kann man sich fragen, wo ist der Unterschied?
Was soll das Ganze? Werdet ihr in FG1 genauer draufgucken?
Das ist so, es gibt so verschiedene Klassen von Grammatiken
und die kontextfreien Grammatiken sind beschreibungsmächtiger als die regulären Ausdrücke.
Man kann mit kontextfreien Grammatiken tatsächlich korrekt geklammerte Ausdrücke,
also für jede öffnende Klammer muss es eine schließende Klammer geben,
das kann man mit kontextfreien Grammatiken definieren.
Mit einem regulären Ausdruck könnt ihr sowas nicht beschreiben. Das heißt,
die sind irgendwie deutlich schwächer. Können wir jetzt fragen,
warum gibt es überhaupt dann diese blöden, regulären Ausdrücke,
wenn sowieso mit kontextfreien Grammatiken alles beschrieben werden kann?
Genau. Und das ist einfach so, dass die sehr,
sehr effizient sind. Also man kann aus so einem regulären Ausdruck automatisiert,
das macht einen Compiler auch, einen Scanner generieren,
der eben halt sehr schnell für eine Zeichenkette entscheidet,
ob sie in diese Menge reingehört oder nicht.
Und das ist für die Effizienz von Compilern sehr, sehr wichtig.
Aber nicht nur für Compiler, sondern eben halt auch für Suchmaschinen,
für Texteditoren und auch für Programmiersprachen sehr, sehr nützlich.
Das heißt, diese regulären Ausdrücke werdet ihr als Werkzeug,
wenn ihr sie denn gut verstanden habt, sehr zu schätzen wissen.
Es ist aber auch, sagen wir nichts,
was jetzt jeder von euch voll verstehen muss.
Ihr werdet auch ohne die Klausur bestehen. Okay,
das ist die Zusammenfassung. Also, String ist integriert,
sehr mächtige Unterstützung für Zeichenketten. Exemplare, der Klasse String
sind unveränderlich und ihr sollt das Trinks nur mit Equals vergleichen.
Für die Darstellung der Syntax wird die EBNF verwendet,
für so, wie man reguläre, wie man Token beschreibt,
kann man reguläre Ausdrücke verwenden und Java bietet Zeitversion
Eine Unterstützung für reguläre Ausdrücke an. So, ihr wisst natürlich,
jetzt habe ich noch ein paar Minuten Zeit für eine Storesi-Frage,
die ist relativ einfach, hoffe ich, dass die einfach ist.
Ja, ich habe jetzt hier viel reguläre Ausdrücke.
Ich habe die Syntax nur sehr schnell erklärt,
also es ist vielleicht nicht ganz so einfach.
Welche der folgenden regulären Ausdrücke in Java-Notation als Zeichenkette angegeben,
definiert nicht eine reguläre Menge, die das Wort Auto enthält.
Also hier sind vier reguläre Ausdrücke und welcher von denen definiert nicht
Die Zeichenkette Autor. Also alle diese regulären Ausdrücke defonieren Mengen von Zeichenketten.
Und das sind also vier Mengen. Und ich frage euch jetzt,
bei welcher von diesen vier Mengen ist das Auto nicht enthalten?
Dafür muss man halt wissen, was einen Punkt bedeutet.
Man muss wissen, was die Rundenklammern bedeuten.
Man muss wissen, was die eckigen Klammern bedeuten.
Das ist sozusagen hier
die Voraussetzung
Ja, bei 150 mache ich wieder Schluss. Ja,
prima. Ich halte an. Wir blenden mal durch,
was ihr so gesagt habt. Okay, das ist relativ verteilt
. Immerhin hat die Mehrheit das richtig. Es ist tatsächlich
der letzte Ausdruck. Warum ist das der letzte Ausdruck?
Das hier habe ich gesagt, die eckigen Klammern definieren Mengen.
Das heißt, dieser reguläre Ausdruck definiert nur Zeichenketten der Länge eins,
die entweder A oder U oder T oder O sind.
Während das hier alles tatsächlich, hier steht A,
dazwischen zwei beliebige Zeichen, also auch U und T,
und das ist sowieso die Zeichenkette selbst.
Also mit regulären Ausdrücken werdet ihr viel Spaß haben,
wenn ihr sie denn gut versteht.
Schönen Feierabend. Tschüss