====== Programmieren in C# ====== {{:ef_informatik:imgoodatcsharp.jpg?300|}} Ziel dieses Tutorials ist, die Grundlagen der Programmierung anhand der Programmiersprache C# (sprich: "C-sharp") zu lernen. Programmieren läuft immer wie folgt ab: - Man **schreibt** seinen Code ... - und **führt** diesen dann **aus**. Am einfachsten geht dies mithilfe einer **IDE** (**Integrated Development Environment**). Dies ist ein Programm, in welchem man seinen Code schreiben kann und welches einem auf allfällige Fehler (Bugs) aufmerksam macht. Per Knopf- oder Tastendruck kann der Code dann ausgeführt werden. ===== - Einführung ===== ==== - Installation & Hello World ==== Um C# zu programmieren, verwenden wir die IDE **Visual Studio** (kurz: **VS**) von Microsoft. Achtung: Nicht zu verwechseln mit dem *Visual Studio Code*! - Lade die **Community Edition** (gratis) des **Visual Studios** von der Website https://visualstudio.microsoft.com/de/ herunter ... - ... und installiere sie. - Starte dann das VS und beginne ein neues C# **Konsolenprojekt** und gib ihm einen Namen. - Diese Projekt enthält bereits Code, der wie unten angezeigt aussehen sollte. - Dieses Programm ist das bekannteste Programm der Welt: **Hello World**. Alles was dieses Programm macht, ist die Nachricht ''Hello World!'' im Ausgabefenster, genannt **Konsole** oder **Terminal**, auszugeben. Drücke, um das Programm auszuführen auf den (grünen) Pfeil oder F5. using System; namespace MyProject { class MainClass { public static void Main(string[] args) { Console.WriteLine("Hello World!"); } } } Doch was macht dieses Programm genau? Was die Zeilen wie ''using System;'' oder ''class MainClass'' bedeuten, besprechen wir später. Für den Moment ist nur folgendes wichtig: Sobald das Programm ausgeführt wird, wird der Code, der in der **Main-Methode** drin steht, ausgeführt - und zwar von oben nach unten. Dies ist der Code, der in den innersten geschwungenen Klammern steht. In diesem Beispiel ist dies nur genau eine Zeile: `Console.WriteLine("Hello World!");` Dies gibt den Befehl, den *String* (bedeutet Text in der Informatik) `Hello World!` in die Konsole zu schreiben. ==== - Tipps ==== * Um eine gute Programmierer\*in zu werden benötigt es **Übung, Übung und nochmals Übung**! * Im **Interne**t findest du sehr viele Informationen zum Thema C#. Nutze dies! Zum Beispiel: schriftliche Tutorials, YouTube-Tutorials, Diskussionen in Foren, ... * Verändere das vorgegebene **Gerüst** des Programms nicht. Lasse also z.B. Zeilen wie `class MainClass` unverändert. * **Projektnamen:** Benenne ein bestehendes Projekt nicht um, da dies zu Fehlern führen wird. Umgehe das Problem wie folgt: Erstelle ein neues Projekt mit dem richtigen Namen und copy-paste den von dir geschriebenen Code (und nicht das Gerüst darum herum) ins neue Projekt. * Lese **Fehlermeldungen**. Schreibst du Code, der nicht den vorgegebenen Regeln von C# entspricht, weigert sich VS, das Programm auszuführen. Du erhältst dann eine Fehlermeldung. Diese Meldungen sind oft sehr hilfreich und helfen dir, das Problem zu identifizieren und zu beheben. * Erhöhe deinen nerdigen **Coolness-Faktor**, in dem du ... * ... deinen Code komplett auf *Englisch* schreibst * ... möglichst wenig die Maus brauchst und alles mit der *Tastatur* mit *Shortcuts* erledigst! * In allen Programmiersprachen gibt es **Stil-Konventionen**, an die man sich halten sollte. In C# sollten Projektnamen, Variablennamen usw. folgende Regeln befolgen: * Verwende **CamelCase**. Nenne dein erstes Projekt zum Beipiel ''HelloWorld'' und nicht ''hello_world''. * Vermeide Leerschläge, also nicht: ''Hello World'' (in Strings sind Leerzeichen natürlich völlig ok) * Vermeide Umlaute, also nicht: ''HelloWörld'' {{youtube>HluANRwPyNo|What people think programming is vs. how it actually is}} * Weitere **Tipps** und **Shortcuts** werden in den unteren Kapiteln in grün angezeigt. * Viele Personen machen ihre ersten Programmiererfahrungen in Python. Deshalb werden immer wieder Vergleiche zwischen Python und C# gezogen. Alle solche sind in der gleichen Farbe dargestellt. ==== - Ausgabe in Konsole ==== Wie bereits oben gesehen, kann man mit `Console.WriteLine();` etwas in der **Konsole ausgeben**: Console.WriteLine(7); // Zahl ausgeben Console.WriteLine("Hello World!"); // String ausgeben Console.WriteLine("Die Zahl " + 7 + " ist eine Primzahl"); * In Zeile 1 wird die Zahl `7` ausgegeben. * In Zeile 2 wird der Text `Hello World` ausgegeben. Beachte, dass man hier zwingend Anführungs- und Schlusszeichen anbringen muss. Eine Textzeile wird in der Informatik als **String** bezeichnet. * In Zeile 3 werden Strings und eine Zahl mit dem `+`-Symbol miteinander verknüpft. * Beachte, dass jede Linie mit einem **Semikolon** `;` beendet werden muss. * Mit zwei **Slashes** `\\` kann man dem Code einen **Kommentar** hinzufügen. Ein solcher ist kein Programmcode und wird beim Ausführen ignoriert. Damit kann man seinem Code Anmerkungen anfügen, damit dieser besser verständlich wird. * **Shortcut:** Tippe nur `cw` + Tab und schon steht `Console.WriteLine();` da! Hurray! * Das `Console.WriteLine();` entspricht dem `print()` in Python. Kommentare in C# schreibt man mit `\\`, in Python mit `#`. ===== - Variablen ===== Variablen geben einem die Möglichkeit, Information wie zum Beispiel Zahlen temporär zu speichern. Eine Variable hat immer einen **Datentyp**, von denen es eine grosse Anzahl gibt in C#. Möchte man Text ausgeben, so arbeitet man mit **Strings**. Möchte man eine Rechnung mit ganzen Zahlen (Zahlen ohne Nachkommastellen) ausführen, arbeitet man mit **int**s (von integer). Möchte man aber Nachkommastellen anzeigen können, verwendet man **double**s. Beachte, dass C# eine **typensichere** Sprache ist. Möchtest du eine Variable mit Namen `a` haben, die eine ganze Zahl speichern soll, so musst du die Variable mit ihrem Typ *explizit deklarieren*: int a; // Deklaration der Variablen a vom Type int a = 7; // Variable a wird den Wert 7 zuweisen Console.WriteLine(a); * In Zeile 1 wird die Variable deklariert. Diese soll vom Typ `int`, also ein Integer (ganze Zahl, also keine Kommazahl), sein. Dabei soll sie den Namen `a` tragen. * In Zeile 2 weisen wir der Variablen `a` den Wert `7` zu. * In Zeile 3 greifen wir auf diese Variable zurück und geben deren Wert in der Konsole aus. Es wird die Zahl `7` in der Konsole angezeigt. Übrigens kann man die ersten beiden Zeilen auch in eine zusammenfassen: int a = 7; // Kombination von Deklaration und Wertzuweisung einer Variablen Zusammengefasst hat eine Variable: * einen Namen (z.B. `a`) * einen Typ (z.B. `int`) * einen Wert (z.B. `7`) C# ist eine **typensichere** Sprache. Dies bedeutet, dass wir einer Variablen ausschliesslich Werte zuordnen können, die ihrem Typ entspricht. Betrachten wir folgenden Code: int a = 7; a = 9; // erlaubt a = 3.14; // nicht erlaubt, gibt einen Fehler In Zeile 2 wird der Wert der Variablen geändert. Dies ist kein Problem, da `9` ein Integer ist. Zeile 3 ist aber problematisch, da man versucht, `a` eine Gleitkommazahl zuzuordnen. Dieser Code ist *nicht ausführbar* - versucht man es trotzdem, wird man eine Fehlermeldung erhalten. Möchtest du eine Gleitkommazahl oder einen String in einer Variablen speichern, musst du dazu passende **Datentypen** wählen: double b = 3.14; // Gleitkommazahlen heissen Doubles in C# string c = "Ich bin ein String!"; // Variable vom Typ String Neben den hier erwähnten Datentypen int, double und string gibt es noch weitere. Einige davon wirst du im Verlaufe dieses Dossiers kennenlernen. Python ist eine **nicht typensichere** Programmiersprache. Dort kann eine Variable beliebig ihren Typ ändern. Folgender Code würde in Python ohne Probleme funktionieren: # Dies ist Python-Code, NICHT C#! a = 42 # hier ist a ein int a = 1.23 # hier wird a zu einem float umdeklariert a = "And now I am a string!" # ... und hier zu einem String Solche Umdeklarationen sind in C# NICHT möglich! Ist `a` einmal ein int, so bleibt es ein solcher. In C# kann man auch rechnen. Hier ein Beispiel mit den gängigsten **Rechenoberationen**: int a = 7; int b = 15; Console.WriteLine(a + b); // Addition Console.WriteLine(a - b); // Subtraktion Console.WriteLine(a * b); // Multiplikation Console.WriteLine(a / b); // Division int result = a + b; // Addition, Resultat in neue Variable schreiben Console.WriteLine(result); **Übersicht mathematische Operatoren:** ^Operator^Beschreibung^ |`+`|Additionsoperator (Summe `x + y`) oder Vorzeichenoperator (`+x`)| |`-`|Subtraktionsoperator (Differenz `x - y`) oder Vorzeichenoperator (`-x`)| |`*`|Multiplikationsoperator multipliziert zwei Operanden (`x * y`).| |`/`|Divisionsoperator (`x/y`), Ganzzahldivision bei ints, mit Nachkommateil bei doubles| |`%`|Restwertoperator (`x % y`), gibt Rest der Ganzzahldivision| |`++`|Inkrementoperation (`x++` oder `++x`), erhöht Wert von `x` um 1| |`--`|Dekrementoperation (`x--` oder `--x`), verringert Wert von `x` um 1| ==== - Aufgaben A ==== === Aufgabe A1 === Welche der folgenden Namen sind zulässig für Variablen in C#? Begründe warum (nicht): - `iamavariable` - `42` - `g` - `IchPinEyneWariaple` - `secret number` - `_hans` - `!number` - `int` - `iAmAVariable` === Aufgabe A2 === Spiele mit den oben gelernten Datentypen. Deklariere Variablen, weise ihnen Werte zu, mache Rechnungen, gib Resultate in der Konsole aus ... Tobe dich aus und habe Spass! === Aufgabe A3 === Speichere die Zahl `1000000000` als int in einer Variablen. - Berechne nun das Doppelte, das Dreifache, Vierfache, ... dieser Variablen und gebe jeweils das Resultat aus. Was fällt dir auf? Begründe. - Nenne ein konkretes Beispiel für eine Aufgabe, bei der dies zu Problemen führen kann. - Was lernst du daraus für deine Programmierprojekte? Wie kann man das Problem umgehen? === Aufgabe A4 === Welchen Wert hat der grösste und welchen Wert der kleinste int, den man in C# darstellen kann? Woher kommt dies? Begründe ganz genau. === Aufgabe A5 === Mit `Console.WriteLine()` kann man etwas in der Konsole ausgeben. Ähnlich kann man mit `Console.ReadLine()` in der Konsole etwas eingeben und diesen Inhalt dann in einer Variablen speichern. Die Benutzer*In soll der Reihe nach aufgefordert werden, zuerst denn Vornamen, dann den Nachnamen und dann das Alter einzugeben. Alle Werte sollen dann in einer Variablen gespeichert werden. Danach soll ein Sätzli ausgegeben werden, welches alle eingegebenen Werte beinhaltet, zum Beispiel: //Hello Fritz Fischer. You are 42 years old!// Achtung: Beim eingeben des Alters wird ein Problem auftreten. Versuche mithilfe des Internets, selbst eine Lösung dafür zu finden! === Aufgabe A6 === Berechne oder bestimme mithilfe der Rechenoperatoren. Verwende ausschliesslich ints. Schreibe also `45` und //nicht// `45.0`: - Berechne die Ganzzahldivision $10023 / 45 = ?$, bestimme also Ganzzahlquotient und Rest. - Ist 1346 durch 17 teilbar? ===== - Logik ===== Beim Programmieren kommt man oft in die Situation, wo man **Entscheidungen** treffen muss. Zum Beispiel soll, je nach dem was der Benutzer eintippt, ein anderer Code ausgeführt werden. ==== - If-else if-else-Anweisung ==== Am einfachsten ist die sogenannte **if**-Anweisung: Damit kann man überprüfen ob (also //if//) eine gewisse **Bedingung (condition)** erfüllt ist oder nicht. Dieser Befehl hat die Form if (CONDITION) { // Codeblock in geschwungenen Klammern wird nur ausgeführt, wenn die BEDINGUNG erfüllt ist. } Damit können wir herausfinden, ob der Wert einer Variablen positiv ist: int x = 5; if (x >= 0) // checks if x greater equal zero { // code block gets executed ONLY if condition above is satisfied Console.WriteLine("x is greater equal zero!"); } Falls der Wert von `x` auf eine negative Zahl gesetzt wird, wird in der Konsole nichts ausgegeben. Schön wäre aber, dass uns mitgeteilt würde, dass die Zahl negativ ist. Dies erreicht man mit einer **if-else**-Anweisung: int x = -5; if (x >= 0) // checks if x greater equal zero { // code block gets executed ONLY if condition above is satisfied Console.WriteLine("x is greater equal zero!"); } else { // code block gets executed ONLY if condition above is NOT satisfied Console.WriteLine("x is negative!"); } Der Codeblock im else wird nur dann ausgeführt, wenn die Bedingung beim if //nicht// erfüllt wird. Falls wir den Fall $x = 0$ separat behandeln möchten, verwenden wir eine **if-else if-else**-Anweisung: int x = 0; if (x > 0) // checks if x greater zero { Console.WriteLine("x is positive!"); } else if (x == 0) // checks if x equal zero but only if first condition is not satisfied { Console.WriteLine("x is zero!"); } else { // gets executed if none of the above conditions are satisfied Console.WriteLine("x is negative!"); } Beachte, dass man beliebig viele //else if// haben kann. Die allgemeine Form ist also: if (CONDITION 1) { // code block if CONDITION 1 satisfied } else if (CONDITION 2) { // code block if CONDITION 2 satisfied } else if (CONDITION 3) { // code block if CONDITION 3 satisfied } ... else { // code block if NONE of the above conditions are satisfied } ==== - Vergleichsoperatoren ==== Für die Bedingungen stehen folgende **Vergleichsoperatoren** zur Verfügung: ^Operator^Beschreibung^ |`a == b`|(Vergleichsoperator) prüft, ob der Ausdruck a dem Ausdruck b entspricht, und gibt in diesem Fall `true` zurück.| |`a != b`|Ergebnis der Operation ist `true`, wenn a ungleich b ist.| |`a > b`|Ergebnis der Operation ist `true`, wenn a grösser b ist.| |`a >= b`|Ergebnis der Operation ist `true`, wenn a grösser oder gleich b ist.| |`a < b`|Ergebnis der Operation ist `true`, wenn a kleiner b ist.| |`a <= b`|Ergebnis der Operation ist `true`, wenn a kleiner oder gleich b ist.| **Bemerkungen**: * Eine Bedingung ist vom Datentyp **Boolean**, für den es genau zwei Möglichkeiten gibt: Er kann entweder erfüllt sein (`true`) oder nicht (`false`). Vergewissere dich selbst, dass zum Beispiel `Console.WriteLine(3 == 5);` den Wert `false` und `Console.WriteLine(3 < 5);` den Wert `true` ausgibt. Den Wert, der eine Bedingung annimmt, kann man auch wieder in einer Variablen speichern, z.B. `Boolean boo = (3 == 5);` * Beachte auch, dass **Verschachtelungen** gar kein Problem sein. Zum Beispiel kannst du im Codeblock von einem //else// ein neues //if-else if-else// haben. * Beachte den Unterschied zwischen dem **einfachen (`=`) und doppelten (`==`) Gleichheitszeichen**: * Das einfache Gleichheitszeichen ist ein //Zuweisungsoperator//: `x = 7` bedeutet, dass der Variablen auf der linken Seite der Wert auf der rechten Seite zugewiesen wird. * Das doppelte Gleichheitszeichen ist ein //Vergleichsoperator//: er überprüft, ob links und rechts das gleiche steht. Falls ja, nimmt die Bedingung den Wert `true`. Falls nicht, nimmt es den Wert `false` an. Das doppelte Gleichheitszeichen in der Informatik entspricht also in etwa dem normalen Gleichheitszeichen in der Mathe, zum Beispiel beim Lösen von Gleichungen. In Python sieht das entsprechende if-else if-else-Statement wie folgt aus: # Python Code! if CONDITION 1: # code block if CONDITION 1 satisfied elif CONDITION 2: # code block if CONDITION 2 satisfied elif CONDITION 3: # code block if CONDITION 3 satisfied ... else: #code block if NONE of the above conditions are satisfied Beachte, dass in Python Codeblocks durch Einrückungen (Tab) festgelegt werden. In C# ist die Einrückung komplett egal, stattdessen werden geschwungene Klammern verwendet. **Shortcut:** Alles wieder schön einzurücken (rein visuell, hat keinen Einfluss auf Code): Ctrl + K, Ctrl + D ==== - Logische Operatoren ==== Es können auch mehrere Vergleichsoperatoren miteinander verknüpft werden. Verknüpft man mehrere solche mit dem **AND-Operator**`&&` so ist die Bedingung genau dann true, wenn //sämtliche// Vergleichsoperatoren true sind. Verwendet man den **OR-Operator** `||`, so ist die Bedingung true, wenn //mindestens ein// Vergleichsoperator true ist. Die entsprechenden logischen Operatoren in Python sind `and` und `or`. ==== Aufgaben B ==== === Aufgaben B1 === Schreibe ein Programm, welches die Benutzer*In auffordert eine ganze Zahl einzugeben. Die Zahl soll auf vier Fälle untersucht werden und eine entsprechende Nachricht soll ausgegeben werden. Die vier Fälle sind: * 42: The answer to the ultimate question of life, the universe and everything! * positive Zahlen (ausser 42) * Null * negative Zahlen Falls du den 42-Joke nicht verstehst, bist du noch nicht nerdy genug und musst dringend //The Hitchhiker's Guide to the Galaxy// lesen oder schauen! === Aufgaben B2 === - Schreibe für einen Online-Alkoholhändler ein einfaches Programm, bei dem die Kund\*In ihren Namen und ihr Alter eingeben muss und dann eine personalisierte Antwort darüber erhält, ob sie alt genug ist für einen Einkauf, je nachdem, welches Alter sie eingetippt hat, zum Beispiel: //"Liebe\(r) Ruthild, leider bist du nicht alt genug für einen Einkauf bei uns."// ! - Erweitere dein Programm: Die Kund\*In wird auch nach dem Geschlecht gefragt. Je nach Eingabe soll dann die Anrede angepasst werden: //"Liebe Ruthild, leider bist du nicht alt genug für einen Einkauf bei uns."// oder //"Lieber Hans-Joseph, viel Spass beim Einkaufen!"// ===== - Debugging ===== Der **Debugger** erlaubt es einem, den Code eine Zeile nach der anderen auszuführen. Damit kann man einfach nachvollziehen, was der Code wo macht und welche Variable wann und wo welchen Wert annimmt. Dies ist besonders wertvoll, um Fehler (also Bugs) im Code zu finden - deshalb nennt man diesen Modus den Debugger! Zuerst musst du einen oder mehrere **Breakpoints** setzen: Klicke dazu beim Code links von der Zeilennummer auf den Rand, dann erscheint ein roter Punkt, genannt Breakpoint. Führst du den Code nun im Debug-Modus aus, so pausiert die Ausführung des Codes beim ersten Breakpoint. Nun kannst du mit F10 den weiteren Code Zeile-für-Zeile ausführen. Mit F5 lässt du den Code wieder weiterlaufen bis er zu einem weiteren Breakpoint oder dem Ende gelangt. Im Fenster **Lokale Variablen** siehst du, welche Variablen im Code vorkommen und welche Werte sie zum aktuellen Zeitpunkt gerade haben. **Shortcuts:** * **F5**: Code bis zum Ende/nächsten Breakpoint ausführen * **F10**: Code Zeile-für-Zeile ausführen ==== - Aufgaben C ==== === Aufgabe C1 === Öffne eines deiner bisherigen Projekt mit möglichst viel Code. - Setze eine Breakpoint und führe den Code aus. Gehe dann mit F10 Schritt-für-Schritt durch den Code. - Setze mehrere Breakpoints und finde heraus, wie du mit F10 und F5 den Code ausführen kannst. ===== - Schleifen ===== Schleifen (loops) kommen dann zum Zug, wenn wir einen gewissen **Codeblock mehrfach hintereinander ausführen** wollen. Dabei gibt es zwei unterschiedliche Arten von Schleifen: * **While-Schleife:** wird solange ausgeführt, wie eine gegebene //Bedingung// erfüllt ist. * **For-Schleife:** wird eine fest //vorgegebene Anzahl-Mal ausgeführt//, praktisch, wenn man z.B. etwas hochzählen möchte ==== - While-Schleifen ==== Eine **while-Schleife** hat immer folgende Form: while (CONDITION) { // code block that gets executed as long as CONDITION is satisfied } Der Codeblock in den geschwungenen Klammern wird solange wiederholt, wie die Bedingung (condition) erfüllt ist, solange also die Bedingung `true` ist. Ein Fehler, der einem beim Programmieren gerne passiert, sind **Endlosschleifen (infinite loop)**. Dies sind while-Schleifen, deren Bedingung //immer erfüllt// ist, die Schleife wird also solange ausgeführt, bis die Benutzer*In den Code manuell abbricht. Ein Beispiel für eine Endlosschleife ist folgender Code: while (true) { Console.WriteLine(42); } Für die Bedingung einer while-Schleife kann man die Gleicheng Bedingungen wie bei if-Anweisungen (siehe [[#vergleichsoperatoren|Vergleichsoperatoren]]) verwenden. Eine while-Schleife in Python sieht wie folgt aus: # PYTHON CODE while true: # Code block in while-loop Beachte, dass in Python der Codeblock, der zur Schleife gehört, per Tab eingerückt wird. In C# wird der entsprechende Codeblock innerhalb der geschwungenen Klammern geschrieben. **Shortcut:** Tippe //while// + 2xTab, um das Grundgerüst einer for-Schleife zu erzeugen. Passe es dann entsprechend an. While-Schleifen sind sehr praktisch, wenn man im Vorfeld //nicht genau weiss, wie oft der Codeblock in der Schleife wiederholt// werden soll. Zum Beispiel könnte man ein Quizspiel programmieren, in dem die Benutzer*In solange weiterspielen darf, bis sie einen Fehler macht. Dies kann bereits bei der ersten Frage sein, es kann aber auch erst bei Frage 42 sein - man weiss es nicht im Vorfeld. Weiss man hingegen bereits im Vorfeld, wie oft der Inhalt wiederholt werden soll, sind for-Schleifen sehr praktisch ... ==== - For-Schleifen ==== Eine **for-Schleife** führt einen Codeblock //eine fest vorgegebene Anzahl Mal durch// und hat folgenden Aufbau: for (VARIABLE WITH STARTING VALUE; VARIABLE WITH CONDITION; VARIABLE IN/DECREMENT) { // Code block } Sie kommt also dann zur Anwendung, wenn man schon im Vorhinein //genau weiss, wie oft die Schleife ausgeführt werden soll//. Folgender Code zum Beispiel gibt $7\times$ hintereinander die Zahl $42$ aus. for (int i = 0; i < 7; i++) { Console.WriteLine("42"); } Für die for-Schleife wird hier die Variable `i` (vom Typ int) mit Anfangswert $0$ festgelegt. Beim ersten Durchlauf der Schleife hat `i` also den Wert $0$. Beim zweiten Durchlauf hat `i` dann den Wert $1$, da wir mit `i++` vorgeben, dass `i` bei jedem durchlauf um $1$ erhöht wird. Dies geschieht solange, wie die Bedingung (hier: `i < 7`) erfüllt ist. Die Variable `i` kann man natürlich auch im Codeblock verwenden. Folgender Code gibt zum Beispiel alle Zahlen von $0$ bis und mit $6$ in der Konsole aus: for (int i = 0; i < 7; i++) { Console.WriteLine(i); } Der entsprechende Code in Python sieht wie folgt aus: # PYTHON CODE for i in range(7): print(i) **Shortcut:** Tippe //for// + 2xTab, um das Grundgerüst einer for-Schleife zu erzeugen. Passe die genauen Details dann entsprechend an. ==== - Break & continue ==== Mithilfe von **break** und **continue** kann man jederzeit aus einer Schleife **ausbrechen**: * Die **break** Anweisung steht irgendwo im Codeblock der Schleife, meist in Verbindung mit einer if Abfrage. Läuft das Programm in sie hinein, bricht sie die Schleife ab und man springt zum Code nach der Schleife. * Die **continue** Anweisung bricht den //aktuellen Schleifendurchgang// ab und springt wieder zum Beginn der Schleife. ==== - Aufgaben D ==== === Aufgabe D1 === - Gib alle ganzen Zahlen von $10$ bis und mit $20$ aus ... - mithilfe einer for-Schleife - mithilfe einer while-Schleife - Gib alle geraden Zahlen von $2$ bis und mit $20$ aus ... - mithilfe einer for-Schleife - mithilfe einer while-Schleife - Gib alle ganzen Zahlen von $-1$ bis und mit $-10$ in absteigender Reihenfolge aus ... - mithilfe einer for-Schleife - mithilfe einer while-Schleife === Aufgabe D2 === Wandle in eine while-Schleife um: for (int k = 7; k < 13; k++) { Console.WriteLine(2*k); } === Aufgabe D3 === Betrachte folgenden Code: int x = 5; while (x < 10) { Console.WriteLine(x); x++; } - Was macht der Code? Versuche es herauszufinden, ohne den Code auszuführen. - Schreibe nun den Code so um, dass die Schleife das unten angezeigt Grundgerüst hat. Verwende die **break** Anweisung, um aus der Schleife auszubrechen. ... while (true) { ... } === Aufgabe D4 === - Die Benutzer\*In wird aufgefordert, eine positive ganze Zahl einzugeben. Dieses Spiel soll solange wiederholt werden, bis sie eine negative Zahl eingibt. Das Spiel wird dann abgebrochen.\\ \\ - Beim Programmieren muss man immer damit rechnen, dass die Benutzer\*In nicht das macht, was sie sollte (manchmal aus Unwissenheit, manchmal aus purer Bosheit)! Hier zum Beispiel könnte die Benutzerin etwas eintippen, was //nicht in eine Zahl umgewandelt werden// kann. Aktuell würde dies wahrscheinlich dein Programm crashen. Dieses Problem kann man damit beheben, in dem man den **Fehler abfängt (exception handling)**, und zwar mit einer **try-catch**-Anweisung. Finde mithile einer Internetsuche heraus, wie dies funktioniert und schreibe dir einen kurzen Eintrag dazu in deine Unterlagen. Nutze dann dein neu erlangtes Wissen, um dein Programm aus dem vorherigen Punkt so anzupassen, dass es robust gegen renitente Benutzer\*Innen ist. === Aufgabe D5 === Schreibe ein einfache **Quiz-Game**, um deine Kopfrechenfähigkeiten zu trainieren. Die Benutzer\*In soll solange weiterspielen können, bis sie einen Fehler begeht, dann ist Game Over! Die Rechnungen sollen einfache Additionen von zwei ganzen Zahlen sein, wobei diese Zahlen per Zufall ausgewählt werden (siehe Codebeispiel unten). Der Benutzer\*In soll also die Rechnung präsentiert werden und dann das eingetippte Resultat mit dem tatsächlichen Resultat verglichen werden. Folgender Code gibt einem $10$ **Zufallszahlen** im Bereich $1$ bis $5$ aus und dient als Beispiel dafür, wie man Zufallszahlen generiert: Random rand = new Random(); for (int i = 0; i < 10; i++) { Console.WriteLine(rand.Next(1, 6)); } === Aufgabe D6 === Mache eine Kopie von deinem Mathe-Quiz und erweitere es Schritt-für-Schritt: - Am Schluss soll der Spielstand ausgegeben werden. - Man soll solange spielen können, bis man drei Fehler gemacht hat. - Neben Aufgaben zur Addition sollen auch Aufgaben zur Subtraktion und Multiplikation, wenn du möchtest auch zur //Ganzzahldivision// vorkommen. Welche Operation in einer Aufgabe vorkommt, soll per Zufall bestimmt werden. - Gestalte das Spiel schön. Gib jeweils den Spielstand aus, bereinige die Konsole nach jedem Durchgang (`Console.Clear();`), ... ===== - Algorithmen ===== Ein **Algorithmus** ist eine //eindeutige Handlungsanweisung// für die Lösung von Problemen. Algorithmen bestehen aus //endlich vielen, wohldefinierten// Einzelschritten. Ein Algorithmus ist also ein Lösungsweg, eine Verarbeitungsvorschrift, die so präzise formuliert ist, dass sie einem Computer beigebracht (durch Programmieren) werden kann. Ein Algorithmus ist gewissermassen wie ein Rezept, welches aber vollständig klar formuliert und keinerlei Spielraum für Freiheiten lässt. Gerade in der Mathematik sind Algorithmen sehr wichtig. Es gibt zum Beispiel Algorithmen für das Erkennen von Primzahlen oder das bestimmen des ggTs. In diesem Kapitel wollen wir einige solche Algorithmen anschauen und dabei unsere Programmierskills verbessern. === Auftrag: === Schreibe für deine Kolleg\*In auf //Papier// einen möglichst guten Algorithmus, der sie vom aktuellen Platz zum (a) Wasserhahn / (b) Haupteingang der Schule bringt. Gehe dabei aus, dass sie //maximal einen Meter weit sieht!// Danach soll sie den Algorithmus //genau so// ausführen, wie er auf dem Papier steht! ==== - Aufgaben E ==== === Aufgabe E1 === Die berühmte **Fibonacci-Folge** besteht aus den Zahlen $$1,1,2,3,5,8,13,21,\ldots$$ Wie sehen die nächsten Folgenglieder aus? Schreibe ein Programm, welches die Folgenglieder der Fibonacci-Folge bestimmt. === Aufgabe E2 === - **Primzahlen** sind positive ganze Zahlen, die nur durch sich selbst und $1$ teilbar sind, also $2,3,5,7,11,13,\ldots$. Schreibe ein Programm, in dem die Benutzer\*In eine ganze Zahl eingeben soll. Das Programm überprüft, ob es sich bei der Zahl um eine Primzahl handelt oder nicht und gibt eine entsprechende Rückmeldung. \\ \\ - Mache eine Kopie deines Programms. Nun soll eine Zahl eingegeben werden und sämtliche kleiner gleich dieser Zahl ausgegeben werden. === Aufgabe E3 === Mithilfe des **Euklidschen Algorithmus** kann man den grössten gemeinsamen Teiler (ggT) von zwei natürlichen Zahlen bestimmen. - Studiere, wie der Algorithmus funktioniert: https://www.lernhelfer.de/schuelerlexikon/mathematik/artikel/euklidischer-algorithmus. - Rechne von Hand auf (digitalem) Papier in paar Beispiele durch. - Implementiere den Euklidschen Algorithmus. === Zusatzaufgaben === Auf der Website https://projecteuler.net gibt es viele interessante Aufgaben, die man programmiererisch lösen soll. Erstelle einen Account und logge dich ein. Studiere eine Aufgabe, schreibe ein Programm, um sie zu lösen und gib deine Lösung auf der Website ein. Du erhältst dann eine Rückmeldung darüber, ob dein Resultat korrekt ist oder nicht. ===== - Arrays & Listen ===== Bisher haben wir Zahlen nur in Variablen speichern können, die jeweils den Wert einer //einzelnen// Zahl speichern können. Als nächstes schauen wir deshalb **Arrays** und **Listen** an. Diese erlauben es uns //mehrere Werte in einer einzigen Variablen// zu speichern, anstatt für jeden Wert eine eigene Variable zu deklarieren. Ein **Array** deklariert man wie folgt: int[] arr; // declare arr = new int[3]; // initialize In der ersten Zeile wird die Variable //deklariert//. Wir sagen dem Programm, dass die Variable mit Namen `arr` ein Array (wegen den eckigen Klammern `[]`) sein soll, welches ints beinhalten soll. In der zweiten Zeile wird die Variable //initialisiert//: Dazu verwenden wir das Keyword `new`. Wir sagen hier dem Programm, das das Array //genau 3// Elemente beinhalten soll. Der entsprechende Speicherplatz wird dann im RAM dafür bereitgestellt. Die beiden Zeilen können auch in einem Schritt erledigt werden: int[] arr = new int[3]; // declare and initialize Bei einem int-Array werden sämtliche Werte per Default auf $0$ gesetzt. Wir können aber auch gleich mit der Deklarierung und Initialisierung Werte zuweisen: int[] arr = new int[3] {7,13,42}; // declare, initialize and set values Auf die Werte eines Arrays kann über eckige Klammern zugegriffen werden: arr[0] = 42; // set value of first element to 42 Console.WriteLine(arr[0]); // access first element of array and print in console ACHTUNG: Beachte, dass man in C# und den meisten anderen Programmiersprachen **bei 0 zu zählen beginn!**. Das erste Element hat also den **Index** 0 usw. Dementsprechend folgender Joke: {{:ef_informatik:programmerjoke.jpg?400|}} Für Arrays muss bei der Initialisierung angegeben werden, wie viele Elemente es enthalten soll, damit der entsprechende Speicherplatz reserviert werden kann. Aus diesem Grund ist die **Grösse eines Arrays unveränderbar!** Dies ist oft aber unpraktisch, da man oft zu Beginn nicht weiss, wie viele Elemente man speichern möchte. Für solche sind Arrays wegen ihrer fixen Grösse unpraktisch. Stattdessen verwendet man dann **Listen**: List myList = new List(); Beachte, dass du mit `using System.Collections.Generic;` den Namespace System.Collections.Generic einbinden musst, um Listen überhaupt verwenden zu können. Listen können dann wie folgt manipuliert werden: myList.Add(7); // add element 7 at end of list, increases length of list by 1 myList.Insert(3, 19); // insert element 19 at index 3 myList.RemoveAt(3); // remove element at index 3, decreases length by 1 myList.Remove(42); // remove first occurrence of element 42 myList.RemoveAll(item => item == 42); // remove ALL occurrences of element 42 Console.WriteLine(myList.Count); // outputs length of list Beachte, dass Listen deutlich langsamer sind als Arrays. Verwende also wann immer möglich Arrays. Um sämtlichen Elemente eines Arrays oder einer Liste **in der Konsole auszugeben**, kann man die Liste mit einer for- oder foreach-Schleife durchgehen: // OPTION 1: for-loop for (int i = 0; i < myList.Count; i++) { Console.WriteLine(myList[i]); } // OPTION 2: foreach-loop foreach (var item in myList) { Console.WriteLine(item); } **Zusammengefasst:** * Verwende **Arrays** wenn die Grösse im Vorfeld bekannt ist ... * und **Listen** falls nicht. ==== - Aufgaben F ==== === Aufgabe F1 === Erstelle jeweils ein Array mit dem vorgegebenen Namen, welches die ersten 20 Folgenglieder der angegebenen Zahlenfolge enthält. Befülle sämtlichen Arrays in einer //einzigen// Schleife: - Natürlichen Zahlen (`naturalNumbers`): $1,2,3,...,20$ - Geraden Zahlen (`evenNumbers`): $2,4,6,...,40$ - Quadratzahlen (`squareNumbers`): $1,4,9,16,25,...$ === Aufgabe F2 === Erstelle jeweils ein Array mit dem vorgegebenen Namen, welches die ersten 20 Folgenglieder der angegebenen Zahlenfolge enthält. //Tipp:// Recycle deinen Code aus vorherigen Aufgaben. - Primzahlen (`primeNumbers`): $2,3,5,...$ - Fibonacci-Zahlen (`fibonacciNumbers`): $1,1,2,3,5,...$ === Aufgabe F3 === Die Benutzer*In wird aufgefordert, eine Zahl einzugeben. Sämtliche Zahlen der Fibonacci-Folge, die kleiner gleich dieser Zahl sind, sollen gespeichert werden. Macht es hier Sinn, ein Array oder eine Liste zu verwenden? Warum? === Zusatzaufgaben === Weiter an den Aufgaben von ProjectEuler. ===== - Funktionen ===== In Programmen muss oft an unterschiedlichen Stellen der gleiche Code ausgeführt werden. Anstelle von copy-paste bietet es sich an, diesen Code in eine **Funktion** zu verschieben. Ist eine Funktion einmal programmiert, so kann man sie beliebig oft aufrufen. In C# haben Funktionen folgende Form: static MyFunction( ) { # CODE return ; } In jedem C#-Programm kommt eine spezielle Funktion vor: die **Main-Methode** (Methoden und Funktionen sind sehr ähnlich - mehr dazu zu einem späteren Zeitpunkt). Dies ist die Funktion, die beim Ausführen des Programms zuerst ausgeführt wird. Alle weiteren Funktionen sollen //parallel// zur Main-Methode und nicht innerhalb dieser geschrieben werden. Bemerkungen: * Der Datentyp des Outputs muss in der Deklaration der Funktion angegeben werden. Ein passendes **return-Statement** ist dann zwingend. * Funktionen, die **nichts zurückgeben** werden mit **void** gekennzeichnet. * Eine Funktion kann beliebig viele **Inputs** (auch **Argumente** genannt) haben. Beispiele: static void SayHi(string name) { Console.WriteLine("Hello, dear " + name + "!"); // void-function: does something (print to console in this case) // but doesn't return anything } static int Square(int x) { return x * x; // takes one argument (x) of type int, does something with it // and returns result (another int) } static void Main(string[] args) { SayHi("Jerry"); int sq = Square(3); Console.WriteLine(sq); } ==== - Aufgaben G ==== === Aufgabe G1 === In dieser ersten Aufgabe geht es darum, möglichst viele, kurze Funktionen zu schreiben. Rufe sämtliche programmierte Funktionen für verschiedene Inputs auf. - Schreibe eine Funktion `AnswerToEverything`, die keine Argumente entgegennimmt und nichts zurück gibt. Es soll nur die Zahl $42$ in die Konsole geschrieben werden. - Schreibe eine Funktion `Sum`, die zwei ints entgegennimmt und die Summe von diesen berechnet. - Schreibe eine Funktion `isEven` vom Typ Boolean, die einen int entgegen nimmt. Falls dieser gerade (even) ist, wird `true` und ansonsten `false` zurückgegeben. === Aufgabe G2 === Schreibe eine Funktion `FahrenheitToCelsius`, die eine Temperatur als Gleitkommazahl von Fahrenheit in Celsius umrechnet und zurück gibt. Wieviel Grad sind 212 Fahrenheit? Tipp: https://de.wikipedia.org/wiki/Grad_Fahrenheit#Umrechnung === Aufgabe G3 === **Primzahlen immer wieder** - Schreibe eine Funktion `isPrime` vom Typ Boolean, die einen int entgegen nimmt. Die Funktion entscheidet dann darüber, ob die Zahl prim ist oder nicht und gibt einen entsprechenden Output heraus: `true` falls sie prim ist und ansonsten `false`. - Gehe nun in einer Schleife alle Zahlen von 2 bis 100 durch. Entscheide mithilfe der Funktion von oben für jede Zahl, ob sie prim ist oder nicht. Falls ja, gib die Zahl in der Konsole aus. === Aufgabe G4 === Die meisten Programmiersprachen kommen mit vielen vorprogrammierten Utility-Functions daher, die einem das Leben vereinfachen. Ziel dieser Aufgabe ist es, diese Utility-Functions selbst zu programmieren, und dabei nur die rudimentärsten Befehle wie Schleifen oder if-else zu verwenden. Es sollen auch keine Listen sondern nur Arrays verwendet werden. Natürlich sollen selbst programmierte Utility-Functions verwendet werden dürfen. - Funktion `Length`, die die Länge eines Strings bestimmt und zurück gibt. - Funktion `NumberOfChar`, die überprüft, wie oft ein bestimmter Buchstabe (Typ: char) in einem String vorkommt. - Funktion `PositionsOfChar`, die in einem Array sämtliche Positionen eines Buchstaben in einem String angibt. ===== - Projekt: Hangman ===== **Ziel:** Programmiere das bekannte Hangman-Game als Konsolengame. *Tipps:* Definiere, wann sinnvoll, Funktionen. ==== Version 1: Basics ==== 1. Eine Person soll das Wort, welches der Spieler erraten soll, eingeben. Natürlich soll dieses in einer Variablen gespeichert werden. 1. Zeige nun mithilfe von Underlines _ an, aus wie vielen Buchstaben das gesuchte Wort besteht. 1. Der Spieler soll nun Buchstaben raten und eingeben. Kommt ein Buchstabe im gesuchten Wort vor, so wird der entsprechende Underline ersetzt. //Tipp:// Verwende `char c = Console.ReadKey().KeyChar;` um einzelne Buchstaben einzugeben, ohne Enter drücken zu müssen. 4. Gross- und Kleinschreibweise soll keine Rolle spielen. 5. Sobald das gesamte Wort erraten wurde, wird dem Spieler gratuliert. ==== Version 2: Gameplay ==== 1. Zähle mit, wie viele Versuche der Spieler braucht. Teile dem Spieler am Schluss die Anzahl Versuche mit. 1. Lege eine maximale Anzahl versuche fest. Benötigt der Spieler zu viele Versuche, soll Game Over ausgegeben werden. 1. Gibt der Spieler einen Buchstaben erneut ein, soll der Versuch ignoriert werden und nicht als Fehlversuch zählen. ==== Version 3: Grafische Darstellung ==== Ziel ist nun, dass die Anzahl Versuche durch den hänging Man graphisch dargestellt werden. {{ :ef_informatik:hangman.png?200 |}} 1. Definiere dazu eine Funktion, die die Anzahl Versuche entgegen nimmt und die zugehörige Grafik in die Konsole schreibt: 1. 0 - nur Galgen 1. 1 - Kopf 1. 2 - Kopf, Oberkörper 1. 3 - Kopf, Oberkörper, ein Bein 1. 4 - Kopf, Oberkörper, beide Beine 1. 5 - Kopf, Oberkörper, beide Beine, ein Arm 1. 6 - HangMan 1. Verbessere und fine-tune nun dein Spiel so, dass es gut funktioniert und hübsch aussieht. ==== ** Version 4: OOP ==== **Nur für Fortgeschrittene mit Vorwissen in OOP** * Programmiere Hangman mit OOP. * Implementiere mindestens zwei Klassen: * Spielelogik * Ausgabe * Schreibe nach jeder Runde für die Ausgabe nicht den ganzen Bildschirm neu, sondern immer nur die Zeichen, die sich tatsächlich ändern. * Der letzte angefügte Körperteile soll jeweils in einer anderen Farbe angezeigt werden. * Die Klassen dürfen statisch sein. * Eingabe und Programmfluss können direkt in Main() liegen.