Oh, sehr schön. Also ich glaube,
ich fange jetzt meine ganzen Vorlesungen mit Musik an.
Das ist irgendwie viel netter. Ja, herzlich willkommen zur dritten Vorlesung.
Softwareentwicklung. Eins. Was werden wir heute machen? Zwei
große Themen. Zum einen werden wir uns Prozeduren etwas intensiver ansehen,
also Prozeduraufrufe, Parameterübergabe und alles, was dazugehört.
Und zum anderen werden wir uns ansehen, wie Java-Programme syntaktisch aufgebaut sind.
Also welche Regeln gibt es, nach denen man beschreiben kann,
wie ein korrektes Java-Programm aussehen sollte.
Also so ein bisschen was mit Grammatiken, also auf der Meta-Ebene.
Das sind zwei relativ eigenständige Themen. Das eine mache ich
vor der Pause, das andere nach der Pause.
Vorher will ich aber nochmal eine Sache hier, muss man
ein bisschen leise, das scheint mir sehr laut zu sein.
Eine Sache ansprechen, die ich immer wieder in den Übungen so aufschnappe,
auch in Diskussionen mit Betreuern und ist das denn überhaupt der richtige Begriff,
den wir hier lernen in dieser Veranstaltung.
Also da wird dann irgendwie, zum Beispiel die Dinger,
die Zustände halten in Objekten.
Wie heißen denn die so? Bleibt mir mal irgendwelche Worte zu.
Wie nennt man die Dinger, bewusst jetzt Dinger genannt,
in den Objekten, die den Zustand halten eines Objektes?
Hat man jemand eine Idee? Nee, wir werden wieder
... Im Skript werden sie Datenfelder genannt. Ich glaube,
das ist nicht ganz richtig, sonst noch Vorschläge?
Zustand über Reha. Zustandsvariablen, auch sehr schön,
ja. Gibt es sonst noch Vorschläge
Ja. Bitte? Attribute werden sie bei uns nicht genannt.
Richtig. Zustandsfelder. Okay. Also ihr seht schon,
das eine und dasselbe wird sehr unterschiedlich genannt.
Ja, und dann findet man tatsächlich auch noch Lehrbücher,
in denen sie dann Attribute genannt werden.
Was ich unglücklich finde, also wir vermeiden bewusst den Begriff Attribut.
Jetzt kann man wieder sagen, warum machen wir das denn?
Ist das denn nicht richtige, die auch Attribute zu nennen?
Oder Exemplare. Also die Objekte,
die man mit einer Klasse erzeugen kann
Die heißen bei uns Exemplare. Andere nennen die auch gerne
wie. Hat jemand noch einen Vorschlag? Na? Ja
, da oben. Instanzen, genau. Also,
warum heißen die jetzt bei uns Exemplare und nicht Instanzen,
wie in den meisten Lehrbüchern, wie zum Beispiel auch in dem Bluejay-Buch?
Naja, jetzt kann man natürlich sagen, das ist irgendwie ganz dramatisch,
weil das Richtige und das falsche Wort und so.
Aber ich würde das ein bisschen in Frage stellen.
Es gibt gar nicht das Richtige oder das falsche Wort.
Was ich persönlich viel wichtiger finde, ist,
und das ist einfach auch aus meiner eigenen Erfahrung mit Lehrveranstaltung,
an denen ich selbst teilgenommen habe, Ich finde es viel wichtiger,
dass die Begriffe konsistent verwendet werden.
Das heißt, dass man ein klares Vokabular hat in der Veranstaltung,
wo man wirklich weiß, naja, dieser Begriff steht für das.
Und das ist auch durchgängig die ganze Veranstaltung so.
Ich habe einfach schon sehr viel andere Veranstaltungen erlebt.
Und das ist eine Qualität, die wir uns hier ziemlich,
sagen wir, inzwischen anheften können, dass es bei uns zumindest konsistent ist.
Wie gesagt, wir benennen manche Dinge etwas anders.
Das muss euch teilweise auch bewusst sein.
Also wenn ihr dann in anderen Texten was nachlest im Internet,
dann heißen die Dinge halt dann mal Instanzen.
Das ist halt auch wieder eine nicht so schlaue Übersetzung des englischen Worts,
Instance, ein Instance of a Class.
Also haben die diesen Deutschen mit Instanz übersetzt.
Und Instanz ist aber eigentlich relativ unglücklich. Aber es ist
auch etabliert inzwischen in der deutschen OO-Community, dass man von Instanzen redet.
Wir verurteilen das Wort also nicht. Wir verwenden es nur
nicht. Und wir verziehen so ein bisschen das Gesicht,
wenn jemand es verwendet, aber es ist nicht weiter schlimm.
Hauptsache ist es eben halt konsistent verwendet. Und das ist eben halt auch,
was wir jetzt in den ersten ein, zwei,
drei, vier Wochen hier intensiv machen,
dass wir euch ziemlich viele Begriffe bringen und ich dann auch in den Übungen,
zumindest ich, dann immer sehr konsequent auch nachfrage.
Naja, was ist denn das hier und was ist denn das,
was du da siehst und wie nennt man das
Und dann geht es halt oft sehr durcheinander. Und wenn dann
die Begriffe noch nicht mal konsistent sind, ist es noch schwieriger.
Und wie gesagt, ich glaube, wir hier haben inzwischen
ein relativ ausgefeiltes Begriffsgebäude, was auch einfach stimmig ist.
Also das ist der entscheidende Punkt an der Stelle.
Gut, also jetzt zum eigentlichen Inhalt. Wie gesagt,
die erste Hälfte dreht sich um Prozeduren. Ich werde ein
bisschen was erstmal zu den Grundlagen nochmal sagen, auf denen wir hier aufbauen.
Nämlich fast zum von Neumann-Rechner. Wenn ihr mit euren Computern Irgendetwas tut,
dann arbeitet ihr letztlich auf etwas, was im Kern so funktioniert,
wie das, was hier beschrieben ist.
Das ist eine sehr knappe Beschreibung dessen, wie ein Computer funktioniert.
Es gibt Rechnerarchitektoren, also Prozessoren für Computer, die sind anders aufgebaut.
Das sind aber eher die Exoten. Diese Form der Architektur,
wie sie hier beschrieben ist, die von Neumann-Architektur für Computer,
die ist insbesondere deshalb so spannend oder so ungewohnt damals gewesen,
als sie eingeführt wurde, vorhin auch schon,
was weiß ich, 50, 60 Jahren.
Vorher war es so, eine CPU, ihr wisst,
was eine CPU ist, das ist die Central Processing Unit,
das ist sozusagen der Hauptprozessor.
Üblicherweise ist das der Prozessor. Üblicherweise ist es so,
dass vor der von Neumann-Architektur war es so, dass die Befehle,
die ausgeführt werden, auch in der CPU gespeichert waren.
Meistens sogar hart eingebrannt in den Prozessor. Das waren halt
noch diskret aufgebaute Rechnerstrukturen. Und dann wurden Daten verarbeitet,
die irgendwo in einem Speicherlagen, das ist hier der Hauptspeicher.
Also irgendwie arbeitet so ein Prozessor ja immer auf einem Hauptspeicher,
auf dem Daten irgendwie hin und her geschoben werden, verändert werden und ähnliches.
Und es war einfach normalerweise so, im Speicher liegen die
Daten und die Befehle hat der Prozessor sozusagen fest eingebrannt.
Und von Neumann hatte dann diese simple Idee, aber dann doch relativ sensationelle Idee,
dass man auch die Programme in den Speicher schreiben kann.
Das ist für uns heute selbstverständlich,
dass die Programme auch im Speicher stehen, weil wir halt,
wie gesagt, alle mehr oder weniger von meiner Architektur Rechner benutzen.
Das führt aber dann dazu, dass irgendwie, wenn Befehl ausgeführt wird.
Als erstes, es kommt zu einem zweischrittigen Zyklus.
Also zuerst muss der Befehl ja,
der als nächster ausgeführt werden muss, von der CPU,
also hier von dieser Haupteinheit über den Bus, aus dem Speicher geholt werden.
Also irgendwo hat es hier ein Leitwerk üblicherweise einen Zähler,
das ist einfach eine Adresse von dem nächsten Befehl im Hauptspeicher.
Und der erste Teil des Zykluses begriff steht halt darin,
dass aus diesem Dings die Adresse ausgelesen wird,
dann wird von der Adresse im Hauptspeicher der nächste Befehl geholt,
in den Hauptspeicher geladen,
in den Hauptspeicher,
in die CPU geladen
Wird da dekodiert? Also was ist es denn für ein Befehl?
Und dann wird der Befehl ausgeführt. Und der Befehl kann
zum Beispiel heißen, speichere jetzt mal etwas,
einen bestimmten Wert oder berechne etwas und speichere es dann in einer Speicherzelle ab.
Dann wird also hier möglicherweise im Rechenwerk irgendwas berechnet auf der rechten Seite.
Und am Ende wird dann das Ergebnis wieder in den Hauptspeicher geschrieben.
Das heißt also ein Datum oder ein Wert hier reingeschrieben.
Also diese zwei Zyklen hat eigentlich jeder Prozessor heutzutage.
Fetch Execute heißt dieser Zyklus. Ersten Befehl holen,
den auswerten und dann ausführen, executen und dann eventuell auch
wieder Im zweiten Schritt auch noch mal Daten lesen oder auch schreiben.
Und das ist erst mal als Grundlage für uns ganz wichtig,
weil wir halt hier ja imperative Grundlagen lehren.
Das heißt, wir sind nicht sehr weit weg von dieser Logik eines Rechners.
Der Hauptspeicher ist üblicherweise in Zeilen gleicher Größe unterteilt.
Heutzutage bei den heutigen Architekturen sowas wie 32 Bit, 64 Bit.
Und die sind auch alle durchgehend adressierbar. Heutige Rechner irgendwie
vier Giga-Byte, Hauptspeicher, also irgendwie vier Milliarden unterschiedliche Adressen.
Ja, und dann ist es normalerweise so,
dass das Programm dass die Programmanweisungen im Speicher stehen und hintereinander
auch im Speicher stehen und dann eben halt auch entsprechend abgearbeitet werden.
Und von dieser sequentiellen Abfolge kann dann aber teilweise durch Sprungbefehle abgewichen werden.
Also in dem Rechner kann man dann tatsächlich mit diesem Adressregister,
wo ja der nächste Befehl ist, der Befehlsregister,
kann ich dann auch rechnen.
Man kann sagen, so jetzt springe ich mal an die Stelle,
wo an den 500. Befehlen nach dem aktuellen Befehl.
Das ist das, was letztendlich in einem Rechner passiert,
in der Hardware. Das ist die Architektur,
auf der wir eigentlich alles aufsetzen
Was auch noch wichtig ist, die Maschine benutzt Binärcodes.
Das heißt also, alles, was im Speicher liegt,
ist nicht im Klartext abgelehnt oder selten im Klartext abgelegt,
sondern hier liegen halt üblicherweise Binär-Daten.
Sowohl die Befehle sind irgendwie Binär abgelegt, als auch die Daten
irgendwie als Binär da. Bin ja Werte. Okay,
diejenigen von euch, die Rechnerstrukturen noch besuchen als Modul,
die hören da eine ganze Menge mehr drüber.
Für die anderen ist das hier so ein ganz kleiner,
grundlegender Eindruck, wie Rechner arbeiten.
Okay, also hier noch mal ein bisschen dargestellt,
elementaren Operationen bei einem Fonds-Rechner. CPU führt Maschinenbefehle aus.
Dabei werden über den Bus sowohl Befehle als auch Daten vom speichern,
die CPU übertragen und die Ergebnisse dann auch zurückübertragen.
So, und was wir machen ist,
wir sind zwar sozusagen, das ist zwar die Grundlage,
auf der wir aufsetzen mit der imperativen Programmierung,
aber wir abstrahieren von ein paar Dingen.
Wenn ihr programmiert, wisst ihr nicht mehr,
an welcher Speicherstelle wirklich im Speicher ein Befehl steht oder ein Datum.
Das wollt ihr auch gar nicht wissen. Da ist ganz viel Betriebssystem dazwischen,
das dann irgendwie Speichersegmente beliebig verschieben kann und ähnliches.
Was wir als Idee haben, ist,
dass es Anweisungen gibt bei der emperativen Programmierung.
Und die fassen oft Folgen von Maschinenbefehlen zusammen.
Also sowas wie eine Zuweisung in Java
Steht, I ist gleich 3. Dann ist das tatsächlich
relativ viel Aufwand, was dahinter an Maschinenbefehlen passiert.
Das sieht zwar harmlos aus, eine Zeile im Java-Programm,
aber es muss erst mal ausgerechnet werden, wo steht denn diese Variable überhaupt?
Da muss irgendwie dieser Wert 3 irgendwo in Register geladen werden
und dann muss das an die richtige Stelle gespeichert werden,
mehrere Maschinenbefehle, um eine Anweisung auszuführen.
Das ist häufig so. Gut,
und die zweite zentrale Eigenschaft ist, dass wir halt Variablen haben,
Zustandsbehaftete Variablen, die ihre Belegung ändern können.
Das sind die Variablen hier und die abstrahieren auch von physischen Speicherplätzen.
Wir können uns also eigentlich immer so ein bisschen vorstellen,
naja, Jede Variable hat so ein Stück Speicher.
Das ist eigentlich immer richtig. Okay, ja,
wenn wir jetzt das dann uns noch mal ein bisschen,
noch ein bisschen näher ansehen,
dann sehen wir hier von Neumann-Maschine, wie das da abläuft.
Und dann hier die zentralen Kontrollstrukturen,
die ich auch letzte Woche schon mal hier eingeführt habe.
Also Sequenz, Fallunterscheidung, Wiederholung. Was aber dann dazu kommt bei
der Imperativen Programmierung, ist, sind außerdem noch Methodenaufrufe.
Also was wir hier in Java ganz viel machen,
wir rufen Methoden auf.
Und das ist ein sehr zentrales Konzept und deswegen gucken wir
uns das auch noch mal
ein bisschen näher an
Wir gucken uns nämlich jetzt den Prozedurbegriff an.
Aber bevor ich das mache, bin ich nochmal eben
ganz kurz hier vielleicht ein bisschen Blue Jay starten.
Oh. Oh mein Gott, falsche Taste. Schnell wieder
zumachen. Ja, es gibt bestimmt Leute von euch,
die wollen lieber CPlus plus machen.
Nein, wir machen,
wir machen Java
Prozeduraufrufe oder Methodenaufrufe. Ich habe hier einfach mal wieder ein Projekt,
das ihr schon kennt. Ja, das ist die übliche,
das übliche Projekt mit den Kreisen und den drei Quadraten und so weiter.
Hier ist diese Klasse Zeichner. Die kennt ihr auch schon,
aber vielleicht noch nicht in dieser Form.
Das ist nämlich jetzt sozusagen die Musterlösung von Woche 2.
Da ging es ja unter anderem darum, da gab es
eine Methode Zeichner, die hat ein Haus gezeichnet.
Habt ihr gesehen und in einer einen Aufgabe solltet ihr die
mit einem Parameter versehen. Das ist also hier tatsächlich passiert
. Hier haben wir jetzt den Parameter vom Typ String.
Und der wird dann hier irgendwo verwendet,
um zum Beispiel von einer Wand hier die Farbe zu ändern
Das heißt also, irgendwie kann man mit dieser Farbe
Einfluss nehmen auf die Wandfarbe eines Hauses.
Kann man also mal eben ausführen hier, mal so ein Zeichner,
Exemplar und sage dem Zeichner, zeichne doch mal.
Und dann sage ich hier irgendwie, mach das mal grün.
Und dann dürfte da irgendwas rauskommen. Also ein Haus mit
einem grünen, mit einer grünen Hauswand.
Ja, toll. Ganz wunderbar. Aber worauf es mir
ankommt, ist gar nicht mal diese eine Methode hier
Die können wir jetzt mal direkt rausschmeißen.
Was wir jetzt schon gesehen haben, ist,
dass sie Teil der Schnittstelle ist, weil sie nämlich Public ist.
Die schmeißen wir jetzt mal schnell raus.
Du hast das alles gar nicht so spannend.
Das kennt ihr schon. Was ein bisschen spannender ist,
ist das hier, was jetzt hier noch übrig geblieben ist.
Es gibt hier eine weitere Methode, die heißt Zeichnerhäuser.
Und was macht die? Die ruft hier? Aha,
eine andere Methode auf, zeichne Sonne. Zeichne Haus und
dann mit zwei Parametern rot und dann hier nochmal Zeichnerhaus,
gelb 200 und Zeichnerhaus, lila 400.
Was da genau passiert ist, können wir uns ja
mal angucken. Wir lassen hier nochmal das, müssen uns
nochmal übersetzen
So ein, sagen wir mal, das Exemplar.
Jetzt ist natürlich aus der Schnittstelle diese eine Methode verschwunden.
Wir haben nur noch die Methode Zeichenhäuser. Und die sieht
halt so aus. Das sind also jetzt mehrere Häuser,
die hier gezeichnet werden. Und in dem Quelltext ist es
jetzt so, dass es hier eine Methode gibt.
Das ist wieder unglücklich. Wo ist sie denn?
Wo ist sie denn? Zeichnerhaus. Da ist Zeichnerhaus.
Das ist die Methode Zeichnerhaus. Die wird hier aufgerufen mit
einer Farbe und einem Offset. Also um das zu sagen,
wo soll das Haus denn stehen?
Und dann wird hier zeichne, Wand, zeichne Dachzeichen,
Fenster,
zeichne Tür aufrufen
Das ist also alles nur einmal hingeschrieben,
aber man kann dann eben halt drei Häuser zeichnen,
indem man jetzt hier an dieser Stelle dreimal aufruft.
Das ist ein sehr einfaches Beispiel für Methodenaufrufe,
wo man tatsächlich dann in einer Methode eine Dienstleistung zusammenfasst.
In diesem Fall ist es tatsächlich so, dass das alles private Methoden sind.
Das sind also hier tatsächlich private Methoden dieser Klasse. Das heißt,
die Klasse bietet sich selbst private Methoden als Dienstleistung an,
um einfach den Quelltext ein bisschen übersichtlicher zu machen.
Kann man also auch durchaus schon auf dieser kleinen Ebene machen.
Da ist eine Frage, Wasser ist Offset. Offset heißt so viel,
das ist ein doofes, englisches Wort für Verschiebung.
Mit diesem Offset wird festgelegt bei einem Haus,
wenn hier irgendwo zeichne Wand aufset, zeichnet Dach aufset,
also dieser Parameter wird hier übergeben an Zeichner ein Haus.
Und dieser Parameter wird an alle weiteren Methoden weiter übergeben.
Und alle diese Methoden zeichnen an einer bestimmten Stelle etwas auf
dem Bildschirm, also auf die Leinwand.
Und der Offset ist sozusagen von dir gerechnet von dieser Kante.
Also hier ist null und dann ist hier 200 und da ist 400.
Also einfach, dass man die Häuser nebeneinander setzt.
Das heißt, diese Verschiebung muss allen diesen Methoden mit übergeben werden.
Man sieht hier eben halt schon eine ganze Menge in diesem relativ kleinen Beispiel.
Zum Beispiel, dass hier Farbe ein formaler Parameter ist.
Woran erkennt man formalen Parameter? Schmeißt mir mal jemand was
zu? Nochmal habe ich nicht ganz verstanden.
Jein, also er sagt, es steht in der Signatur
Ah, die Signatur ist so ein Begriff,
auf dem haben wir so ein bisschen zu sehr rumgeritten.
Also klar, das hier ist der Methodenkopf. Keine Frage
. Und das hier ist der Name eines formalen Parameters.
Wir erkennen eben formale Parameter daran, dass sie Namen haben.
Während aktuelle Parameter nicht unbedingt einen Namen haben. Wir sehen
da vielleicht dann gleich noch mal ein paar Beispiele dafür.
In der Signatur spielt der Name nämlich eigentlich keine Rolle.
Deswegen ist das ein bisschen verwirrend. Okay,
also hier bekommt diese Methode von dem Klient, der das aufruft,
in diesem Fall ist hier die Methode Zeichenhäuser, Klient von Zeichnerhaus.
Muss also nicht ein anderes Objekt sein. Ein Objekt kann
sich auch selbst aufrufen. Und hier wird zum Beispiel
Rot null übergeben. Und dann ist das tatsächlich so,
dass diese Werte hier ankommen und entsprechend hier auch einfach weitergegeben werden.
Der Witz ist, hier ist es ein formaler Parameter,
weil es steht ja im Methodenkopf drin.
Und an dieser Stelle ist es dann zwar auch ein formaler Parameter,
aber es wird zu einem aktuellen Parameter,
weil das hier ist ein Aufruf einer Methode.
Und beim Aufruf an der Stelle, wo ich eine Methode aufrufe,
gebe ich aktuelle Parameter an. Okay, und das heißt also,
was hier steht, ist eigentlich, es wird nicht
Der formale Parameter-Offset übergeben, sondern was da übergeben wird,
ist der Wert, der in der Variablen gespeichert ist.
Also nur die Belegung der Variablen. Das ist etwas,
was man sich leider sehr klar machen muss,
weil sonst versteht man vieles nicht, was in der Programmierung hier passiert.
Ja, okay, also das erstmal jetzt dazu, zu diesem Beispiel,
da können wir gleich nochmal so ein bisschen mehr drauf zurück.
Was ich jetzt ein bisschen mache, ist,
ich versuche so ein bisschen den Hintergrund zu beleuchten,
wo diese Ideen herkommen und sie kommen natürlich nicht aus der objektantierten Programmierung.
Also so etwas wie Anweisungsfolgen zusammenfassen etwas,
was man dann benennen kann und aufrufen kann,
dass es deutlich älter als die Objektorientierung.
Und der emperative Begriff, der dahintersteht, ist die Prozedur.
Also, das ist in der imperativen Programmierung schon ein ziemlich mächtiger Mechanismus.
Und vieles für die Methoden in der Objektorientierung gilt analog für Prozeduren.
Oder was für Prozeduren gilt, gilt auch für Methoden.
Ich werde noch ein bisschen auf die Unterschiede eingehen am Ende,
aber das lassen wir erstmal außen vor.
Wir versuchen also einfach mal, die Prozeduren uns etwas näher anzusehen.
Häufig ist es so, dass in einer Methode oder in
einer Prozedur fachlich irgendwie einen Algorithmus, eine Anweisungsfolge,
die irgendeine Aufgabe erfüllt.
Implementiert ist. Softwaretechnisch ist es einfach nur eine Folge von Anweisungen.
Haben wir schon besprochen. Im Rumpf einer Methode steht eine
Folge von Anweisungen. Und die Grundidee ist eben halt,
dass man den Namen der Methode oder Prozedur stellvertretend für die Anweisungsfolge sehen kann.
Die Sache mit dem Algorithmus ist hier nur erstmal jetzt angenähert.
Wir werden den Algorithmusbegriff noch ein bisschen näher beleuchten und
ihr werdet ihn intensiver auch nochmal hören in anderen Veranstaltungen,
zum Beispiel Algorithmen und Datenstrukturen
Für uns ist erst mal wichtig, so ein Algorithmus ist
üblicherweise ein endlicher Text. Problemlösungsverfahren. Und der beschreibt also
mehrere Aktionen, die da ausgeführt werden. Und er terminiert,
wenn er tatsächlich zu einem Ende kommt und so weiter.
Und wir haben üblicherweise einen Namen dafür.
Und da setzen wir halt üblicherweise solchen Algorithmen in Prozeduren um.
Also das ist die Grundidee hier. Wir haben eine Anweisungsfolge.
In Max, eine Variable anscheinend, wird hier deklariert.
Dann haben wir zwei Varianten, wo wir nicht wissen, wo sie herkommen.
A und B. FA-Größerbietern ist Max gleich A. Els,
Max gleich B. Was das tut, können wir ahnen.
Soll das Maximum von zwei Zahlen
ermittelt werden
So, jetzt geben wir einfach dem einen Namen dieser Anweisungsfolge.
Und jetzt muss ich an den stellen,
an denen ich das Maximum zweier Zahlen ermitteln möchte in meinem Programm,
muss ich jetzt nicht mehr diese Anweisungsfolge hinschreiben.
Kann ich natürlich immer wieder hinprogrammieren. Ich kann aber auch stattdessen so eine Prozedur schreiben,
der ich einen Namen gebe und wo ich sage,
na, die kriegt halt zwei Parameter.
Und dann macht die das,
dann liefert die als Ergebnis dann das Maximum der beiden Werte.
Und dann kann ich an allen möglichen Stellen in meinem Programm,
muss ich nur noch diesen kleinen Aufruf hinschreiben.
Gib mir mal das Maximum von Zahlen. In diesem Fall 6,
9 ist jetzt nicht so intelligent, dass man da Literale verwendet.
Üblicherweise stehen da Variablen. Und diese Aufrufe können halt überall im Programm stehen.
Also wenn ich jetzt an drei Stellen stehen habe,
muss ich es wirklich da immer nur diese eine Zeile hinschreiben,
statt überall diese ganze Anweisungsfolge.
Das erspart uns also auch viel Redundanz. Da ist eine Frage.
Ich habe gelernt, dass eine Prozedur kein Glück gehabt hat.
Wir haben uns das im Jahre anders. Okay,
die Frage ist, er sagt, er hat gelernt,
dass eine Prozedur keinen Rückgabewert haben darf.
Ob das in Java anders ist. Und da sind wir
wieder bei den Begriffen
Ja, es ist einfach eine Frage von Terminologie, was man wie nennt.
Wir nennen diesen Grundmechanismus, dass etwas aufgerufen wird,
eine benannte Anweisungsfolge, nennen wir eine Prozedur.
Das ist für uns ein Oberbegriff. Und dann kommen wir
etwas näher dann auf die sondierenden Methoden zum Beispiel, die ein Ergebnis liefern.
Die verändernden Methoden, die üblicherweise kein Ergebnis liefern sollen.
So, also das ist einfach,
da hat jemand bei dir eben halt den Wert drauf gelegt,
eine Prozedur ist das eine und wahrscheinlich war das andere eine Funktion.
So, genau. Und Funktionen verwenden wir hier als Begriff
eigentlich auch kaum, weil wir Funktionen eher so verstehen
wie in der Mathematik
Dass sie wirklich Seiten effektfrei sein sollen.
Und das ist in der Objektorientierung eher, sagen wir,
nicht die Regel, kommt auch vor, dass man sowas verwendet.
Aber deswegen haben wir es hier erstmal rausgelassen. Also der
Prozedurbegriff ist hier wirklich ein Oberbegriff für alles sowas, was hier dargestellt ist.
Okay. So, und was natürlich jetzt besonders spannend ist,
haben wir ja gerade schon gesehen, ich kann ja so eine Prozedur parametrisieren.
Da wird es ja erst richtig spannend,
wenn ich die einfach nur aufrufen kann,
das ist relativ langweilig, dann kann ich nur,
entweder rufe ich sie auf oder ich rufe sie nicht auf.
Wenn ich sie aber parametrisieren kann,
kann ich sehr unterschiedliche Effekte erzielen mit derselben Prozedur.
Ich kriege also mehr Flexibilität für den Aufrufer. Der Klient kann mit Parametern bestimmen,
wie denn das jetzt genau ausgeführt wird, diese Prozentur.
Wir haben das gesehen bei den Häusern mit verschiedenen Hauswenden,
verschiedenen Farben für die Hauswende und ähnliches.
Und das ist allgemein, das allgemeine Konzept,
was dahinter steht, ist einfach die Möglichkeit, Parameter zu übergeben.
Wieder auf Maschinenebene passiert das, indem irgendwelche Speicherzellen,
was reingeschrieben wird und dann der Aufgerufene weiß,
in welcher Speicherzelle es entsteht.
In der imperativen Programmierung läuft das etwas besser,
nämlich mit getypten formalen Parametern
Was jetzt für uns wichtig ist, was wir ja auch
schon gesehen haben, man kann in Java Parameter übergeben.
Ihr habt das schon informell ein bisschen gesehen.
Wir gucken uns das jetzt ein bisschen genauer an.
Was für Java ganz wichtig ist, ist,
es gibt nur eine Art und Weise, wie man Parameter übergeben kann.
Und zwar immer per Wert. Also es gibt nur Eingabeparameter
. Eingabeparameter zeichnen sich dadurch aus,
dass an der Aufrufstelle irgendetwas berechnet wird, irgendein Wert,
eine Belegung einer Variablen wird ausgelesen oder irgendetwas wird berechnet.
Und das wird dann mehr oder weniger
in Kopie übergeben
Es gibt auch Sprachen, in denen kann man von
der Aufruferseite aussagen, ich habe jetzt hier eine Variable.
Und die Prozedur, die hier aufgerufen werden soll,
der übergebe ich nicht nur die Belegung der Variablen.
Also hier ist eine Variable I,
die hat eine Belegung mit einem Wert 3,
dann übergebe ich hier die drei und stecke sie da rein,
nein, ich übergebe wirklich mehr oder weniger die Variable.
Ich sage hier, ich gebe das I mit rüber,
dann kann die nämlich hier irgendwie auf dem I arbeiten,
sodass hinterher, wenn die Prozedur fertig ist,
beim Aufrufer in dem I ein N ein anderer Wert drinsteht.
Das nennt man ja sowas wie Ausgabe oder Variablen Parameter. Das heißt also,
ich kann wirklich an der Aufrufstelle etwas angeben, einen Behälter,
mitgeben, indem der die gerufene, prozedurell etwas reinstopfen kann.
Das geht in Java nicht. Es werden einige Leute sagen,
wieso? Es geht doch irgendwie und ihr werdet dann im
Laufe des Semesters sehen, dass das nicht ganz richtig ist.
Es gibt zwar so etwas wie Referenzen in Java,
mit denen man sowas ähnliches erreichen kann,
aber es ist eben halt nicht genau das Gleiche.
Die Parameter, die man in Java übergeben kann, sind alle Eingabeparameter.
Also ihr könnt eigentlich immer nur Dinge reinkopieren. Ihr könnt nicht
über den Parameter mechern. Es muss etwas rausbekommen aus einer Prozedur.
Ihr kriegt da keine Informationen raus. Die einzige Information,
die aus einer Prozedur rausbekommen könnt, Beim Aufruf ist über den Ergebnistyp.
Das ist halt ein sehr enger Kanal,
weil es nur genau ein Wert sein kann.
Ausgabeparameter oder Varialienparameter gibt es zum Beispiel in C-Sharp.
Gibt es zum Beispiel in C-Plus Plus. Das sind alles Sachen,
die wir uns dann vielleicht in fortgeschrittenen Veranstaltungen ansehen.
Also, das ist das Schöne an Java, Javas,
in der Hinsicht ganz einfach.
Es gibt einfach nur einen Mechanismus, um Parameter zu übergeben.
Einfach per Kopie. Okay, hier jetzt einfach mal so
ein Beispiel. Wir haben wieder unser Maximum,
unsere Maximumprozesse
Dur. Und die definiert zwei formale Parameter. Das muss man sich immer merken,
das ist einfach diese Terminologie mal wieder, das ist jetzt,
sagen wir, nichts Ungewöhnliches, ja,
das ist nichts, was wir uns ausgewertet haben,
die heißen in allen Lehrbüchern so, da,
wo die Prozedur definiert ist, wo also sozusagen genau steht,
was sie denn tut, da ist auch festgelegt,
welche Parameter sie bekommt.
Und da nennt man die Parameter dann formale Parameter.
Das ist also die formale Definition. Und hier ist
jetzt festgelegt, diese Prozedur Maximum kriegt immer zwei Parameter.
In Java sind es dann auch wirklich immer zwei.
Wenn da zwei stehen, müssen zwar
übergeben werden
Es gibt andere Sprachen,
da kann man sowas sagen wie, ja,
wenn da jetzt der Klienten nicht der Aufruf von nichts angibt,
dann nehme ich als Default, als Voreinstellung irgendeinen bestimmten Wert.
Geht in Java nicht. Okay, das sind also formale
Parameter und dann gibt es verschiedene Stellen. Das ist eben
halt genau der Punkt. Es gibt eine Definition einer Prozedur,
aber es kann ganz viele Stellen geben, an denen sie aufgerufen wird.
Und da können dann ganz unterschiedliche aktuelle Parameter stehen.
Also hier ist es, falls es zwei Ins sind,
ist zum Beispiel sechs, neun sind gültige aktuelle Parameter.
Aber hier kann auch etwas stehen wie Ergebnis und auch wieder
jetzt hier aufpassen, hier wird nicht Ergebnis übergeben,
sondern hier wird die Belegung der Variablen Ergebnisse übergeben.
Also das, was aktuell gerade an dieser Stelle in Ergebnis gespeichert ist.
Und hier wird tatsächlich auch mal was berechnet. Hier sieht man also
einen Aufruf. Da wird an der Aufrufstelle irgendetwas ausgerechnet.
Und das Ergebnis dieses Ausdrucks wird dann als Parameter übergeben.
Und was das dann letztendlich heißt, ist,
dass diese Werte, die hier berechnet werden an der Stelle,
diesen formalen Parametern zugewiesen werden.
Und dann wird der Rumpf ausgeführt. Der Methode. Das heißt,
ihr könnt immer davon ausgehen, Dass die formalen Parameter mit irgendwelchen Werten belegt sind.
Es wird nie so sein, dass die undefiniert sind.
Und Java ist ein Klient immer gezwungen, alle Parameter anzugeben.
Also sind da irgendwelche Ausdrücke gewesen an der Aufrufstelle.
Also kann man in der Prozedur sicher sein,
dass diese Parameter mit irgendetwas belegt sind.
Okay, wie gesagt, das sind die aktuellen Parameter hier.
Dann habe ich schon gesagt, die Anzahl der Parameter muss gleich
der Anzahl der formalen und der aktuellen Parameter gleich sein.
Zumindest in Java ist das relativ strikt.
Es gibt kleine Ausnahmen, die sind aber eher Randthemen
Dann ist es auch immer so,
dass der Name der Aufzurufenden Prozedur einfach festgelegt ist.
Damit legt man fest, welche Putzen da aufgerufen wird.
Die Bindung der jeweiligen Parrameter erfolgt über die Position.
Auch das ist in manchen Sprachen anders. Da kann ich wirklich sagen,
ich habe irgendwie drei Parameter und man kann dann irgendwie durch zusätzliche Schlüsselworte angeben,
so ich möchte als erstes den zweiten Parameter übergeben und so.
Alles sehr kompliziert. Also Ada zum Beispiel ist eine Programmiersprache,
die da ganz raffinierte Möglichkeiten anbietet. Macht das alles
nur komplizierter. Hier ist es bei uns so wirklich,
Anzahl und Reihenfolge ist einfach genauso, wie man es beim Aufrufe sieht,
ist es dann auch beim Aufgerufenen, kommt es dann auch an.
Und was wir auch gelernt haben, ist,
die müssen natürlich typkompatibel sein, wenn eine Prozedur als Parameter einen String erwartet,
kann ich da nicht eine ganze Zahl übergeben.
Passiert, geht einfach nicht so gut. Das sind
alles Sachen, die üblicherweise von einem Compiler überprüft werden.
Also wenn ich jetzt hier mir noch mal dieses Beispiel angucke,
im Quelltext, dann kann ich ja hier jetzt zum Beispiel,
zeichne Dach, wenn ich da jetzt statt Offset irgendwie sage,
hallo, Offset ist ein Int.
Das heißt, was da vorher war, Stand war ein korrekter Ausdruck für einen Intiger-Wert.
Und ich habe jetzt hier einfach mal ins Trinken geschrieben. So,
und jetzt erwarte ich einfach,
dass es noch bei einer vernünftigen Programmiersprache,
wie der Compiler auf die Finger klopft und sagt,
hallo, das geht nicht, zeichne Dach int in Zeichner,
kennen hat be apply to Java Langstring.
Also, er sagt, die Methode bekommt einen Intparameter, ja,
hier int, aber du gibst mir hier gerade einen String.
Klar, das ist ja auch ganz wunderbar, dass mir dann solche Sachen verhindert werden.
Dafür wollen wir genau Typ-Prüfungen haben. Ich kann aber auch nicht da jetzt irgendwie,
selbst wenn ich da jetzt als erstes ein Int angebe,
also hier wieder mein Offset,
Kannst aber auch hier noch einen zweiten Parameter angeben.
Bin ich mal so frei. Ja, mache ich mal
so. Übersetzt das. Und dann sagt er natürlich auch,
zeichne ihn, cannot be apply to end in.
Er sagt, da sind zwei Ins als aktuelle Parameterliste.
Aber ich kann hier nur eine Methode aufrufen, die eine Parante.
Ein Parameter bekommt. Also auch das ist hier festgelegt.
Okay. Dann habe ich auch schon in der letzten Woche
mal so ein bisschen was gezeigt mit Prozeduren.
Eigentlich Methoden haben wir uns angeguckt, die ein Ergebnis liefern können.
Ihr erinnert euch an das Konto
Die habe ich, meine ich, auch hier irgendwo.
Machen wir das mal schnell auf. Aha, meine Girokonten
wieder hier. Juhu, hier ist dieses Projekt mit
den Giro-Content. Und hier habe ich meine Methode gehabt,
die mir von einem Konto den Saldo liefert.
Gib Saldo, ja, bekommt keine Parameter,
ist also hier eine leere Parameterliste, keine formalen Parameter definiert.
Und es kommt als Ergebnis
ein Int raus
An allen Stellen, an denen ich Gibsaldo aufrufe,
kann ich dann tatsächlich mit dem Ergebnis was machen.
Und das ist auch das, was man hier jetzt nochmal sieht.
Hier ist nochmal ein Beispiel, das tatsächlich an den Stellen,
an denen jetzt Maximum aufgerufen wird, das verwendet werden kann.
Ich kann also auch tatsächlich jetzt hier drei plus Maximum von
Ergebnis und zweimal X hinschreiben.
Also das kann selber wieder in einem Ausdruck verwendet werden.
Das ist relativ wichtig, damit man mit diesen ganzen Prozeduren
ganz souverän umgehen kann
Und was auch noch wichtig ist, das ist was,
was wir jetzt die ganze Zeit sozusagen implizit im Kopf hatten.
Wir haben so eine gewisse Vorstellung, dass ja,
wenn ich so eine Prozedur aufrufe, in der Prozedur was
gemacht wird und am Ende kommt es wieder zurück zu der Stelle,
wo ich es aufgerufen habe.
Das ist auch etwas, was ja, man sich explizit klar machen sollte.
Es ist ja auch nochmal ein bisschen angedeutet. Hier ist
mal so ein Quelltextfragment. Eine kleine Abfolge von Anweisungen,
IF-Werte gegeben, was immer das Gerät ist hier,
dann führe das aus.
Volume ist gleich, berechne NR. Els,
Volume ist gleich
So, und am Ende wird irgendwie noch ein Gesamtvolumen berechnet,
was immer was hat, das heißen soll.
Dann ist das so, dass der Kontrollfluss wieder das übliche ist,
so ein roter Faden, der sich da so durchzieht,
jetzt wird hier was ausgeführt.
Jetzt kommen wir an diese Zeile, wo eine Zuweisung steht.
Und bei so einer Zuweisung ist es halt normalerweise so,
dass zuerst die rechte Seite der Zuweisung ausgewertet wird.
Ja, also hier der Prozeduraufruf, denn wenn da
so ein Porzedoraufruf steht und das Programm lässt sich übersetzen,
dann muss es auch irgendwo die passende Definition dazu geben.
Irgendwo wird die stehen. Zum Beispiel wahrscheinlich hier in der
gleichen Klasse. So, und dann ist es einfach so,
dass in diesem Fall hier dieses Berechner aufgerufen wird,
also wird hier
der Rumpf ausgeführt
Und am Ende wird das Ergebnis zurückgegeben und wird dann hier
dem Volume 1 Volumen 1 zugewiesen.
Und dann geht es von da aus weiter,
ist das Els und dann geht der Kontrollfluss vorbei und dann ist das hier das Ende.
Okay, also das ist der Kontrollfluss. Das ist etwas,
was man sich auch nochmal gut verdeutlichen kann in diesem anderen Projekt hier.
Unser Zeichner, wir können den nochmal hier aufmachen.
Ich habe das ja letztes Mal schon gezeigt,
dass man ja auch Haltepunkte setzen kann.
Also ich kann hier jetzt in meinen Zeichnerhäuser Einen Haltepunkt setzen.
Klasse muss übersetzt sein, ja, ist richtig. So,
also ich übersetze das und dann kann ich hier einen Haltepunkt hinsetzen. So,
wenn ich jetzt mir ein Exemplar von diesem Zeichner erzeuge und
das jetzt hier ausführe, dann habe ich hier meinen Debugger,
der jetzt genau an der richtigen Stelle steht, nämlich hier bei Zeichnerhaus.
So, und was ihr bisher gesehen habt, ist,
ich kann jetzt hier Schritt übermachen, dann wird an der Stelle,
wo der Debugger jetzt gerade angehalten hat,
wirklich eine ganze Zeile ausgeführt, eine ganze Anweisung,
mehr oder weniger.
Also wenn ich jetzt hier Schritt überaufrufe,
wird ein Haus
komplett gezeichnet
Und dann müsst ihr hier auch irgendwo das auf der Leinwand erscheinen.
Es wird jetzt nicht so glücklich aus hier.
Puh, puh, puh. Naja, vielleicht mal ein
bisschen hin und her bewegen. Sieht nicht so glücklich aus
, okay. Also ich kann es leider nicht zeigen,
dass das Haus gezeichnet wird. Das ist auch gar nicht
so wichtig. Worauf es mir ankommt, ist,
dass wir jetzt an dieser Stelle hier stehen in der Ausführung.
Und ich kann jetzt aber auch sagen,
ich mache nicht einen Schritt über diese Zeile,
sondern ich mache einen Schritt hinein.
Das heißt, ich gehe jetzt in diesen Prozeduraufruf hinein
Und da kann ich mich halt immer entscheiden. Möchte ich
sozusagen den ganzen Prozeduraufruf überspringen? Oder gehe ich hinein?
Wenn ich das jetzt hier mal mit Schritt hinein mache,
dann sitzen wir tatsächlich jetzt,
sind wir von da oben da runter gesprungen,
hier zu zeichne Wand.
Und wenn ich jetzt wieder Schritt hinein mache,
dann komme ich wirklich hier nach Zeichen eine Wand.
Und jetzt kann ich hier Schritt über wieder mehrere Dinge aufrufen.
Diese ganzen... Bitte? Was für ein Tag, schön
. Bitte? Was, wenn Sie jetzt Schritt hinein?
Wenn ich jetzt Schritt hineindrücke, dann gehe ich hier
zum Beispiel in horizontal Bewegung hinein.
Oh, das liegt ja auch gut.
Dann spricht ja auch eine andere Klasse rein, ja.
Dann würde hier den Quelltext von einer anderen Klasse öffnen
Und würde dann das da anzeigen. Wenn der Quelltext denn
zur Verfügung steht, an der Stelle. Das heißt also,
hier würden wir tatsächlich jetzt dann zum Beispiel den Quelltext von Quadrat sehen.
Will ich jetzt nicht machen. Okay, gehe hier weiter
. Und dann ist irgendwann diese Prozedur zu Ende.
Und dann ist es wieder genau so, dass er dann
wieder zurückspringt an die Stelle, wo er herkam.
Das war da oben. Und da haben wir jetzt zeichne Wand sozusagen abgeschlossen.
Und dann kann ich hier wieder da rüber gehen.
Und dann habe ich diese Prozedur abgeschlossen. Dann habe ich
hier das zweite Haus gezeichnet und wir stehen da.
Das heißt also, wenn jetzt hier mal irgendwas aktualisiert wäre,
würde man das sehen,
funktioniert gerade nicht so
Ich hätte wahrscheinlich C plus plus nicht starten sollen zwischendurch.
Hat ihm den ganzen Speicher geraubt und jetzt ist ja hier,
keine Ahnung. Ist auch nicht weiter wichtig. Ach,
guck mal, da hat jetzt irgendwas gezeigt.
Toll, tolle Effekte hier. Nicht das,
was wir wollen, aber nicht so schlimm. Okay,
also das auch nochmal ganz wichtig, dass man sich das klar macht,
dass dieser Kontrollfluss immer letztendlich an einer bestimmten Stelle ist.
Wir sind immer in irgendeiner Prozedur. Das ist einfach so,
weil wir ja nur von einem roten Strang ausgehen,
von einem Kontrollfaden. Und wir können also immer festlegen,
welches ist gerade die aktuelle... Prozedur beziehungsweise Methode.
So, jetzt habe ich schon immer von und Methoden gesprochen.
Und das, was wir jetzt gerade gesehen haben, also Parameterübergabe,
ein Ergebnis liefern, der Kontrollfluss wechselt an eine andere Stelle.
All das ist universell für Prozedur. Könnt ihr in jeder imperativen Programmiersprache
genauso machen. Was jetzt ein bisschen anders ist bei den Methoden,
die ihr so jetzt programmieren werdet auch, mit Java ist,
dass Methoden zusätzliche Eigenschaften haben.
Erstmal ist es ja so eine Methode, gehört immer zu einer bestimmten Klasse.
Das heißt, wenn eine Methode gerade ausgeführt wird,
dann hat sie immer auch ein Objekt, auf dem sie gerade ausgeführt wird.
Also es gibt immer ein aktuelles Objekt
Es gibt nicht nur eine aktuelle Methode, die gerade ausgeführt wird,
sondern diese Methode gehört zu einer Klasse und deswegen auch zu einem Exemplar der Klasse.
Das heißt also, diese Methoden, die da ausgeführt werden,
die haben immer auch noch Zugriff auf die Felder,
auf die Zustandsfelder, auf die Exemplarvariablen ihrer Klasse.
Das ist sehr ungewöhnlich. Das ist etwas,
was eben halt die Objektorientierung von klassischer imperativer Programmierung unterscheidet.
Außerdem ist es auch noch so,
dass wir bei Methoden ja die Sichtbarkeit unterscheiden,
also private Methoden sind nur innerhalb der eigenen Klasse aufrufbar.
Public-Methoden sind auch für Klientenaufrufbar gehören. Also zur Schnittstelle. Das ist etwas,
was man auch in klassischer, imperativer Programmierung erstmal nicht hat.
Sondern das sind klassische objektorientierte Begriffe. Okay,
was wir jetzt kennengelernt haben, in den bisherigen Vorlesungen,
sind drei verschiedene Arten von Variablen.
Ihr habt kennengelernt auf dem zweiten Aufgabenblatt sowas wie lokale Variablen.
In der Klasse Zeichner, diese Methode Zeichen,
da gab es dann diese lokalen Variablen Wand und Dach und so weiter,
das waren Variablen, die nur innerhalb einer Prozedur einer Methode definiert waren.
Dann habt ihr auch schon Exemplarvariablen, zumindest mal gesehen,
nämlich bei dem Kontobeispiel, was wir hier schon ein paar Mal diskutiert haben.
Und formale Parameter habt ihr jetzt auch schon gesehen,
formale Parameter kann man wirklich als Variablen ansehen.
Tatsächlich in dem Sinne sogar, dass ihr in Java
einem formalen Parameter einen anderen Wert zuweisen könnt.
Wenn eine Prozedur aufgerufen wird, dann haben die formalen Parameter
eine Belegung. Das ist einfach festgelegt,
weil ja der Aufruf
irgendwas angeben musste
Aber ihr könnt das einfach dem was Neues zuweisen.
Ihr könnt das also ignorieren, was euch da als Parameter übergeben wird.
Ihr könnt einfach den formalen Parameter einen neuen Wert zuweisen,
wenn ihr das denn wollt.
Ihr könnt die also praktisch wie eine lokale Variable verwenden.
Man kann das verhindern in der Programmierung,
aber das ist erst mal die Voreinstellung, dass das geht.
So, das ist eben halt genau das,
was wir hier jetzt nochmal sehen.
An ein Beispiel dieses Girokontos, eine Exemplarvariable.
Das ist hier der Saldo. Hier ein formaler Parameter.
Und hier lokale Variablen. Und Zuweisung und Variablen werden wir
uns dann in der nächsten Woche nochmal ein bisschen intensiver ansehen
Was so da die Unterschiede sind, wie die und wann die initialisiert werden.
Das kommt dann aber erst nächste Woche. Okay, das heißt,
was wir jetzt hier in der ersten Hälfte erst mal gemacht haben,
wir haben uns angesehen, dass Aufrufe von Methoden,
wie ihr sie in Java auch kennt,
weitgehend imperativen Prozeduraufrufen entsprechen, Prozeduren können paramaritisiert werden.
Der Aufrufer gibt aktuelle Parameter an. Und die
gerufene Methode bekommt formale Parameter. Java kennt nur Wertparameter,
ganz wichtig. Also es werden letztendlich Werte da kopiert.
Die gerufene Methode arbeitet immer nur auf Kopien
Und Ergebnisprozeduren, das ist im Skript noch ein bisschen falsch,
da steht irgendwie noch Funktionsprozedur.
Den Begriff haben wir inzwischen rausgeschmissen. Also Ergebnisprozeduren liefern ein Ergebnis,
das an der Aufrufstelle wieder direkt in einem Ausdruck auch verwendet werden kann.
Und treuflos wechselt in die gerufenen Prozedur und kommt dann irgendwann wieder.
Und wir haben auch gesehen, Methoden sind ein etwas reicheres Konzept als Prozeduren.
Also eine Methode ist immer einem Objekt zugeordnet und eine Methode
hat immer auch Zugriff auf die Felder eines Objektes.
So, gibt es dazu jetzt noch Fragen zu Prozeduren,
zu Parameterübergabe, Kontrollflusswechsel oder ist das einigermaßen eingängig klar geworden.
Da gibt es anscheinend keine Fragen. Dann machen wir fünf Minuten Pause.
Hey, also, bitte langsam das Reden einstellen.
Das werden wir lieb. Tatsächlich ist es für mich fast egal,
weil ich hier vorne relativ wenig mitkriege von dem, was ihr redet.
Aber meistens ist es für die Kommilitonen etwas störend,
wenn andere Leute laut reden. Weil es gibt Leute,
die wollen zuhören. Und für die ist es dann oft
lästig, wenn dann zu laut geredet wird.
Also von daher bitte mal ein bisschen das Reden einstellen. Okay,
also in der zweiten Hälfte geht es jetzt hier um ein,
ja, etwas anderes Thema. Ich bin eben schon gefragt worden,
na, wir haben in der letzten Vorlesung,
haben wir ja irgendwie ganz viele Folien ausgelassen.
Ich habe mir die Freiheit genommen, mal so ein paar Reihenfolgen zu ändern.
Das heißt, wenn wir heute mit diesen Folien durch sind,
dann sind wir wieder sozusagen in der Spur und danach
wird dann wieder genau wie im Skript vorgegangen.
Also das war jetzt hier ein bisschen notwendig,
weil wir sonst in der vierten Vorlesung unglaublich viel Zeug auf
einmal gehabt hätten
Ich glaube, das war jetzt alles noch bewältigbar, was
wir gemacht haben. So, also diese Folien hier,
dieses Thema Jetzt hier, beschäftigt sich damit,
was passiert da eigentlich, wenn wir solche Programme schreiben?
Dass solche Programme selber Texte sind und dass die eine Struktur haben.
Das ist sozusagen eine Ebene höher für uns auch immer interessant.
Also, wenn ihr eine Klassendefinition schreibt in Java,
hier sowas hier, dann ist das Text.
Einfach Text in einem Editor. Also letztendlich kann man sagen,
Das fängt hier an und geht dann hier so zeichenweise komplett
durch die ganzen Texte durch, bis dann hier ans Ende.
Das ist eine lange Folge von Zeichen. Einfach eine lange
Folge verzeichnen und irgendwie verstehen wir das. Wir können es lesen,
weil es Java ist, wenn wir Java gelernt haben.
Aber das ist natürlich nur eine textuelle Darstellung und irgendwie muss
das dann umgewandelt werden in etwas, was ein Computer ausführen kann.
Ich habe eben schon gesagt, in der Verneumann-Architektur ist es so,
dass die Befehle und auch die Daten üblicherweise binär kodiert vorliegen,
Und das Format, diese Klassendefinition, diese textuelle Ausführung,
ist kein Binärformat, sondern das ist textuelles Format.
Also irgendwie muss dieses textuelle Format ja umgewandelt werden und
das nennen wir eben halt Übersetzen oder Komplinen.
Das macht einen Compiler. Und wenn dann so eine Klassendefinition
komplett korrekt übersetzt ist, dann haben wir tatsächlich eine Klasse.
Und mit so einer Klasse kann ich dann tatsächlich Exemplare erzeugen.
Also wirklich hier live, sowas machen wir hier,
ein Exemplar auf. Und das könnte ich nicht machen.
Wenn ich diese Klasse jetzt hier irgendwie bearbeite, dann schließe,
dann ist das hier gestreift. Dann ist es tatsächlich so,
dass diese Klasse jetzt hier mir nicht ermöglicht, ein Exemplar zu erzeugen.
Okay, also das wollen wir uns mal ein bisschen näher ansehen.
Zum einen wollen wir uns mal ansehen,
wie das so überhaupt funktioniert in verschiedenen Programmiersprachen.
Was wir uns jetzt hier ansehen, ist Java.
Aber es gibt andere Programmiersprachen. Es gibt Sprachen,
zum Beispiel wie CPlus oder auch Modular 2
oder es gibt noch eine ganze Menge mehr davon.
Da ist es üblicherweise so, man schreibt ein Programm in einem textuellen Form.
Und diese Anweisungen werden dann einmalig in Maschinensprache übersetzt.
Das heißt, es gibt also irgendwie einen Übersetzungsprozess und
dann kommt als Ergebnis etwas raus, Binärcode ist.
Oder Objektcode. Und üblicherweise ist dieser Binärcode dann tatsächlich Binärcode
für einen bestimmten Prozessor. Also früher war das so,
als man noch so diese Welt hatte von Windows und Macintosh.
Da gab es auf den Windows-Rechnern, gab es Intel-Prozessoren,
auf den Macintosh gab es dann irgendwie sowas wie Power PC.
Das war damals ein Chip, der einen Prozessor,
der in Max verbaut war. Und da war es tatsächlich so,
da musste man einfach verschiedene Compiler haben für dieselbe Sprache,
für dieselbe Sprache, verschiedene Compiler haben, weil die Maschinencodes einfach unterschiedlich waren.
Und das ist dann tatsächlich auch normalerweise so,
dass so eine Maschinensprache nur auf einer Prozessor-Architektur ausgeführt werden kann.
Also die Maschinensprache ist immer sehr spezifisch, führt zum Beispiel einen Intel-Prozessor.
Inzwischen ist das ja, dass Apple so schlau war,
auf Intel-Prozessoren zu wechseln. Das heißt, das ist jetzt
tatsächlich die gleiche Architektur, gleiche Prozessor.
Aber man hat natürlich immer noch ganz unterschiedliche Prozessoren,
also auch in so einem Handy, was ihr so habt.
Da ist natürlich auch ein Prozessor drin,
der ist wieder anders als der Intel-Prozessor, der in eurem Laptop drinsteckt oder ähnliches.
Das ist also ein ganz wichtiger Punkt,
dass dann das üblicherweise in Maschinensprache abgelegt wird,
die nur auf dem Prozessor läuft,
für den sie übersetzt wurde
Das aber dann auch üblicherweise sehr schnell funktioniert. Dann gibt es
sowas wie Interpretersprachen. Da ist es dann eher so,
jede Anweisung im Programm wird immer wieder neu übersetzt.
Kommt gesagt, da gibt es auch wieder Optimierungsmöglichkeiten,
aber letztendlich ist es wirklich so, da wird jede Zeile für sich genommen interpretiert.
Und sozusagen dann direkt on the fly übersetzt.
Also sie wird sozusagen verarbeitet, übersetzt und dann ausgeführt.
Und das ist bei so Sprachen wie Lisp so oder Skriptsprachen,
PHP, Pearl und so weiter.
Und dann gibt es hybride Sprachen. Und da gehört Java
dazu, auch Cishab gehört dazu
Da werden die Programme in eine Zwischensprache übersetzt.
Und die ist dann gut für die Interpretation geeignet.
Das heißt, bei Java ist es tatsächlich so,
eure Punkt Java-Dateien. Werden wir uns gleich mal ankommen,
dass das tatsächlich so Punkt Java-Dateien sind, wo die Texte drinstehen.
Also Klartext, wie ihr ihn auch gerade hier vor euch sehen könnt.
Die werden übersetzt in etwas, was in Java Bitecode heißt.
Okay, also dieser Quelltext wird von einem Compiler inzwischen
gehaut transformiert, der Java Bitecode heißt.
Wir können uns das mal angucken im Dateisystem hier,
indem wir uns zum Beispiel hier jetzt mal Bei Girokonten,
das Ansehen. Da sieht man hier verschiedene Dateien.
Und ich kann tatsächlich im Dateisystem mir das direkt angucken.
Das, was wir uns in BlueJ ansehen können,
können wir uns auch hier direkt ansehen.
Ich kann das hier einfach mit einem Doppelklick,
mir einen Editor anzeigen lassen, der einfach nur hier die Klasse anzeigt.
Man sieht hier kein Syntax-Highlighting, keine Blog-Hervorhebung,
also alles das, was der BlueJ Editor bietet,
haben wir in einem normalen Editor einfach nicht.
Der zeigt das hier einfach als Plain, als flachen Text an.
So, und dann haben wir aber auch hier noch Girokonto.klaas.
Also Java ist der Klartext, das,
was man so als Mensch lesen kann.
Und es wurde übersetzt von einem Compiler in eine andere Darstellung und diesen Javad-Bike-Code.
Und dieser Javad-Bike-Code ist in so einer Klaas-Datei drin und
das kann man sich auch nicht mehr gut angucken.
Das ist quasi so eine Art Binär-Format. Also wenn man
da jetzt irgendwie sich das anzeigen lassen würde,
dann würde das wahrscheinlich nicht gut funktionieren.
Das sieht ziemlich komisch aus. Das ist aber kein Maschinencode
, das muss man sich klarmachen. Maschinencode wäre nur für
einen bestimmten Prozessor ausführbar. In Java ist es so,
Dass das, was da rauskommt, tatsächlich eine Klaas-Datei ist
und die kann auf jedem Rechner ausgeführt werden, der einen Java-Interpreter hat.
Das heißt, man braucht nur so ein bestimmtes Programm,
das in der Lage ist, diese Klaas-Dateien zu lesen und auszuführen.
Und nur dieses kleine Programm muss man schreiben,
schon kann man jedes Java-Programm,
das man irgendwo auf einem Rechner übersetzt hat,
kann man dann auf beliebige andere Rechner einfach rüberschieben.
Tatsächlich einfach eins zu eins kopierbar. Das heißt also,
diese Bitecode ist sehr portabel. Das ist also etwas,
was komplett Rechner,
Architektur unabhängig ist
Das ist also ein Format wie Java-Befehle in einem Zwischenformat
abgelegt sind, das dann überall hinbewegt werden kann.
Das ist ziemlich schön, weil dadurch Java etwas ermöglicht,
was die Marketingleute damals genannt haben, Right Once, Run Anywhere.
Also schreib ein Programm,
du kannst es auch übersetzen und das Übersetzte kannst du auf jeder Plattform,
auf jedem Dings laufen lassen, wo zumindest dieser Interpreter da ist,
der dann diesen Byte-Code interpretieren kann.
Also hier so in alten Tagen Windows 32, Solaris,
MacOS, Ein Programm, und es ist einfach immer das Gleiche tatsächlich.
Das ist ziemlich cool. Bei C-Sharp ist es genau andersrum.
Bei C-Sharp ist es so, da gibt es auch eine Zwischensprache.
Also C-Sharp wird übersetzt in eine Zwischensprache.
Aber diese Zwischensprache ist die gleiche Zwischensprache für verschiedene Sprachen.
Das ist aber nur gebaut für die Windows-Plattform.
Also diese Zwischensprache funktioniert nur üblicherweise. Es gibt natürlich
auch Opportierungen, aber auf Windows-Systemen. Und dann kann man
aber verschiedene Sprachen in diese Zwischensprache übersetzen.
Das heißt, es gibt irgendwie einen Übersetzer von C-Sharp,
von C-Plus
und so weiter
Von Eifel, von verschiedenen Programmiersprachen, in diesen Zwischencode von Sishab.
Das ist dann letztendlich die Donette-Plattform.
Und da ist es dann wieder viele Sprachen eine Plattform.
Wenn es bei Java ist, eine Sprache, viele Plattformen.
Aber das Prinzip ist das Gleiche. Und der Schlüssel zu
dem Ganzen sind halt sogenannte virtuelle Maschinen. Das heißt,
das Programm, was in der Lage ist, so ein Bitecode,
ein Java-Butt-Code auszuführen, wird auch virtuelle Maschine genannt.
Virtual Machine. Und letztendlich ist es so,
im Kern hat man immer eine reale Maschine,
aber um diese reale Maschine drumherum ist dann so ein eine Hülle gelegt,
die dann die virtuelle Maschine ist.
Und die hat einen Befehlssatz. Also jeder Prozessor hat seinen
eigenen Befehlssatz und verschiedene Prozessortypen unterscheiden sich in ihren Befehlssätzen.
Aber da ist dann halt so eine Hülle rumgelegt.
Und die virtuelle Maschine hat halt für Java immer den gleichen Befehlssatz.
Und den kann man dann einfach auch verwenden. Das heißt,
hier ist es auch nochmal so ein bisschen dargestellt,
Hardware-based Plattform, da drauf sitzt dann die Java Virtual Machine,
die kapselt sozusagen die Hardware weg.
Dann gibt es hier die API und so Java-Programme setzen letztendlich
auf diese Plattform auf. Das ist, wie gesagt,
einer der tollen Eigenschaften von Java, dass man tatsächlich Programme Ja,
einmal übersetzen kann und dann können sie überall ausgeführt werden.
Das wird wieder so ein bisschen verwässert inzwischen durch doch
so Plattformabhängige Sachen. Es gibt so inzwischen so Frameworks,
die zum Beispiel grafische Oberflächen anbieten zur Programmierung von grafischen Oberflächen.
Und die sind dann teilweise wieder plattformabhängig. Aber die Idee
ist eigentlich mal sehr gut gewesen bei Java. Okay,
das erst mal so als Hintergrund so ein bisschen,
wie das in Java funktioniert und wie ist es eben halt
auch im Dateisystem sich darstellt, nur noch mal vielleicht zur Ergänzung.
Diese Girokonto.ctXT ist eine Blue Jay-Datei. Darin hält in Form
Blue Jay die Informationen Zum Beispiel hier in diesem Girokonto,
wo liegt das Ding jetzt hier? Also wenn ich das
jetzt hier hinschiebe oder da hinschiebe oder wie groß ist das?
Also ich kann es ja hier auch irgendwie ein bisschen größer
machen, also ein Kram. Das sind so Informationen,
die legt BlueJ dann in diesen sogenannten Kontextdateien ab.
Also es ist sozusagen nur für BlueJ wichtig,
wenn ihr also so ein Java-Projekt mal kopieren wollt,
braucht ihr diese BlueJ-Dateien nicht mit zu kopieren.
Das Readme hier ist tatsächlich genau das Readme,
was auch hier das hier ist.
Also hier, ja, da steht also irgendwas drin.
Und wenn ich das jetzt hier,
kann ich genauso auch dieses Readme nehmen
Ja. Das ist also tatsächlich das Gleiche. Und diese Bluejay-Bit, das Package Bluejay,
das hat auch nochmal Informationen über das ganze Projekt gespeichert.
Auch wieder so Layout-Sachen und ähnliches. Gut, also das
nur, dass ihr es nochmal gesehen habt. So,
und jetzt werden wir die letzte knapp halbe Stunde nochmal uns Syntax angucken.
Syntax von Programmiersparen. Das ist jetzt so ein Thema,
das ist immer sehr beliebt bei den Studenten der letzten Jahre gewesen,
weil es, ja, es hat halt wenig mit konkreter Programmierung zu tun,
sondern wir begegnen uns jetzt auf einer Ebene und gucken einfach mal auf Programmiersprachen drauf und überlegen uns,
ja, was tun wir da eigentlich,
wenn wir die irgendwie beschreibbar machen wollen.
Das Programmiersprach, eine formale Syntaxampe habt ihr schon ganz oft gemerkt,
wenn ihr nämlich irgendwas vergesst,
ein Semikolon oder ein Leerzeichen an der falschen Stelle hinschreibt,
dann meckert der Compiler sofort.
Das liegt daran, dass natürlich die Syntax von einer Programmiersprache
sehr formal festgelegt ist und der Compiler auch relativ unerbittlich ist,
dass das auch eingehalten wird. Das muss so sein,
damit der Computer dann auch wirklich eindeutig das Programm abbilden kann
auf einen Binärcode, der dann ausgeführt werden kann.
Aber es ist auch für uns ganz nützlich für uns als
Menschen, dass die eine syntaktische Struktur haben.
Also eine formale, syntaktische Struktur, weil die können wir
relativ leicht lernen. Ihr wisst vermutlich aus eigener Erfahrung,
dass es nicht so leicht ist, eine natürliche Sprache zu lernen.
Da gibt es irgendwie sehr viel Dinge, die man dazu aufbringen muss.
Man muss vulkabeln, beherrschen, man muss auf Grammatikregelregeln beherrschen.
Und bei Programmiersprachen ist das alles ein bisschen, sagen wir,
klarer umrissen. Und das liegt auch unter anderem daran,
dass wir die Syntax eben halt mit formalen Mechanismen beschreiben können,
die sehr, sehr nützlich sind.
Und wir werden uns jetzt mal zwei Darstellungsformen für die Syntax von Programmiersprachen angucken,
nämlich einmal die erweiterte Baccos-Nauerform und dann auch vielleicht noch ein bisschen Syntaxdiagramme.
Okay, war zuerst mal noch mal so grundlegende Begriffe,
die wir an dieser Stelle dann ganz gern mal einführen.
Also Syntax steht auch im Informatik-Duden, das ist letztendlich,
das sind die Regeln, die festlegen, wie eine Sprache,
wie die Wörter einer Sprache aufgebaut sein müssen.
Also formaler Aufbau der Sätze und Wörter,
die zur Sprache gehören, bezeichnet man als ihre Syntax.
Also es gibt üblicherweise feste Syntax-Regeln und ihr kennt auch
Syntax-Regeln aus natürlichen Sprachen
Kommen wir gleich noch mal drauf. Semantik, das ist das,
was letztendlich die Bedeutung des Programms ausmacht. Also wenn man fragt,
was ist die Semantik dieses Programms, dann letztendlich genau das,
was das Programm tut, wenn es ausgeführt wird.
Also was ist zum einen im Rechner bewirkt und natürlich auch
darüber hinaus. Also wenn ein Rechner, ein Programm,
dann irgendwelche Sensoren abfragt oder irgendwelche Aktuatoren ansteuert oder einen Roboter
steuert oder so ähnliches, das ist dann letztendlich das,
was wirklich passiert.
Also Semantik ist ein Begriff aus der letztendlichsten Syntax-Semantik und Pragmatikbegriffe
aus der Semiotik
Das ist eine wissenschaftliche Disziplin, die sich mit der Verarbeitung von Zeichen beschäftigt.
Also die Semantik ist die Lehre von der inhaltlichen Bedeutung.
Also die Semantik eines deutschen Satzes ist was anderes als seine Syntax.
Werden wir gleich uns mal genauer ansehen. Und dann gibt es
noch die Pragmatik. Und das ist auch nochmal etwas,
was auch ein Begriff aus der Simiotik, also das ist
die Lehre vom Gebrauch einer Sprache in einem bestimmten Zusammenhang.
Und dass sich die Pragmatik von der Semantik teilweise unterscheiden kann,
können wir mal uns an einem Beispiel ansehen.
Also Syntax einer Sprache gibt es üblicherweise Grammatikregeln.
Also ein deutscher Satz, Kann man zum Beispiel sagen,
ein deutscher Satz besteht aus dem Subjekt, ein Prädikat an einem Objekt.
Und mitten endet mit einem Punkt. So,
jetzt mal so eine ganz einfache Syntax-Regel für deutsche Sätze.
Dann ist dieser Satz hier, der Mann beißt den Hund,
ein so ein taktisch korrekter Satz. Ob das semantisch sinnvoll ist,
also ob das wirklich,
ob der Sinn dieser Aussage sinnvoll ist, hängt davon ab,
ob man Literatur mag und vielleicht mal gab und wie er die Welt hat,
gab und wie er die Welt sieht, gelesen hat.
Da ist tatsächlich so, da beißt ein Mann einen Hund.
Normalerweise ist es umgekehrt, normalerweise beißt ein Hund
den Mann
Wenn man das jetzt mal sozusagen komplett ad absurdum führen will,
dann hat man hier das Haus streichelt den Mann.
Das ist syntaktisch korrekt. Man hat ein Subjekt,
man hat ein Predikat, man hat ein Objekt,
steht ein Punkt da, wunderbar.
Die Bedeutung ist aber schon ziemlich komisch. Also was soll das heißen?
Das Haus streichelt den Mann. Wow, da muss man
schon sehr viel geraucht haben, um das irgendwie nachzuvollziehen.
Und also da einfach, dass der Unterschied,
das eine ist einfach die Form, wie es zusammengesetzt ist,
das andere ist die Semantik, die Bedeutung dieses Satzes.
So, und wenn wir jetzt nochmal uns den Unterschied zwischen Semantik und Pragmatik angucken wollen,
dann ist es hier mit diesem Satz ganz schön zu zeigen.
Also dieser Satz steht hier erstmal so da,
da ist die Tür
Das ist auch ein deutscher Satz. Sicherlich ein korrekter
deutscher Satz. Jetzt kann man den sehr unterschiedlich lesen.
Ich kann einfach sagen, wenn mich jemand fragt,
sagen, wie komme ich denn hier aus dem Hörsäule raus?
Dann sage ich, da ist die Tür. Oder wenn
mich jemand hier tierisch aufregt und ich irgendwie schon die Galle kriege,
dann sage ich, da ist die Tür.
Dann meine ich raus mit dir. Das ist dann,
die Semantik ist die gleiche, da ist die Tür,
die Aussage des Satzes, aber die Pragmatik,
also wie es verstanden wird, ist dann noch wieder eine etwas andere Ebene.
Also Pragmatik ist tatsächlich eher so auch sehr kontextabhängig.
Semantik sollte idealerweise nicht kontextabhängig sein. Semantik sollte eigentlich klar
aus dem Text hervorgehen. Auch das ist bei einer natürlichen Sprache
überhaupt fast nie so. Also das ist so eines der
harten Probleme für die Informatiker, dass wir,
die sich mit Sprachverarbeitung beschäftigen, dass normale Sätze in natürlicher Sprache,
in ihrer Semantik selten eindeutig bestimmbar sind.
Und wie gesagt, die Pragmatik spielt ja immer auch immer eine sehr wichtige Rolle.
Okay, Benutzer, Handbücher geben oft Hinweise zur Pragmatik.
Also wie macht man denn etwas? Also zum einen steht im Benutzerhandbuch drin,
was bestimmte Funktionen bewirken, aber auch macht man das denn?
Das ist dann eher so was, was Pragmatik ist oder auch Quelltextkonventionen,
die wir jetzt diese Woche einführen in den Übungen.
So schreiben wir unsere Quelltexte hin. Das ist nichts,
was irgendwie Semantik definiert. Also ob da jetzt irgendwie alles auf
einer Zeile steht oder man jede Anweisung schön auf eine eigene Zeile schreibt,
ist für die Semantik eines Java-Programms völlig egal.
Das ist reine Pragmatik. Okay, also hier auch nochmal so an
so einem Beispiel. Das hier ist unsere klasse Girokonto.
Dann kann man sich hier das mal so vorstellen,
dass es die Syntax für Klassen in Java, mal ganz übervereinfacht dargestellt.
Man kann also schreiben, Klassenkopf, Klaas, Klassenname,
das ist hier so ein Platzhalter.
Dann hat man hier irgendwie geschweifte Klammern und dann optional Felder,
optionale Konstrukturen, optionalen Methoden.
Tatsächlich, es schreibt die Syntax von Java überhaupt nicht vor,
dass die in dieser Reihenfolge da stehen müssen.
Die Semantik von Java definiert zum Beispiel,
das haben wir ja auch schon letzte Woche gesehen,
wenn ich das hier so hinschreibe, dann ist für diese Klasse automatisch ein Konstrukter definiert.
Der parameterlose Konstruktor ist dann definitiv, ich kann also von
dieser Klasse Exemplare erzeugen. Das ist also tatsächlich einfach eine
Semantikregel von Java für ein solches Java-Programm
Obwohl ich keinen Konstrukt da hinschreibe, ist er da.
Das ist ein Beispiel für einen Semantik-Regel. Und die Reihenfolge
hier, wie gesagt, ist halt komplett nicht festgelegt.
Wir können rein von den Syntax-Regeln, von Java können wir
wild durcheinander erst Konstruktoren, dann Felder, dann wieder Methoden definieren.
Also das ist überhaupt nicht festgelegt. Das ist eine reine Konvention,
dass wir dann in den Quelltext-Konventionen sagen, bitte zuerst immer
die Felder, dann die Konstruktoren, dann die Methoden.
Okay. So, und jetzt Ist es halt tatsächlich so,
wenn wir korrekte Java-Programme schreiben wollen, müssen wir die Sympath-Regeln kennen.
Und wenn ihr euch mal versucht anzugucken, wie die Sympath-Regeln
von Java aussehen, dann ist das nicht so ganz einfach.
Und da werden wir jetzt mal ein bisschen drauf kommen,
warum das nicht so einfach ist.
Also ähnlich wie bei natürlichen Sprachen haben wir dann sowas wie Grammatik-Regeln.
Und die sind tatsächlich existent für Java. Und diese formalen Grammatiken,
mit denen wir diese Programmiersprachen beschreiben, die bestehen aus Regeln,
so Bildungsgesetze, mit denen können dann Wörter aus einem Staatssymbol abgeleitet werden.
Also es gibt immer so ein Staatssymbol, sowas wie Java-Programm?
Ist das Staatssymbol? Und dann kann man irgendwie aus
diesem Staatssymbol mit diesen Regeln dann ganze Java-Programme ableiten.
Da gibt es einen theoretischen Hintergrund dazu. Das sind
die kontextfreien Grammatiken aus der Trumpsky-Hierarchie. Das ist nichts,
was wir uns jetzt sehr viel näher ansehen werden.
Das ist was für formale Grundlagen der Informatik.
Da werdet ihr mit kontextfreien Grammatiken in Kontakt kommen.
Wir sind hier sehr pragmatisch. Wir wollen euch die Möglichkeit geben,
dass ihr die Syntaxbeschreibung von Programmiersprachen lesen könnt und verstehen könnt.
Und die ist üblicherweise heutzutage in der Bakus-Nauer-Form angegeben
In irgendeiner Form, die der Bakus-Nauer-Form zumindest gleicht.
Die ist also sehr hilfreich und deswegen werden wir uns die mal ein bisschen näher ansehen.
Diese Bakus-Nauer-Form ist mal entwickelt worden zu Zeiten von Allgäu.
Also Allgäu ist auch eine Programmiersprache, die in den 50er Jahren entstanden ist.
Und die haben also diese formale Beschreibung der Syntax,
hat dann John Bakus vorgenommen,
dann hat der gute Peter Nauer das noch modifiziert.
Und das wurde auch zur Beschreibung der Syntax von Allgäu
60 verwendet. Das heißt, dieser Name Bakus-Nauer-Form ist an
ihren beiden Vätern geschuldet
Und deswegen heißt das dann kurz BNF. Und das ist,
wie gesagt, die häufigste Form der Syntaxbeschreibung von Programmiersprachen.
Ganz lustig ist, dass es schon immer Menschen irgendwie sich bemüht haben,
Regeln über ihre eigene Sprache irgendwie zu formalisieren und festzuhalten,
schriftlich festzuhalten.
Also sowas wie Sanskrit hat auch solche Elemente, genau wie die BNF.
Okay, also jetzt wird es ein bisschen technisch.
Müssen wir jetzt einmal kurz durch. Also die BNF definiert
syntaktische Strukturen mit Hilfe Zweierelement, also zwei Kernelemente.
Einmal gibt es Nicht-Terminale und einmal
gibt es Terminale
Nicht-Terminale sind sowas wie so taktische Variablen. Und Terminale sind so Basiselemente.
Die sind sozusagen unteilbar. Nicht-Terminale werden oft in Spitzenklamonotie.
Also hier ist zum Beispiel jetzt für eine Programmiersprache bezogen.
Hier ist ein Nicht-Terminal Zuweisung. So wie eine Zuweisung
aussehen kann, ist ja sehr unterschiedlich. Es können ja
ganz unterschiedliche Zuweisungen kann man ja sich vorstellen in so Programmiersprachen.
Und hier ist jetzt dann eine Definition gegeben für eine Zuweisung.
Die sieht dann sau aus. Hier steht also ein Ableitungssymbol
. Also wenn ich eine Zuweisung irgendwie ableiten will,
dann kann das folgendermaßen aussehen.
Ich schreibe hin,
einen Bezeichner
Dann ein Gleichheitszeichen und dann ein Ausdruck. Das ist auch das,
was wir schon so von unserem Verständnis fahren, Zuweisung ja kennen.
Also sie besteht immer irgendwie aus einer linken Seite,
ein Bezeichner, dann Gleichheitszeichen und ein Ausdruck.
So, das sind jetzt selber wieder Nicht-Terminale. Das heißt,
der Ausdruck kann jetzt auch wieder ganz vielfältig sein.
Es gibt ganz unterschiedliche Arten und Weisen wie ein Ausdruck,
da kann 3 plus 5 stehen, da kann einfach nur I stehen,
I ist auch ein Ausdruck, also ganz verschiedene Bildungsgesetze.
Und ein Bezeichner kann halt auch sehr unterschiedlich aussehen. So,
in der BNF ist es immer so,
dass so eine Regel so aussieht, auf der linken Seite steht ein Nicht-Terminal,
Und auf der rechten Seite stehen dann beliebig viele andere Nicht-Terminale oder terminale Symbole.
Das hier ist ein terminales Symbol. Und das ist nicht weiter unterteilbar.
Das ist wirklich, wird man genauso hinschreiben. So,
ja, und dann ist es eben halt auch so,
dass es zu jedem Nicht-Terminal mindestens eine Regel geben muss,
damit eben halt man aus jedem Nicht-Terminal irgendwie dann auf etwas kommen kann,
was nur noch aus Terminalsymbolen besteht.
Also diese Terminale sind oft reservierte Wörter in Programmiersprachen. Also sowas wie,
wie das hier in Java mir mal angucke,
also sowas hier, ja, dann haben wir hier Private,
ist zum Beispiel
ein reserviertes Wort
Oder Public ist ein reserviertes Wort. Das ist ein aus
Sicht einer Programmiersprache. Ist das ein Terminal? Ein
reserviertes Wort. Das schreibt man direkt genau so hin.
Bezeichner sind auch so ein bisschen so was Elementares, Literale.
Also was ich vorhin angeschrieben habe, so ein doppelt in Anführungsstrichen
eine Zeichenkette, ist ein Literal. Und die ganzen Sonderzeichen,
also irgendwie größer gleich, kleiner Zuweisungssymbol und so weiter.
Und die werden halt üblicherweise in einfachen Anführungsstrichen angenotiert hier.
Und die sind halt unteilbar. Gut. Und jetzt ganz normal,
ich habe einen bei einer Bakus-Nauer-Form,
bei einer Grammatik habe ich ein Startsymbol,
Und von da ableite ich, dann kann ich alle Sätze ableiten,
die man mit dieser Sprache beschreiben kann.
Also hier zum Beispiel Zuweisung, bezeichne gleich Ausdruck.
Dann kann ich tatsächlich auch mehrere Regeln haben für ein Nicht-Terminal.
Also hier ist zum Beispiel eine Zahl, kann entweder sein Integer oder ein Reel.
Und diese Nicht-Terminale können auch rekursiv definiert sein. Also ich kann sagen,
eine Anweisungsfolge ist entweder eine einzelne Anweisung oder also diesen senkrechten Strich
liest man so als einen oder oder eine Anweisungsfolge kann sein,
eine Anweisung mit einem Silikon dann dahinter und dann eine Anweisungsfolge.
Und jetzt kann man wieder sehen, aha, hier Anweisungsfolge.
Naja, das kann man wieder,
dann nimmt man wieder die gleiche Regel her und macht das
vielleicht wieder
so weiter
Dass man irgendwie dann so eine Kette mal gebildet hat.
Nehmen wir mal so ein Beispiel. Also hier eine Syntaxbeschreibung
für Zuweisungen. Hier, also das Staatssymbol ist Zuweisung,
ist definiert, muss ich ableiten, Bezeichnerausdruck.
Und dann gibt es eine Regel für Bezeichner.
Und es gibt Regeln für Ausdruck. Also für jedes Nicht-Terminal
musste ich mindestens eine Regel haben und hier stehen die also
immer auf der linken Seite, die Nicht-Terminale wieder definiert werden.
So, jetzt kann ich schrittweise diese Nicht-Terminale ersetzen, indem ich irgendwelche Definitionen hierher nehme.
Und ein Beispiel für die Ableitung eines Wortes ist hier zum Beispiel,
Ich habe jetzt hier der erste Schritt.
Ich habe nur eine Regel für eine Zuweisung.
Also muss ich hier die rechte Seite einfach hinhernehmen. Also bezeichne gleich Ausdruck. So,
dann setze ich jetzt hier bezeichne, sage ich mal einfach mal,
ich kann hier bezeichnen, was habe ich für Möglichkeiten,
es kann wieder ein A sein oder ein B oder ein C. Na gut,
dann sage ich hier mal an der Stelle A. Dann habe ich hier einen Ausdruck,
was kann ein Ausdruck sein?
Jetzt haben wir hier verschiedene Regeln. Also es kann sein,
Bezeichner plus Ausdruck oder Bezeichnermalausdruck oder es kann ein geklammerter Ausdruck
sein oder einfach nur ein Bezeichner.
Gut, dann nehmen wir mal die Was nehmen wir hier zuerst mal die zweite Regel?
Ja, dann haben wir jetzt wieder bezeichnete Ausdruck.
Okay, jetzt ersetzen wir wieder irgendwie die Bezeichnung durch B
, haben immer noch einen Ausdruck.
Was nehmen wir jetzt als Ersetzung? Jetzt machen wir da
mal einen geklammerten Ausdruck draus. Hier wird jetzt der Ausdruck
wieder als Nicht-Terminal ersetzt nach einer Regel mit dem Plus.
Und dann haben wir hier den Bezeichner durch A ersetzt und
hier den Ausdruck vielleicht durch einen weiteren Bezeichner.
Und dann haben wir irgendwie sowas da stehen. Das heißt,
das hier, das da oben definiert, eine Menge von gültigen Zuweisungen.
Und hier haben wir ein Beispiel, Einer Zuweisung,
die mit diesen Regeln gebildet werden kann.
Jetzt ist immer die Frage, was will man machen?
Will man jetzt eigentlich solche Dinge erzeugen oder will man eigentlich umgekehrt,
was ein Compiler ja macht, da hat jemand sowas hingeschrieben.
Und ich muss jetzt erkennen, ob es tatsächlich zu meiner formalen Grammatik gehört.
Das sind also zwei verschiedene Richtungen, die man draufgucken kann.
Das eine ist so generativ, man erzeugt was oder das andere ist,
man erkennt etwas, was schon da ist und stellt fest,
gehört es denn zu dieser Grammatik?
Das ist etwas, was, wie gesagt, bei Compilern ganz viel passiert.
Und die BNF ist dann irgendwann mal erweitert worden,
um das alles ein bisschen netter hinschreiben zu können.
Also statt dieser rekursiven Definition Anweisungsfolge ist irgendwie entweder eine Anweisung oder eine Anweisung,
gefolgt von einem Semikol und einer weiteren Anweisungsfolge,
kann man auch so hinschreiben.
Anweisungsfolge ist definiert als eine Anweisung, gefolgt von beliebig vielen Synikolern-Anweisungen.
Also diese geschweifte Klammer hier liest sich als Meta-Symbol.
Das, was dazwischen steht, beliebig oft. Auch nullmal,
auch gar nicht. Optionale Elemente, schreibt man in eckigen Klammern.
Okay, man sieht das ungefähr so aus.
Das Ganze kann man auch in Syntax-Diagrammen darstellen,
wenn man mit Pascal programmiert hat, der hat vielleicht mal sowas gesehen,
also dass diese Syntax-Regeln auch in solche schönen Diagramme gefasst werden können.
Ja, also das ist Hier jetzt zum Beispiel die Zuweisung
ist dann definitiv Bezeichner und das ist dann ein terminales Symbol in
so einem Kreis und ein Nicht-Terminal wieder in einem eckigen Kasten.
Und da muss es natürlich wieder genau für jedes Nicht-Terminal auch wieder Regeln geben.
Und dann kann man die so schön aneinanderketten und so weiter.
Okay, Sonntagsdiagramme nicht so spannend. Was jetzt spannend ist,
ist das, was ich hier zeigen will.
Warum machen wir das Ganze? Warum haben wir das jetzt
alles hier thematisiert? Weil auch die Syntax von Java so beschrieben ist,
und zwar hier, ich kann das hier mal zeigen,
wir haben hier jetzt die Sprachspezifikation von Java,
die ist frei verfügbar im Netz unter diesem Link
So, und die ist, wie ihr seht,
wenn ich das hier mal so runterziehe, ziemlich lang.
Ja, und länger und länger und länger und länger und länger und länger
. Klar. Ist ja eine Riesensprache. Und was ich
eigentlich zeigen will, ist hier unten Syntax. Ja,
hier ist jetzt etwas dazu beschrieben, wie die Syntax von Java ist.
Und hier ist dann auch die Grammatik der Programmiersprache Java.
Der Bibliovita jetzt draufklicke, dann sieht man hier etwas,
was nicht ganz BNF ist.
Deswegen werden wir jetzt gleich nochmal ein bisschen erläutern,
wie das zu lesen ist, was hier zu sehen ist.
Auch die ist wieder sehr lang.
Diese Syntax ist sehr lang
Also leider auch sehr unübersichtlich und sehr schwer nachzuvollziehen.
Tut mir leid, dass ich es hier so durchscrolle.
Deswegen haben wir etwas Einfacheres gebaut. Nämlich eine abgespeckte Syntax. Das heißt,
wir haben gesagt, das,
was wir auf Level 1 jetzt hier in SE1 machen,
also was so ein bisschen mit Klassen,
ein bisschen was mit Ausdrücken, ein bisschen was mit Zuweisung,
das kann man als Subset der Sprache Java auffassen.
Also eine Teilmenge der Sprache da war. Und das
kann man mit relativ wenigen Syntaxregeln beschreiben, relativ wenigen.
Und wir haben diese wenigen Syntaxregeln tatsächlich mehr oder weniger auf eine DIN A4-Seite geschafft zu packen.
Und man kann das hier mal sehen,
wenn ich mir das jetzt mal angucke hier, Aufgabenblatt 3,
da haben wir die Syntax von Java Level 1,
die haben wir hier dann auf einer Seite.
Und so sieht das dann aus, ist natürlich jetzt sehr klein.
Klar, könnt ihr nicht lesen, aber man sieht alles
für Java Level 1, passt auf eine Seite.
Das ist einfach für euch die Möglichkeit,
jetzt mit diesem Bildungsgesetzen für solche Syntax,
und zwar in Syntax drin umzugehen, und zwar an dem realen Beispiel Java-Programme.
Wir nehmen mal hier so einen kleinen Ausschnitt.
Kinder mit der Lupe hier immer so rein.
Zum Beispiel hier Statement. Dann kann man es jetzt mal
lesen. Also ein Statement in Java kann sein.
Ein IF oder ein Return, einfach eine leere Anweisung,
eine Zuweisung, einen Methodenaufruf oder einen Blog.
Das heißt, das hier ist ein Nicht-Terminal und das
erläutere ich jetzt noch mal kurz hier anhand der Folien.
Also, erstens das Startsymbol für unsere Grammatik ist Compilation Unit,
eine Übersetzungseinheit für Java. Man muss immer ein Startsymbol haben
. Und dann ist hier tatsächlich gesagt, naja,
Die steht immer so mit so einem Doppelpunkt da hingeschrieben.
Als Ableitungssymbol. Das ist also der rote Fall,
den ich auf den Folien vorher hatte.
Und dann kann ich halt sagen, eine Übersetzungseinheit kann sein,
eine normale Klassendeklaration. Und was ist jetzt eine normale Klassendeklaration?
Naja, die steht aus Klaas, Name der Klasse,
Klaas Body und so weiter.
Also diese ganzen Bildungsregeln sind hier tatsächlich direkt für Java festgehalten.
Und wie gesagt, die Syntax ist etwas ungewöhnlich.
Wir machen hier Terminale, haben wir ein bisschen hervorgehoben,
in blau. Und die EBNF-Symbole in Rot,
also hier auch in diesem Beispiel jetzt, das sind hier die eckigen Klammern.
Also das ist optional. Also bei einem IF kann ich
einen geklammerten Ausdruck angeben, muss eine Expression sein, Bulsch.
Wenn das zutrifft, dann soll das ausgeführt werden.
Und der Elsfall ist optional. Deswegen hier mit diesen eckigen Klammern
als optional gekennzeichnet. Genau. Und dann haben wir hier
auch nochmal so einen Ausschnitt, das habe ich hier auch nochmal jetzt,
also nicht Terminal, für eine Anweisung, Statement.
Und dann sind die Alternativen, sind hier auf eigenen Zahlen.
Also jede Zeile steht für eine alternative Möglichkeit, das nicht terminal abzuleiten.
Ich kann also entweder aus einem Statement das machen oder das oder das und so weiter.
Sechs Alternativen, hier in einer eigenen Zeile. So im Skript ist jetzt nochmal,
Auf den folgenden Folien ein Beispiel, wo man noch mal nachvollziehen kann,
wie für einen bestimmten Ausdruck man diese Regeln anwenden kann und dann was ableiten kann.
Das könnt ihr euch noch mal sozusagen als Hausaufgabe angucken.
Das werde ich hier jetzt nicht ausführlich vorstellen.
Hier war jetzt erst mal der Ansatz, ihr solltet verstehen,
dass es sowas gibt wie eine Syntax und dass sie formal beschreibt,
wie korrekte Java-Programme aufgeschrieben werden.
Das ist die Markus-Nauer-Form. Wir haben die erweiterte Backos-Nauer aufbauen
gesehen und auch diese Java-Ausprägung. Gab es noch eine Frage
gerade? War keine Frage, sonst noch Fragen dazu
Keine Fragen, dann würde ich sagen, viel Spaß in den Übungen.
Stopp, stopp, stopp, stopp, stopp.
Stopp. Sitzen bleiben, stehen bleiben. Der Sebastian will noch
eine kurze Ansage machen. Du kannst hier mein Mikro kriegen.
Soll ich das einfach mal rausnehmen? Hallo? So,
bitte, ich würde euch gern hier das Tutorenprogramm vorstellen.
Und zwar soll das nächste Woche beginnen. Die Idee von dem Tutorenprogramm ist,
Das ist in den letzten Jahren von Studierenden entstanden, die Idee.
Ihr seid ja im ersten Semester und müsst viel Übungen machen
und wollt euch noch viel kennenlernen und so weiter.
Und deswegen haben wir die Idee gehabt,
dass Tutoren mit euch betreute Lerngruppen machen.
Und da trefft ihr euch dann einmal in der Woche,
das Semester über und könnt dann eben zusammenarbeiten.
Dann könnt ihr miteinander planen, ob ihr mal was unternehmen wollt.
Und ja. So, um daran teilzunehmen,
müsste ich
eine Internetadresse anschreiben
Da solltet ihr, wenn ihr daran teilnehmen wollt,
bitte alle draufgehen und bis Samstag euch dort anmelden.
Ja, also es gibt da dann Gruppen speziell,
auch für SE1, für diskrete Mathematik und für Rechnerstrukturen?
Ja, ich schreibe einfach die URL an die Tafel
und dann wünsche ich euch
viel Spaß