~~NOTOC~~ ====== for-loops (for-Schleifen) ====== Schleifen werden verwendet, wenn ein Programmteil mehrfach ausgeführt werden soll. In diesem Abschnitt lernst du for-Schleifen kennen. Später werden while-Schleifen erklärt. Schleifen sind wichtige Kontrollstrukturen; der Fachbegriff in diesem Kontext lautet //Iteration//: Man führt etwas iterativ (= wiederholt) durch. Schau dir das folgende Video zu for-loops an. [[https://fginfo.ksbg.ch/~olaf/videos-glf22/for-loops.mp4|Video "for-loops"]] anfang = int(input("Gib die Anfangszahl ein: ")) ende = int(input("Gib die Endzahl ein: ")) summe = 0 for x in range(anfang, ende + 1): print(x) summe = summe + x print("Die Summe all dieser Zahlen ist " + str(summe) + ".") ===== Wesentliche Inhalte des Videos ===== Ein einfaches Beispiel einer for-Schleife in Python: for i in range(3, 15): print(i) print("Nun ist die Schleife beendet.") Beachte: * Die sogenannte //Laufvariable// ''i'' im obigen Beispiel nimmt nacheinander alle ganzzahligen Werte von 3 bis $14=15-1$ an; der Wert 15 wird **nicht angenommen**. * Statt ''i'' kann man einen beliebigen anderen Variablennamen verwenden, etwa ''laufvariable''. * Der Doppelpunkt '':'' am Ende der Zeile mit dem ''for'' darf nicht vergessen werden. * Der mehrfach auszuführende Code ist vier Zeichen eingerückt. * Wenn man die erste Zeile als kompletten Satz lesen will: * Auf Englisch: "For every ''i'' in the number range between 3 (included) and 15 (excluded) do the following:" * Auf Deutsch: "Für jedes Element ''i'' im Zahlenbereich zwischen 3 (einschliesslich) und 15 (ausgenommen) mache das Folgende:" Der ''range''-Befehl benötigt standardmäßig zwei ganze Zahlen als Parameter, hat also die Form ''range(start, end)''. Er erzeugt eine (Art) Liste aller ganzen Zahlen, die bei ''start'' startet und bei ''end - 1'' endet. Im Video haben wir dies in der Python-Shell mit Befehlen wie ''list(range(3, 10))'' getestet. Der erste Parameter kann optional weggelassen werden und wird dann als ''0'' interpretiert: ''range(10)'' hat dieselbe Bedeutung wie ''range(0, 10)''. ===== Aufgaben zu for-loops ===== ==== Aufgabe 1: Zahlen und ihre Quadrate ==== Schreibe ein Programm, das für alle Zahlen zwischen $1$ und $20$ sowohl die Zahl als auch deren Quadrat ausgibt: Das Quadrat von 1 ist 1. Das Quadrat von 2 ist 4. ... Das Quadrat von 20 ist 400. for zahl in range(1, 21): print("Das Quadrat von " + str(zahl) + " ist " + str(zahl*zahl) + ".") ==== Aufgabe 2: Summe der Quadratzahlen ==== Schreibe ein Python-Programm, das die Summe aller Quadrate der Zahlen von 1 bis ''n'' ausgibt, wobei der Benutzer die Zahl ''n'' eingibt. Beispiel: Der Dialog mit dem Programm bei Eingabe 8 soll wie folgt aussehen: Bis zu welcher Zahl soll ich die Quadrate aufsummieren? 8 Die Summe der Quadrate aller Zahlen von 1 bis 8 ist 204. Bonus-Teil: Ändere dein Programm so, dass als Ausgabe eine Formel ausgegeben wird, etwa $1+2+3+4+5+6+7+8=204$ bei Eingabe 8. Bei dieser Eingabe soll der Dialog also wie folgt aussehen: Bis zu welcher Zahl soll ich die Quadrate aufsummieren? 8 1+2+3+4+5+6+7+8=204 Definiere am Anfang eine String-Variable ''ausgabestring = ""'', die du während der Schleife (und eventuell am Ende) geeignet veränderst und am Ende ausgibst. Innerhalb der for-Schleife kannst du ein if-statement verwenden, damit nicht zu viele Pluszeichen am Ende erscheinen. n = int(input("Bis zu welcher Zahl soll ich die Quadrate aufsummieren? ")) summe = 0 for i in range(1,n + 1): summe = summe + i*i print("Die Summe der Quadrate aller Zahlen von 1 bis " + str(n) + " ist " + str(summe) + ".") n = int(input("Bis zu welcher Zahl soll ich die Quadrate aufsummieren? ")) summe = 0 ausgabestring = "" for i in range(1,n + 1): summe = summe + i*i ausgabestring = ausgabestring + str(i * i) if i < n: ausgabestring = ausgabestring + "+" ausgabestring = ausgabestring + "=" + str(summe) print(ausgabestring) Nicht ganz so schön in der Ausgabe, aber natürlich auch gut: n = int(input("Bis zu welcher Zahl soll ich die Quadrate aufsummieren? ")) summe = 0 ausgabestring = "" for i in range(1,n + 1): summe = summe + i*i ausgabestring = ausgabestring + "+" + str(i * i) print(ausgabestring + "=" + str(summe)) ==== Aufgabe 3: Muster ausgeben ==== Schreibe ein Programm, das eine Zahl als Eingabe entgegennimmt (oder diese am Anfang des Programms in einer Variablen speichert) und dann ein Dreieck der folgenden Form produziert, hier im Fall der Eingabe 6: * ** *** **** ***** ****** Verwende "Rechnen mit Zeichenketten/Strings"! Erzeuge zunächst das folgende Muster und überlege dir dann, wie viele Leerschläge (in Abhängigkeit von der Laufvariablen deiner Schleife) in der entsprechenden Zeile am Anfang einzufügen sind. * ** *** **** ***** ****** n = int(input("Wie viele Zeilen? ")) for i in range(1, n + 1): print((n - i) * " " + i * "*") ==== Aufgabe 4: Slalomtext ==== Schreibe ein Programm, das vom Benutzer einen kurzen Text erfragt und diesen dann 100 Mal wie unten illustriert in Slalom-Form ausgibt (maximale Einrückung im Beispiel unten sind 5 Leerschläge, etwas mehr ist aber auch erlaubt). Damit es nicht zu leicht ist: Du darfst nur eine for-Schleife verwenden. Beliebig viele if-statements sind aber erlaubt! Bemerkung: Wenn du die Ausgabe zeitlich verzögern willst, importiere am Anfang deines Programms mit ''import time'' die dazu nötige Bibliothek und verwende im Programm den Befehl ''time.sleep(0.05)'' für eine Pause von einer zwanzigstel Sekunde. Gib einen kurzen Satz oder Text ein: Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! Schleifen sind cool! ... Verwende zwei Variablen: Eine speichert die aktuelle Einrückung und die andere speichert die Veränderung der Einrückung von Zeile zu Zeile: Sie nimmt nur die Werte ''1'' und ''-1'' und wird verändert, wenn die aktuelle Einrückung minimal (also Null) oder maximal ist. import time text = input("Gib einen kurzen Satz oder Text ein: ") anfangsabstand = 0 d = 1 for i in range(100): print(anfangsabstand * " " + text) anfangsabstand = anfangsabstand + d if anfangsabstand == 13: d = -1 if anfangsabstand == 0: d = 1 time.sleep(0.05) Zum Ausschalten der zeitlichen Verzögerung: Kommentiere die beiden Zeilen mit "time" aus. ==== Aufgabe 5: Ausbreitung von Corona ==== Schreibe ein Programm, das die Anzahl der Neuansteckungen mit Corona simuliert. Am Anfang des Programms sollen aktuelle Anzahl und wöchentliche Zuwachsrate (in Prozent) und Simulationszeit (wie viele Wochen?) als Variablen definiert werden. Hinweis: Um eine Kommazahl (alias float) zu einer ganzen Zahl (alias int) zu runden, verwende den Befeh ''round''. Zum Testen gib etwa ''round(3.49)'' und ''round(3.5)'' in der Python-Shell ein. Die Ausgabe soll beispielsweise wie folgt aussehen: Anzahl der Neuansteckungen heute: 300 Wöchentliche Zunahme: 17% Woche 0: 300 Woche 1: 351 Woche 2: 410 Woche 3: 480 Woche 4: 562 Woche 5: 657 Woche 6: 769 Woche 7: 900 Woche 8: 1053 Woche 9: 1232 Woche 10: 1442 Woche 11: 1687 Woche 12: 1974 neuansteckungen = 300 zunahmeInProzent = 17 simulationsZeit = 12 print("Anzahl der Neuansteckungen heute: " + str(neuansteckungen)) print("Wöchentliche Zunahme: " + str(zunahmeInProzent) + "%") # Zunahme pro Woche als Faktor zunahme = 1 + zunahmeInProzent / 100 kranke = neuansteckungen for w in range(0, simulationsZeit + 1): print("Woche " + str(w) + ": " + str(round(kranke))) kranke = zunahme * kranke Etwas kürzer, aber wohl weniger verständlich: # Alternative: neuansteckungen = 300 zunahmeInProzent = 17 simulationsZeit = 12 print("Anzahl der Neuansteckungen heute: " + str(neuansteckungen)) print("Wöchentliche Zunahme: " + str(zunahmeInProzent) + "%") for w in range(0, simulationsZeit + 1): print("Woche " + str(w) + ": " + str(round(neuansteckungen * (1 + zunahmeInProzent / 100) ** w))) ===== Verschachtelte for-Schleifen ===== Innerhalb einer for-Schleife können weitere for-Schleifen stehen (und auch if-else statements etc). Lass das folgende Programm auf deinem Rechner laufen und verstehe es: for i in range(3): print("Äussere Schleife beginnt, Laufvariable i = " + str(i) + ".") for j in range(3): print(" Innere Schleife beginnt, Laufvariable j = " + str(j) + ".") print(" (i,j) = (" + str(i) + ", " + str(j) + ")") print(" Innere Schleife abgearbeitet.") print("Äussere Schleife abgearbeitet.") Manchmal ist es sinnvoll, einen Ausgabe-String schrittweise aufzubauen, wie im folgenden Python-Programm. Versuche, es zu verstehen. Warum habe ich die "äussere" Laufvariable ''y'' und nicht ''x'' genannt? for y in range(10): s = "" for x in range(10): s = s + "(" + str(x) + ", " + str(y) + "); " print(s) ==== Aufgabe: Kleines Einmaleins bzw. Multiplikationstabelle (mit unschön formatierter Ausgabe) ==== Schreibe ein Programm, das abhängig von einer Variablen ''n'' eine Multiplikationstabelle der Zahlen von $1$ bis $n$ Zahlen ausgibt. Im Fall ''n == 10'' soll die Ausgabe wie folgt aussehen (wir lernen in Bälde, wie man eine "tabellarisch schöne" Ausgabe erzeugt). 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100 Bau den Ausgabe-String für jede Zeile sukzessive in der inneren for-Schleife auf (ähnlich wie im obigen Beispielprogramm). n = 13 for x in range(1, n + 1): s = "" for y in range(1, n + 1): s = s + str(x * y) + " " print(s) ==== Bonus-Aufgabe: Liste von Primzahlen ==== Schreibe ein Programm, das eine Liste aller Primzahlen bis zu einer Zahl ''n'' aus gibt. Gehe mit einer äusseren Schleife alle Zahlen von 2 bis ''n'' durch. Test auf Primalität (= Primzahl-Sein) dieser Zahl (im Codeblock der äusseren Schleife): Verwende eine boolesche Variable ''istPrim = True'' und setze diese in einer inneren Schleife auf ''False'', sobald ein Teiler gefunden wurde (die entsprechende Division hat Rest Null). # Bis zu dieser Zahl wird die Primzahlliste erzeugt. n = 100 # Der Ausgabestring, den wir schrittweise aufbauen s = "" for zahl in range(2, n + 1): # Solange wir keinen Teiler von zahl kennen, tun wir so, als ob es sich um eine Primzahl handelt. istPrim = True for teilerKandidat in range(2, zahl): # Test, ob teilerKandidat ein Teiler von zahl ist: if zahl % teilerKandidat == 0: # Teiler gefunden! zahl ist keine Primzahl. istPrim = False if istPrim == True: s = s + str(zahl) + ", " print(s) Bemerkungen: * Es würde reichen, wenn wir ''teilerKandidat'' nur bis zur (ab)gerundeten Wurzel von ''zahl'' laufen lassen. * Es gibt schnellere Methoden, eine solche Primzahlliste zu erstellen, etwa das Sieb des Eratosthenes. Hier geht es einfach darum zu zeigen, dass wir bereits eine solche Liste erstellen können. ====== f-strings (formatted strings = formatierte Zeichenketten) und end-Parameter beim print-Befehl ====== Schau dir das folgende Video an. [[https://fginfo.ksbg.ch/~olaf/videos-glf22/f-strings-und-end-parameter-bei-print.mp4|Video "f-strings und end-Parameter bei print"]] x = 123456 print(f"Variablenwert ist {x}.") t = f"Variablenwert ist {x}." print(t) t = f"Variablenwert ist {x:20}." # oder gleichbedeutend # t = f"Variablenwert ist {x:>20}." print(t) t = f"Variablenwert ist {x:<20}." print(t) x = "Pinocchio" t = f"Variablenwert ist {x:>20}." print(t) t = f"Variablenwert ist {x:20}." # oder gleichbedeutend # t = f"Variablenwert ist {x:<20}." print(t) x = 1/7 t = f"Variablenwert ist {x:30}." print(t) t = f"Variablenwert ist {x:20.3f}." print(t) # Optionale Angabe des end-Parameters beim print-Befehl. (Was macht der Cursor nach der Ausgabe? Standardmässig geht er in die nächste Zeile.) print("Hallo", end="") print("Pinocchio") print("Hallo", end="XXX") print("Pinocchio") print("Hallo", end="XXX\n") print("Pinocchio") # Standardeinstellung: print("Hallo", end="\n") print("Pinocchio") ===== Aufgaben zu f-strings ===== ==== Aufgabe 1: f-strings in der Python-Shell testen ==== Gehe in die Python-Shell und definiere Variablen ''s="Hallo"'' und ''x = 5'' und ''b = 1/17''. Wenn du nun etwa ''f"{s:15}"'' eingibst, liefert Python als Ergebnis den String ''"Hallo"'' linksbündig in einem Bereich von 15 Leerzeichen. Was musst du eingeben, um als Ergebnis ... * den Wert von ''s'' rechtsbündig in einem Bereich von 15 Leerzeichen zu erhalten? * den Wert von ''x'' rechsbündig bzw. linksbündig in einem Bereich von 10 Leerzeichen zu erhalten? * den Wert von ''b'' rechsbündig bzw. linksbündig in einem Bereich von 25 Leerzeichen zu erhalten? * den Wert von ''b'' auf 7 Nachkommastellen genau rechsbündig bzw. linksbündig in einem Bereich von 25 Leerzeichen zu erhalten? Die gesuchten Befehle sind: f"{s:>15}" f"{x:10}" f"{x:<10}" f"{b:25}" f"{b:<25}" f"{b:25.7f}" f"{b:<25.7f}" Bemerkung: Wenn du nicht mit Variablen arbeiten willst, kannst du statt ''f"{s:15}"'' auch direkt ''f"{'Hallo':15}"'' eingeben. Beachte dabei, dass hier der f-string mit doppelten Anführungszeichen notiert ist und der innere string '''Hallo''' mit einfachen Anführungszeichen. Nicht akzeptiert wird ''f"{"Hallo":15}"'' (da hier nicht klar ist, wo die Definition des f-strings endet). ==== Aufgabe 2: f-strings in for-Schleife ==== Zuvor hast du hoffentlich ein Programm geschrieben, das für alle Zahlen zwischen $1$ und $20$ sowohl die Zahl als auch deren Quadrat ausgibt. Ändere dieses Programm nun mit Verwendung von f-Strings so, dass jede Zahl rechtsbündig in einem Bereich von 3 Leerschlägen steht. Die Ausgabe soll also übersichtlicher als zuvor so aussehen: Das Quadrat von 1 ist 1. Das Quadrat von 2 ist 4. ... Das Quadrat von 20 ist 400. for zahl in range(1, 21): print(f"Das Quadrat von {zahl:2} ist {zahl*zahl:3}.") ==== Aufgabe 3: Formatierte Multiplikationstabelle; verschachtelte for-Schleife und f-Strings ==== Vereinbarung: In den folgenden Tabellen hat jede Zahl einen "Platz von 5 Zeichen". (1) Ändere dein obiges Programm zur Multiplikationstabelle so, dass die Ausgabe im Fall ''n == 10'' wie folgt aussieht: 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100 (2) Bonus-Teil: Verbessere die Ausgabe weiter: Im Fall ''n==10'' soll sie wie folgt aussehen: * | 1 2 3 4 5 6 7 8 9 10 --------------------------------------------------------- 1 | 1 2 3 4 5 6 7 8 9 10 2 | 2 4 6 8 10 12 14 16 18 20 3 | 3 6 9 12 15 18 21 24 27 30 4 | 4 8 12 16 20 24 28 32 36 40 5 | 5 10 15 20 25 30 35 40 45 50 6 | 6 12 18 24 30 36 42 48 54 60 7 | 7 14 21 28 35 42 49 56 63 70 8 | 8 16 24 32 40 48 56 64 72 80 9 | 9 18 27 36 45 54 63 72 81 90 10 | 10 20 30 40 50 60 70 80 90 100 n = 13 for x in range(1, n + 1): s = "" for y in range(1, n + 1): s = s + f"{x*y:5}" print(s) n = 13 for x in range(1, n + 1): for y in range(1, n + 1): print(f"{x*y:5}", end = "") print() n = 13 s = " * |" # oder eleganter (beachte die verschiedenen Anführungszeichen!) # s = f"{'*':>5} |" for y in range(1, n + 1): s = s + f"{y:5}" print(s) print((5 + 2 + n * 5) * "-") for x in range(1, n + 1): s = f"{x:5} |" for y in range(1, n + 1): s = s + f"{x*y:5}" print(s) n = 13 print(" * |", end = "") # oder eleganter (beachte die verschiedenen Anführungszeichen!) # print(f"{'*':>5} |", end = "") for y in range(1, n + 1): print(f"{y:5}", end = "") print() print((5 + 2 + n * 5) * "-") for x in range(1, n + 1): print(f"{x:5} |", end = "") for y in range(1, n + 1): print(f"{x*y:5}", end = "") print() Eigentlich sollte der Abstand (den wir hier als 5 festgelegt haben) abhängig von der Länge (als String) des längsten Produkts gewählt werden. Dies geht auch mit f-strings, wurde aber bisher nicht erklärt. Das Programm (mit sukzessivem Aufbau der Ausgabestrings) würde dann so aussehen: n = 33 # Bemerkung: Der Befehl "len" liefert die Länge eines Strings. abstand = len(str(n*n)) + 1 s = f"{'*':>{abstand}} |" for y in range(1, n + 1): s = s + f"{y:{abstand}}" print(s) print((abstand + 2 + n * abstand) * "-") for x in range(1, n + 1): s = f"{x:{abstand}} |" for y in range(1, n + 1): s = s + f"{x*y:{abstand}}" print(s) Beachte die zusätzlichen geschweiften Klammern um den "Platzparameter" ''abstand'' in den f-strings. Mir ist nicht ganz klar, warum diese verlangt werden; man könnte dies aber so begründen: Python erwartet als "Platzparameter" eine Zahl; wenn man einen auszuwertenden Ausdruck angeben will, braucht man diese Zusatzklammern. Teste dies in der Python-Shell: Definiere ''abstand = 5''. Dann führt ''f"{20:abstand}"'' zu einem Fehler, während ''f"{20:{abstand}}"'' oder ''f"{20:{abstand+3}}"'' das gewünschte Resultat liefern. ====== Bonusmaterial: range-Befehl mit Schrittweite und print-Befehl mit mehreren Argumenten und Zusatzparameter sep ====== ===== range-Befehl mit Schrittweite ===== Teste in der Python-Shell die folgenden Befehle und versuche, das Ergebnis zu verstehen! * ''list(range(-5))'' * ''list(range(5, 5))'' * ''list(range(4, 5))'' * ''list(range(-5, 7))'' * ''list(range(4, 10, 2))'' * ''list(range(10, 4, 2))'' * ''list(range(10, 4, -1))'' * ''list(range(10, 4, -2))'' * ''list(range(10, 3, -2))'' In der 1-Parameter-Variante von Range: Der Endparameter sollte positiv sein, sonst ist der Bereich leer. In der 2-Parameter-Variante von Range: Der Startparameter sollte kleiner als der Endparameter sein, sonst ist der Bereich leer. Es gibt auch die 3-Parameter Variante ''range(start, end, step)'', wobei ''step'' die Schrittweite angibt. Hier sind auch negative Schrittweiten erlaubt, was natürlich nur im Fall ''start''>''end'' sinnvoll ist. * ''range(15)'' und ''range(0,15)'' und ''range(0,15,1)'' haben dieselbe Bedeutung. * ''range(3, 15)'' und ''range(3, 15, 1)'' haben dieselbe Bedeutung. Stets kann man versuchen, sich im Internet schlau zu machen, wenn etwas unklar ist. Die Kunst ist hier, die richtigen (englischen) Suchbegriffe zu finden. Manchmal ist dies relativ verständlich: * [[https://docs.python.org/3/library/stdtypes.html?highlight=range#ranges|''range''-Erklärung auf der offiziellen Python-Seite]] * [[https://www.w3schools.com/python/ref_func_range.asp|''range''-Erklärung auf W3Schools]] Manchmal (aktuell vermutlich) eher nicht: * [[https://docs.python.org/3/reference/compound_stmts.html#the-for-statement|Erklärung der for-Schleife auf der offiziellen Python-Seite]] * [[https://www.w3schools.com/python/python_for_loops.asp|Erklärung der for-Schleife auf W3Schools]] ===== print mit mehreren Ausgabeargumenten und Parameter sep ===== Teste das folgende Python-Programm und finde heraus, was du dabei lernen sollst! print("Du bist", 999, "Jahre alt.") x = 3 y = 7 z = 13 print(x, y, z) print(x, y, z, sep = " | ") print("Du bist", 999, "Jahre alt.") print("Du bist", 999, "Jahre alt.", sep="") print("Du bist", 999, "Jahre alt.", sep="---") for i in range(1, 6): print(i, i*i, i*i*i, i*i*i*i, sep = " | ", end = " |\n") # ... wobei Letzteres (verschönert) einfacher mit einem einzigen f-string geht: for i in range(1, 6): print(f"{i:4} | {i * i:4} | {i * i * i:4} | {i * i * i * i:4} |") Der ''print''-Befehl kann auch mehrere Argumente entgegennehmen: Er gibt diese dann nacheinander, durch einen Leerschlag getrennt, aus. Mit dem Zusatzparameter ''sep'' (als Abkürzung für "separation" = "Trennung", "Abschottung") kann man angeben, welcher String zwischen den Argumenten ausgegeben wird (Standardwert von ''sep'' ist ein Leerschlag). Wie oben bereits erklärt, kann man mit dem Zusatzparameter ''end'' angeben, mit welchem String die Ausgabe abgeschlossen wird (und ob ein oder mehrere Zeilenwechsel stattfinden). ===== Link zur Kursseite ===== [[lehrkraefte:snr:informatik:glf22|Zur Kursseite]]