Imperative Programmierung mit Python Klaus Becker 2012 2 Imperative Programmierung 3 Teil 0 Vorbemerkungen zu Python 4 Entwicklungsgeschichte Die Sprache wurde Anfang der 1990er Jahre von Guido van Rossum am Centrum voor Wiskunde en Informatica (Zentrum für Mathematik und Informatik) in Amsterdam als Nachfolger für die Programmier-Lehrsprache ABC entwickelt, ursprünglich für das verteilte Betriebssystem Amoeba. Alle bisherigen Implementierungen der Sprache übersetzen den Text eines Python-Programms transparent in einen Zwischencode, der dann von einem Interpreter ausgeführt wird. Der Name geht nicht etwa (wie das Logo vermuten ließe) auf die gleichnamige Schlangengattung Pythons zurück, sondern bezog sich ursprünglich auf die englische Komikertruppe Monty Python. In der Dokumentation finden sich daher auch einige Anspielungen auf Sketche aus dem Flying Circus. Trotzdem etablierte sich die Assoziation zur Schlange, was sich u. a. in der Programmiersprache Cobra sowie dem Python Toolkit „Boa“ äußert. Quelle: Wikipedia 5 Python - Ziele Python wurde mit dem Ziel entworfen, möglichst einfach und übersichtlich zu sein. Dies soll durch zwei Maßnahmen erreicht werden: Zum einen kommt die Sprache mit relativ wenigen Schlüsselwörtern aus, zum anderen ist die Syntax reduziert und auf Übersichtlichkeit optimiert. Dies führt dazu, dass Python eine Sprache ist, in der man schnell, einfach und leicht programmieren kann. Sie ist daher besonders dort geeignet, wo Übersichtlichkeit und Lesbarkeit des Codes eine herausragende Rolle spielen – z. B. in der Teamarbeit, bei Beschäftigung mit dem Quelltext nach längeren Pausen oder bei Programmieranfängern. Durch die Möglichkeit, auch Programme anderer Sprachen als Modul einzubetten, werden viele Nischen in der Programmierung abgedeckt. Bei Bedarf lassen sich so beispielsweise zeitkritische Teile durch maschinennah in C programmierte Routinen ersetzen, oder Python kann als Skriptsprache eines anderen Programms dienen (Beispiele: OpenOffice.org, Blender, Maya, PyMOL, SPSS und GIMP). Quelle: Wikipedia 6 Python - Ziele Python ist eine Multiparadigmensprache. Das heißt, es zwingt den Programmierer nicht zu einem einzigen bestimmten Programmierparadigma, sondern erlaubt es, das für die jeweilige Aufgabe am besten geeignete Paradigma zu wählen. Objektorientierte und strukturierte Programmierung werden vollständig unterstützt, weiterhin gibt es Spracheigenschaften für funktionale und aspektorientierte Programmierung. Quelle: Wikipedia Quelle: http://kamelopedia.mormo.org/index.php/Eierlegende_Wollmilchsau Interne Abläufe 7 Der Quelltext eines Python Programms wird mit einem Texteditor geschrieben (z.B. Idle oder PyScripter). Compiler Interpreter Der Python-Compiler erzeugt einen (maschinenunabhängigen) Byte-Code. Der Compiler entscheidet selbst, ob der Byte-Code nur als Zwischenprodukt im Arbeitsspeicher erzeugt wird, oder ob er auch als .pyc-Datei gespeichert wird. Der Python-Interpreter führt den vom Compiler erzeugten Byte-Code aus. Derselbe Byte-Code kann auf verschiedenen Plattformen ausgeführt werden, sofern diese einen PythonInterpreter zur Verfügung stellen. 8 Gängige Python-Versionen http://www.python.org/download/ http://portablepython.com/releases/ 9 Entwicklungsumgebung Idle 10 Entwicklungsumgebung PyScripter 11 Literatur Johannes Ernesti, Peter Kaiser: Python 3. Das umfassende Handbuch. Galileo Computing 2009. (Preis: 40 €) Michael Weigend: Objektorientierte Programmierung mit Python. mitp 2008. (Preis: 40 €) Michael Weigend: Python Ge-Packt. mitp 2006. (Preis: 16 €) Thomas Theis: Einstieg in Python 3. Galileo Computing 2009. (Preis: 25 €) Gregor Lingl: Python für Kids. bhv 2008. (Preis: 20 €) P. Barry, D. Griffiths: Programmieren von Kopf bis Fuß. O´Reilly 2010 (Preis: 20 €) ... 12 Materialien Internet: Python Official Website http://www.python.org/ Python-Tutorium von Guido van Rossum: http://starship.python.net/crew/gherman/publications/tut-de/tut-de-21.pdf http://starship.python.net/crew/gherman/publications/tut-de/online/tut/ offenes eBook von von Peter Kaiser und Johannes Ernesti (Python 2.5): http://openbook.galileocomputing.de/python/?GalileoSession=10541258A3Vg6VBUX8A PythonWiki: http://wiki.python.de/ Python-Kurs von W.Spiegel: http://www.wspiegel.de/pykurs/pykurs.htm Python, Programmieren macht Spaß : http://www.thomas-guettler.de/vortraege/python/einfuehrung.html BICS: http://schule.de/bics/inf2/programmiersprachen/python/ Unterrichtsmaterialien von Klaus Merkert: http://www.hsg-kl.de/faecher/inf/python/index.php Unterrichtsmaterialien auf www.inf-schule.de 13 Teil 1 Populationsentwicklung Datenverwaltung mit Variablen 14 Populationsentwicklung Zielsetzung: Im Folgenden soll die Entwicklung dieser Mäusepopulation simuliert werden. Ziel ist es, sich mit Hilfe automastisierter Berechnungen einen Überblick über die Größe und Zusammensetzung einer sich fortpflanzenden Mäusepopulation zu verschaffen. Modellannahmen: Unterteilung in drei Alterklassen: junge Mäuse, erwachsene Mäuse und alte Mäuse. In jedem Schritt erfolgt ein Wechsel der Altersklasse: Junge Mäuse werden erwachsen, erwachsene werden alt (und alte Mäuse leider nicht mehr jung). Nur ein bestimmter Anteil (siehe Diagramm) erreicht die nächste Altersstufe. Im Diagramm sind zusätzlich die Geburtenraten eingetragen. Wir gehen davon aus, dass jede erwachsene Maus (im Durchschnitt) vier junge Mäuse erzeugt und dass jede alte Maus (im Durchschnitt) zwei junge Mäuse erzeugt. Populationsentwicklung 15 Aufgabe: Berechne die Populationswerte (ohne Python). Zur Kontrolle: Nach 5 Schritten besteht die gesamte Population aus knapp 300 Mäusen. Schritt jung erwachsen alt 0 6 9 12 1 60 = 4*9+2*12 3 = 6:2 3 = 9:3 2 3 4 5 ... 16 Python als Taschenrechner >>> >>> >>> >>> 6 >>> 9 >>> 12 >>> >>> >>> ... jung = 6 erwachsen = 9 alt = 12 jung erwachsen alt alt = erwachsen // 3 erwachsen = jung // 2 jung = erwachsen*4 + alt*2 Aufgabe: (a) Probiere das selbst aus. Versuche auch zu verstehen, wie die ausgegebenen Zahlenwerte zustande kommen. (b) Irgend etwas stimmt aber nicht. Eigentlich sollten es nach einem Simulationsschritt 60 junge Mäuse sein. Woran liegt es, dass hier ein falsches Ergebnis herauskommt? (c) Benutze Hilfsvariablen, um noch benötigte Zahlenwerte zwischenzuspeichern. (d) Kann man die Berechnungen auch mit Hilfe einer einzigen Hilfsvariablen durchführen? 17 Python als Taschenrechner >>> >>> ??? >>> >>> ??? >>> >>> ??? >>> >>> ??? schritt = 0 schritt schritt = schritt + 1 schritt schritt = schritt + 1 schritt schritt = schritt + 1 schritt Aufgabe: (a) Die Gesamtanzahl der Mäuse soll auch verwaltet werden. Führe hierzu eine geeignete Variable und geeignete Berechnungen ein. (b) Zusätzlich soll auch noch der jeweilige Schrittzahl verwaltet werden. Teste den oben gezeigten Python-Dialog. Kannst du die Ergebnisse erklären, die anstelle der Fragezeichen auftreten? Python als Taschenrechner 18 >>> >>> >>> >>> 6 >>> 9 >>> 12 jung = 6 erwachsen = 9 alt = 12 jung erwachsen alt ??? >>> jung 60 >>> erwachsen 3 >>> alt 3 Python-Dialog >>> Aufgabe: Bei der Berechnung des nächsten Populationszustands haben wir ein etwas merkwürdig aussehendes Divisionszeichen // benutzt. Teste, wie es sich auswirkt, wenn man anstatt des Divisionszeichens // das geläufigere Zeichen / benutzt. Was stellst du fest? Aufgabe: Führe auch Python-Dialoge wie den unten gezeigten aus. Inwiefern tragen die hier erzielten Ergebnisse zur Erklärung des unterschiedlichen Verhaltens der beiden Divisionsoperatoren // und / bei? >>> jung = 11 >>> type(jung) <class 'int'> >>> erwachsen = jung // 2 >>> type(erwachsen) <class 'int'> 19 Fachkonzept - Variable Variablen dienen in der Informatik dazu, Daten zu verwalten. Eine Variable ist ein Name, der (in der Regel) mit einem Datenobjekt verknüpft ist. Variable Wert jung 6 erwachsen 9 alt 12 Variablenzustand Als Wert einer Variablen wird das Datenobjekt angesehen, das von der Variablen verwaltet wird. Ein Variablenzustand beschreibt die aktuell vorliegenden Variablen mit den verwalteten Daten. Fachkonzept - Zuweisung 20 Mit einer Zuweisung kann man eine Variable mit einem neuen Datenobjekt verknüpfen. Zustand - vorher Zuweisungszeichen alt = erwachsen // 3 {jung -> 6; erwachsen -> 9; alt -> 12} alt = erwachsen // 3 Auswertung alt -> Variable Term 3 {jung -> 6; erwachsen -> 9; alt -> 3} Zustand - nachher Aufbau einer Zuweisung: Eine Zuweisung besteht aus einer Variablen (der ein Wert zugewiesen wird) and einem Term (der den zuzuweisenden Wert festlegt). Auswertung einer Zuweisung: Erst wird der Wert des Terms mit Hilfe des aktuellen Variablenzustands ermittelt. Dieser Wert wird dann der Variablen als neuer aktueller Wert zugewiesen. Trace-Tabelle 21 Eine Trace-Tabelle protokolliert die Veränderungen von Variablenzuständen, wenn mehrere Zuweisungen (bzw. sonstige Anweisungen) ausgeführt werden. Das folgende Beispiel zeigt eine solche Trace-Tabelle. Zuweisung schritt jung erwachsen alt hilf schritt = 0 0 jung = 6 0 6 erwachsen = 9 0 6 9 alt = 12 0 6 9 12 schritt = schritt + 1 1 6 9 12 hilf = erwachsen * 4 + alt * 2 1 6 9 12 60 alt = erwachsen // 3 1 6 9 3 60 erwachsen = jung // 2 1 6 3 3 60 jung = hilf 1 60 3 3 60 Variablenbezeichner 22 Bei der Wahl der Variablenbezeichner (d.h. der Namen, die man den Variablen gibt) ist man recht flexibel: Verwendung von Buchstaben, Ziffern und dem Unterstrich; Unterscheidung zwischen Groß- und Kleinschreibung; keine Ziffern am Anfang; Umlaute sind erlaubt, machen aber gelegentlich Schwierigkeiten. Bei der Wahl der Variablenbezeichner sollte man sprechende Namen benutzen. Das sind Namen, die möglichst gut erklären, welche Daten mit den Variablen verwaltet werden. >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> a = 20 b = 12 c=a+b d=a a = a // 2 + 4 * b b = d // 2 + b // 2 c=a+b d=a a = a // 2 + 4 * b b = d // 2 + b // 2 c=a+b Aufgabe: Die Folge von Zuweisungen soll die Entwicklung einer Population beschreiben. Beurteile die Wahl der Variablenbezeichner? Exkurs: Was ist eine Variable? 23 Variablen dienen in der Informatik dazu, Daten zu verwalten. Eine Variable ist ein Name, der mit einer Speicherzelle verknüpft ist. Mit der Variable kann man auf den in der zugehörigen Speicherzelle abgelegten Datenwert zugreifen. "Behältersemantik" Speicherzelle mit Datenwert Name Variablen dienen in der Informatik dazu, Daten zu verwalten. Eine Variable ist ein Name, der (in der Regel) mit einem Datenobjekt verknüpft ist. "Zeigersemantik" Name Zeiger Datenobjekt Variablen dienen in der Informatik dazu, Daten zu verwalten. Eine Variable ist ein Name, der (in der Regel) mit einem Wert verknüpft ist. "Wertsemantik" {jung -> 6; erwachsen -> 9; alt -> 12} Name Datenwert Exkurs: Variablen in Python 24 Variablen dienen in der Informatik dazu, Daten zu verwalten. Eine Variable ist ein Name, der (in der Regel) mit einem Datenobjekt verknüpft ist. "Zeigersemantik" Name Zeiger Datenobjekt Jedes Datenobjekt in Python hat eine Identitätsnummer, einen Typ und einen Wert. Die Identitätsnummer ist die Adresse des Objekts im Speicher. Sie ist also eine Zahl, mit der man ein Datenobjekt eindeutig identifizieren kann. >>> id(2) 505300136 >>> type(2) <class 'int'> >>> 2 2 Eine Variable ist ein Name, der (in der Regel) mit einem Datenobjekt verknüpft ist. Die Verknüpfung wird durch einen Verweis (Referenz) auf die Speicheradresse des Datenobjekts hergestellt. >>> id(2) 505300136 >>> zahl = 2 >>> id(zahl) 505300136 >>> type(zahl) <class 'int'> >>> zahl 2 25 Exkurs: Zuweisungen in Python Eine Veränderung eines Variablenwerts kann mit Hilfe einer Zuweisung erfolgen. Erst wird der Wert des Terms (auf der rechten Seite der Zuweisung) mit Hilfe des aktuellen Variablenzustands ermittelt. Dann wird ein Datenobjekt mit diesem Wert an die Variable (auf der linken Seite der Zuweisung) gebunden. Dieses Datenobjekt kann ein bereits existierendes Datenobjekt sein oder ein neu erzeugtes. Python entscheidet nach internen Strategien, welche Version günstiger ist. Je nach Programmablaufsituation können gleiche Zuweisungen durchaus zu unterschiedlichen Datenobjektkonstellationen führen. Entscheidend ist nur, dass der Variablen ein Datenobjekt zugeordnet wird, das den gewünschten Datenwert hat. Beachte: Auch wenn zwei Variablen denselben Wert haben, müssen sie nicht auf dasselbe Datenobjekt verweisen. >>> a = 2 >>> id(a) 505300136 >>> b = a >>> id(b) 505300136 [variable] = [term] Struktur Auswertung >>> a = "Test" >>> b = "Test" >>> id(a) 27764768 >>> id(b) 27757280 26 Exkurs: Mehrfachzuweisungen in Python Python erlaubt Zuweisungen der Gestalt [variablentupel] = [termtupel]. >>> (jung, erwachsen, alt) = (6, 9, 12) >>> jung 6 >>> erwachsen 9 >>> alt Variablentupel Termtupel 12 >>> (jung, erwachsen, alt) = (erwachsen*4+alt*2, jung//2, erwachsen//3) >>> (jung, erwachsen, alt) (60, 3, 3) 27 Ein erstes Programm Wenn man sich bei einem Dialog im Ausführfenster vertippt, muss man neu anfangen. >>> jung = 6 >>> erwachsen = 9 >>> alt = 12 >>> hilf = erwachen*4 + alt*2 ... hilf = erwachen*4 + alt*2 NameError: name 'erwachen' is not defined >>> >>> jung 60 >>> erwachsen 3 >>> alt 3 Ausführfenster Tippfehler Programmfenster jung = 6 erwachsen = 9 alt = 12 hilf = erwachsen*4 + alt*2 alt = erwachsen // 3 erwachsen = jung // 2 jung = hilf Aufgabe: Öffne ein Programmfenster und gib die Anweisungen zur Berechnung der Entwicklung der Mäusepopulation hier ein. Speichere die Anweisungen in einer Datei ab. Mit einen Run-Befehl kann man jetzt die Anweisungen von Python ausführen lassen. Anschließend kann man sich im Ausführfenster die aktuell von den Variablen verwalteten Werte anzeigen lassen. 28 Benutzerfreundliche Programme Die bisher betrachteten Programme waren nicht benutzerfreundlich angelegt. Die Ausgangspopulation konnte nicht vom Benutzer vorgegeben werden, sondern war stets im Programm fest vorgegeben. Die erreichte Endpopulation musste vom Benutzer im Ausführfenster Schritt für Schritt abgefragt werden. Ziel ist es, Programme etwas benutzerfreundlicher anzulegen. Benutzereingaben Ausgaben 29 Ausgabe von Daten Aufgabe: Ändere das bisher entwickelte Programm zur Mäusepopulation so ab, dass folgende Ausgabe im Ausführfenster erscheint: >>> Die berechneten Populationswerte sind: Anzahl junger Maeuse: 60 Anzahl erwachsener Maeuse: 3 Anzahl alter Maeuse: 3 Ausführfenster Bearbeite hierzu die Aufgaben auf inf-schule 1.6.1.1.7. 30 Eingabe von Daten Aufgabe: Ergänze das bisher entwickelte Programm zur Mäusepopulation so, dass der Benutzer selbst die Ausgangspopulation eingeben kann. Im Ausführfenster soll z.B. folgender Ein-/AusgabeDialog erscheinen. >>> Gib die Werte der Ausgangspopulation ein: Anzahl junger Maeuse: 20 Anzahl erwachsener Maeuse: 18 Anzahl alter Maeuse: 4 Ausführfenster Die berechneten Populationswerte sind: Anzahl junger Maeuse: 80 Anzahl erwachsener Maeuse: 10 Anzahl alter Maeuse: 6 Bearbeite hierzu die Aufgaben auf inf-schule 1.6.1.1.8. Fachkonzept - Programm 31 Ein (Python-) Programm ist eine Folge von (Python-) Anweisungen und Kommentaren. Der Programmtext wird auch Quelltext genannt. # -*- coding: utf8 -*# Eingabe print('Gib die Werte der Ausgangspopulation ein:') jung = int(input('Anzahl junger Maeuse: ')) erwachsen = int(input('Anzahl erwachsener Maeuse: ')) alt = int(input('Anzahl alter Maeuse: ')) # Verarbeitung hilf = erwachsen*4 + alt*2 alt = erwachsen // 3 erwachsen = jung // 2 jung = hilf # Ausgabe: print('') print('Die berechneten Populationswerte sind:') print('Anzahl junger Maeuse: ', jung) print('Anzahl erwachsener Maeuse: ', erwachsen) print('Anzahl alter Maeuse: ', alt) Quelltext Jede Anweisung wird im Quelltext in eine neue Zeile geschrieben. Die Verständlichkeit eines Programms wird durch sogenannte sprechende Bezeichner deutlich erhöht. Ein sprechende Bezeichner ist ein Name (z. B. für eine Variable), der die Bedeutung des bezeichneten Gegenstands möglichst gut wiedergibt. Kommentare dienen dazu, die Bedeutung von Programmteilen zu erläutern. Kommentare werden eigentlich nur für die Menschen ergänzt, die den Quelltext bearbeiten. Bei der Ausführung von Programmen werden sie ignoriert. 32 Fachkonzept - EVA-Prinzip Viele Programme lassen sich wie im folgenden Beispiel nach dem EVA-Prinzip strukturieren. EVA steht hier für Eingabe - Verarbeitung - Ausgabe. Auf einen Eingabeteil folgt ein Verarbeitungsteil und schließlich ein Ausgabeteil. Eingabe Verarbeitung Ausgabe # Eingabe ... jung = int(input("Anzahl junger Maeuse: ")) ... # Verarbeitung schritt = 0 ... # Ausgabe ... print('Anzahl junger Maeuse: ', jung) ... Eingaben in Python: Der input-Operator gibt zunächst den Aufforderungstext aus, wartet dann, bis der Benutzer seine Eingabe mit der Return-Taste abgeschlossen hat und liefert diese Eingabe als Zeichenkette vom Typ str zurück. Ausgaben in Pythen: Die print-Anweisung gibt alle übergebenen Werte der Reihe nach (in einer Zeile) auf dem Bildschirm aus. Fehler in Programmen 33 Jeder macht Fehler! # Eingabe print('Gib die Werte der Ausgangspopulation ein:') jung = input('Anzahl junger Maeuse: ') erwachsen = input('Anzahl erwachsener Maeuse: ') alt = input('Anzahl alter Maeuse: ' # Verarbeitung hilf = erwachsen*4 + alt*2 alt = erwachsen // 3 erwachen = jung // 2 jung = hilf # Ausgabe: print('') print('Die berechneten Populationswerte sind:') print('Anzahl junger Maeuse: ', jung) print('Anzahl erwachsener Maeuse: ', erwachsen) print('Anzahl alter Maeuse: ', alt) Programm mit Fehlern Aufgabe: Teste, wie sich das Programm verhält. Findest du die Fehler? Worin unterscheiden sie sich? 34 Fehler in Programmen Bei einem Syntaxfehler hat man eine Regel der gewählten Programmiersprache nicht beachtet. Programmfenster Aufgabe: Welcher Syntaxfehler wird hier angezeigt? 35 Fehler in Programmen Laufzeitfehler treten während der Abarbeitung des Programms (zur Laufzeit) auf. # Eingabe Aufgabe: print('Gib die Werte der Ausgangspopulation ein:') Warum kommt es hier zu jung = input('Anzahl junger Maeuse: ') einem Laufzeitfehler? erwachsen = input('Anzahl erwachsener Maeuse: ') alt = input('Anzahl alter Maeuse: ') # Verarbeitung hilf = erwachsen*4 + alt*2 alt = erwachsen // 3 erwachen = jung // 2 Ausführfenster jung = hilf # Ausgabe: >>> print('') Gibsind:') die Werte der Ausgangspopulation ein: print('Die berechneten Populationswerte print('Anzahl junger Maeuse: ', jung) Anzahl junger Maeuse: 6 Anzahl erwachsener Maeuse: 9 print('Anzahl erwachsener Maeuse: ', erwachsen) Anzahl alter Maeuse: 12 print('Anzahl alter Maeuse: ', alt) Traceback (most recent call last): File ... Programmfenster alt = erwachsen // 3 TypeError: unsupported operand type(s) for //: 'str' and 'int' 36 Fehler in Programmen Logische Fehler liegen vor, wenn das System nicht die beabsichtigten Ausgaben erzeugt. # Eingabe Aufgabe: print('Gib die Werte der Ausgangspopulation ein:') Welcher logische Fehler liegt jung = int(input('Anzahl junger Maeuse: ')) hier vor? erwachsen = int(input('Anzahl erwachsener Maeuse: ')) alt = int(input('Anzahl alter Maeuse: ')) # Verarbeitung Ausführfenster hilf = erwachsen*4 + alt*2 alt = erwachsen // 3 >>> erwachen = jung // 2 Gib die Werte der Ausgangspopulation ein: jung = hilf Anzahl junger Maeuse: 6 # Ausgabe: Anzahl erwachsener Maeuse: 9 print('') Anzahl print('Die berechneten Populationswerte sind:')alter Maeuse: 12 print('Anzahl junger Maeuse: ', jung) Die berechneten Populationswerte sind: print('Anzahl erwachsener Maeuse: ', erwachsen) Anzahl junger Maeuse: 60 print('Anzahl alter Maeuse: ', alt) Anzahl erwachsener Maeuse: 9 Anzahl alter Maeuse: 3 Programmfenster >>> globals() {..., 'erwachsen': 9, 'jung': 60, ..., 'erwachen': 3, ..., 'alt': 3, ..., 'hilf': 60} 37 Datentypen Zu verarbeitende Daten können von ganz unterschiedlichem Typ sein, z. B. Zahlen, mit denen man rechnen kann, oder Zeichenketten, die man hintereinanderhängen kann. Mit dem Begriff "Datentyp" erfasst man in der Informatik diese unterschiedlichen Typen von Daten. # Eingabe eingabe = input('Anzahl junger Maeuse: ') print(type(eingabe)) jung = int(eingabe) print(type(jung)) # Verarbeitung erwachsen = jung // 2 print(type(erwachsen)) # Ausgabe: ausgabe = 'Anzahl erwachsener Maeuse: ' + str(erwachsen) print(type(ausgabe)) print(ausgabe) Programm mit Ausgabe von Datentypen Aufgaben: (a) Im Testprogramm werden auch die Datentypen der von den Variablen verwalteten Daten ausgegeben. Führe das Programm aus. Erkläre die vom Programm gemachten Ausgaben, wenn du eine Zahl eingibst. (b) Welche Rolle spielen hier der int-Operator und der str-Operator? 38 Fachkonzept - Datentyp Ein Datentyp beschreibt eine Menge von Datenobjekten, die alle die gleiche Struktur haben und mit denen die gleichen Operationen ausgeführt werden können. >>> jung = 6 >>> jung 6 >>> type(jung) <class 'int'> >>> erwachsen = jung // 2 >>> erwachsen 3 >>> type(erwachsen) <class 'int'> >>> erwachsen = jung / 2 >>> erwachsen 3.0 >>> type(erwachsen) <class 'float'> # Eingabe eingabe = input('Anzahl junger Maeuse: ') print(type(eingabe)) jung = int(eingabe) print(type(jung)) # Verarbeitung erwachsen = jung // 2 print(type(erwachsen)) # Ausgabe: ausgabe = 'Anzahl erwachsener Maeuse: ' + str(erwachsen) print(type(ausgabe)) print(ausgabe) >>> Anzahl junger Maeuse: 6 <class 'str'> <class 'int'> <class 'int'> <class 'str'> Anzahl erwachsener Maeuse: 3 39 Exkurs: Datentypen in Python >>> 2 2 Datentyp: ganze Zahl int >>> 2.0 2.0 Datentyp: Dezimalzahl float >>> True True Datentyp: Wahrheitswert bool Datentyp: Zeichenkette string >>> ('Hans', 'Meier', 34, 'Koblenz') ('Hans', 'Meier', 34, 'Koblenz') Datentyp: Tupel tuple >>> [1, 2, 3, 4, 5] [1, 2, 3, 4, 5] Datentyp: Liste list ... ... >>> 'Hallo!' 'Hallo!' >>> "Hallo!" 'Hallo!' 40 Exkurs: Typumwandlungen in Python Häufig benötigt man Operatoren, mit denen man aus einem Datenobjekt ein entsprechendes mit einem anderen Datentyp erzeugen kann. Der folgende Python-Dialog zeigt einige Möglichkeiten auf. >>> 3 >>> 3.0 >>> 3 >>> 3 >>> 3.0 >>> '3' int("3") float("3") int(3.0) int(3.5) float(3) str(3) >>> list("[1, 2, 3]") ['[', '1', ',', ' ', '2', ',', ' ', '3', ']'] >>> eval("[1, 2, 3]") [1, 2, 3] 41 Wiederholte Berechnungen # Eingabe ... # Verarbeitung schritt = 0 hilf = erwachsen*4 + alt*2 alt = erwachsen // 3 erwachsen = jung // 2 jung = hilf schritt = schritt + 1 hilf = erwachsen*4 + alt*2 alt = erwachsen // 3 erwachsen = jung // 2 jung = hilf schritt = schritt + 1 hilf = erwachsen*4 + alt*2 alt = erwachsen // 3 erwachsen = jung // 2 jung = hilf schritt = schritt + 1 ... # Ausgabe: ... Programm mit einer Folge von Zuweisungen Zur Simulation der Entwicklung der Mäusepopulation haben wir bisher ein Programm entwickelt, das die Berechnungen für einen einzigen Simulationsschritt durchführt. Interessanter wird es, wenn die Simulation über mehrere Schritte laufen soll. Aufgabe: Wie müsste man das Programm ergänzen, wenn genau 10 (bzw. 100) Simulationsschritte durchgeführt werden sollen? Warum ist der gezeigte Lösungsansatz nicht sonderlich praktisch? 42 Wiederholte Berechnungen # Eingabe jung = int(input("Anzahl der jungen Maeuse: ")) erwachsen = int(input("Anzahl der erwachsenen Maeuse: ")) alt = int(input("Anzahl der alten Maeuse: ")) Aufgaben: # Verarbeitung schritt = 0 while schritt < 10: hilf = erwachsen*4 + alt*2 alt = erwachsen // 3 erwachsen = jung // 2 jung = hilf schritt = schritt + 1 (b) Ändere das gezeigte Programm so ab, dass folgende Aufgaben erledigt werden können: # Ausgabe: print("Neue Populationswerte:") print("Anzahl der Schritte: ", schritt) print("Anzahl junger Maeuse: ", jung) print("Anzahl erwachsener Maeuse: ", erwachsen) print("Anzahl alter Maeuse: ", alt) - Der Benutzer kann selbst eingeben, wie viele Schritte simuliert werden sollen. Programm mit Wiederholungsanweisung (a) Erkläre, warum der Simulationsschritt hier genau 10 mal durchgeführt wird. - In jedem Berechnungsschritt sollen die aktuellen Populationswerte ausgegeben werden. - Der Benutzer kann eine bestimmte Gesamtanzahl von Mäusen eingeben, bis zu der die Simulation durchgeführt werden soll. Ausgabe: Schritte 43 Fachkonzept - Wiederholung Eine Solange-Wiederholung besteht aus einer Bedingung und einer Anweisungssequenz. Solange die Bedingung erfüllt ist, wird die Anweisungssequenz ausgeführt. Aufbau SOLANGE [Bedingung]: [Anweisungssequenz] Struktogramm Python-Syntax Schlüsselwort Flussdiagramm Einrückung Doppelpunkt while [Bedingung]: [Anweisungssequenz] 44 Übung zur Vernetzung Der Body-Mass-Index wird mit der folgenden Formel berechnet. (a) Teste das folgende Programm zur automatisierten Berechnung des BMI. Welche Nachteile hat das Programm? a = 100 b = 1.8 c = a / (b*b) print(a, b, c) (b) Erstelle selbst ein benutzer- und entwicklerfreudliches Programm zur automatisierten BMIBerechnung. 45 Teil 2 Zufallsexperimente Ablaufmodellierung mit Kontrollstrukturen 46 Craps Craps ist ein in den USA beliebtes Glücksspiel, das mit 2 Würfeln gespielt wird. Die beiden Würfel werden geworfen. Wenn die Augensumme 7 oder 11 beträgt, hat man gewonnen. Wenn die Augensumme 2, 3 oder 12 beträgt, hat man verloren. Ansonsten wird weitergewürfelt, bis eine Entscheidung gefallen ist. Bei jedem weiteren Wurf der beiden Würfel hat man verloren, wenn eine 7 fällt, und gewonnen, wenn die neue Augensumme gleich der Augensumme im ersten Wurf ist. Hier stellt sich die Frage, ob das Spiel fair ist, oder ob der Spieler bzw. der Spielanbieter auf lange Sicht im Vorteil ist. Orientierung: Um zu beurteilen, ob das Spiel fair ist, muss man das Spiel sehr oft ausführen (z.B. 1000 mal). Ziel ist es, ein Programm zu entwickeln, mit dem man das Spiel wiederholt ausführen kann. Erzeugung von Zufallszahlen 47 Programmiersprachen stellen in der Regel einen Baustein zur Erzeugung von (Pseudo-) Zufallszahlen zur Verfügung. In Python heißt dieser Baustein random und kann wie folgt benutzt werden. >>> >>> 2 >>> 5 >>> 5 >>> >>> 6 from random import randint randint(1, 6) randint(1, 6) randint(2, 5) augen = randint(1, 6) augen Erzeugung von Zufallszahlen 48 Simulation eines Münzwurfs Aufgabe: Welche Ausgaben werden nach dem Flussdiagramm erzeugt, wenn der Benutzer als Tipp ein 'K' eingibt und die Zufallszahl 1 erzeugt wird? Welche Abläufe und Ausgaben sind hier denkbar. Begründe mit den Fallunterscheidungen im Flussdiagramm. 49 Simulation eines Münzwurfs from random import randint # Eingabe tipp = input('Kopf (K) oder Zahl (Z): ') # Verarbeitung mit Ausgabe zufallszahl = randint(0, 1) if zufallszahl == 0: print('Kopf ist gefallen.') seite = 'K' else: print('Zahl ist gefallen.') seite = 'Z' # Ausgabe # ... Aufgabe: (a) Teste zunächst das gezeigte Programm. Welcher Teil des Struktogramms wird hier implementiert? (b) Ergänze den noch fehlenden Teil. Achte ganz genau auf Doppelpunkte und die Einrückungen (um 4 Einheiten). 50 Simulation eines Münzwurfs Aufgabe: Das folgende Programm enthält weitere Fallunterscheidungen. Kannst du das Flussdiagramm so erweitern, dass es zum erweiterten Programm passt. Beschreibe anhand von Fallbeispielen, wie sich das erweiterte Programm verhält. from random import randint # Eingabe tipp = input('Kopf (K) oder Zahl (Z): ') if tipp == 'k': tipp = 'K' if tipp == 'z': tipp = 'Z' # Verarbeitung mit Ausgabe zahl = randint(0, 1) if zahl == 0: print('Kopf ist gefallen.') seite = 'K' else: print('Zahl ist gefallen.') seite = 'Z' # Ausgabe # ... 51 Simulation eines Würfelwurfs Aufgabe: (a) Verdeutliche die hier vorgesehenen Abläufe mit Struktogrammen. (b) Das Programm links ist noch nicht ganz fertig erstellt. Ergänze den fehlenden Teil (siehe # ...) und teste das Programm. Vergleiche es mit dem Programm rechts. from random import randint # Verarbeitung augen = randint(1, 6) if augen == 1: bild = 'o' else: if augen == '2': bild = 'oo' else: if augen == '3': bild = 'ooo' else: # ... # Ausgabe print(bild) from random import randint # Verarbeitung augen = randint(1, 6) if augen == 1: bild = 'o' if augen == '2': bild = 'oo' if augen == '3': bild = 'ooo' if augen == '4': bild = 'oooo' if augen == '5': bild = 'ooooo' if augen == '6': bild = 'oooooo' # Ausgabe print(bild) 52 Ein vereinfachtes Craps-Spiel Aufgabe: Entwickle ein Programm zur Simulation der folgenden vereinfachten Version des Craps-Spiels: Zwei Würfel werden geworfen. Wenn die Augensumme 7 oder 11 beträgt, hat man gewonnen. Wenn die Augensumme 2, 3 oder 12 beträgt, hat man verloren. Ansonsten wird nochmal gewürfelt. Man hat dann gewonnen, wenn die neue Augensumme der Augensumme im ersten Wurf entspricht, ansonsten hat man verloren. Beginne zunächst mit einem Struktogramm. Übersetze es dann in ein Programm. 53 Fachkonzept - Fallunterscheidung Eine Fallunterscheidung dient dazu, alternative Abläufe zu beschreiben. 54 Fachkonzept - Fallunterscheidung Eine Fallunterscheidung dient dazu, alternative Abläufe zu beschreiben. WENN [Bedingung]: [Anweisungssequenz] SONST: [Anweisungssequenz] zweiseitige Fallunterscheidung WENN [Bedingung]: [Anweisungssequenz] einseitige Fallunterscheidung 55 Fachkonzept - Fallunterscheidung Eine Fallunterscheidung dient dazu, alternative Abläufe zu beschreiben. zweiseitige Fallunterscheidung einseitige Fallunterscheidung Doppelpunkt if [Bedingung]: [Anweisungssequenz] else: [Anweisungssequenz] if zahl == 0: print('Kopf ist gefallen.') seite = 'K' else: print('Zahl ist gefallen.') seite = 'Z' Schlüsselwort Einrückung if [Bedingung]: [Anweisungssequenz] if tipp == 'k': tipp = 'K' if tipp == 'z': tipp = 'Z' 56 Exkurs: Mehrfachfallunterscheidung augen = randint(1, 6) if augen == 1: bild = 'o' else: if augen == '2': bild = 'oo' else: if augen == '3': bild = 'ooo' else: # ... Schlüsselwort Einrückung augen = randint(1, 6) if augen == 1: bild = 'o' elif augen == '2': bild = 'oo' elif augen == '3': bild = 'ooo' elif augen == '4': bild = 'oooo' elif augen == '5': bild = 'ooooo' else: bild = 'oooooo' if [Bedingung]: [Anweisungssequenz] elif [Bedingung]: [Anweisungssequenz] elif [Bedingung]: [Anweisungssequenz] ... else: [Anweisungssequenz] Doppelpunkt 57 Würfel wiederholt werfen Beim Spiel "Mensch ärgere dich nicht" darf man erst loslegen, wenn man eine 6 gewürferlt hat. Manchmal dauert das ziemlich lang. Wie lang - das soll mit einem Programm hier untersucht werden. Aufgabe: Erkläre zunächst anhand der Diagramme, wie die Anzahl der benötigten Würfelwürfe hier bestimmt wird. Teste anschließend das gezeigte Programm. Ergänze es so, dass alle Würfelergebnisse zur Kontrolle ausgegeben werden. from random import randint # Verarbeitung augenzahl = randint(1, 6) zaehler = 1 while augenzahl != 6: augenzahl = randint(1, 6) zaehler = zaehler + 1 # Ausgabe print('Anzahl der Versuche: ', zaehler) 58 Würfel wiederholt werfen Aufgabe Untersuche mit einem Programm, ob der mit dem Zufallsgenerator von Python simulierte Würfel fair ist. Hierzu soll ein Würfel 1000 mal (bzw. eine vom Benutzer eingegebene Anzahl mal) geworfen werden. Dabei soll mitgezählt werden, wie oft eine 6 fällt. Ausgegeben werden soll die Gesamtanzahl der Würfe und die Anzahl der Treffer (hier: 6). Wie kann man aus den beiden Zahlen auf Fairness schließen? Aufgabe Entwickle ein Programm, mit dem man herausfinden kann, wie oft man durchschnittlich würfeln muss, bis man eine 6 erhält. Benutze hierzu zwei ineinander geschachtelte Wiederholungen. Beginne mit einem Struktogramm oder Flussdiagramm. Wenn die Ablauflogik stimmt, dann übersetze das Diagramm in ein Programm. 59 Fachkonzept - Wiederholung Bei der automatisierten Verarbeitung von Daten kommt es sehr oft vor, dass dieselben Anweisungen in einer Art Schleife wiederholt ausgeführt werden. Das folgende Flussdiagramm zeigt eine solche Verarbeitung mit Wiederholung. ... while augenzahl != 6: augenzahl = randint(1, 6) zaehler = zaehler + 1 ... 60 Fachkonzept - Wiederholung Eine Solange-Wiederholung besteht aus einer Bedingung und einer Anweisungssequenz. Solange die Bedingung erfüllt ist, wird die Anweisungssequenz ausgeführt. Aufbau SOLANGE [Bedingung]: [Anweisungssequenz] Struktogramm Python-Syntax Schlüsselwort Flussdiagramm Einrückung Doppelpunkt while [Bedingung]: [Anweisungssequenz] 61 Exkurs - Kontrollstrukturen Kontrollstrukturen sind Ablaufbausteine, die dazu dienen, die Reihenfolge der Abarbeitung von Anweisungen (eines Algorithmus / eines Programms) festzulegen. 62 Exkurs - Struktogramme Besonders deutlich werden die Ablaufbausteine, wenn man sie mit einer Bildsprache - den sogenannten Struktogrammen - beschreibt. Pasch 63 Wenn man mehrere Würfel wirft, dann bezeichnet man einen Wurf, bei dem alle Würfel dieselbe Augenzahl liefern, als Pasch. Es dauert manchmal ziemlich lange, bis man einen Pasch beim Würfeln mit 3 (bzw. n) Würfeln erhält. Im Folgenden soll ein Programm entwickelt werden, mit dessen Hilfe man die Anzahl der Versuche durch Simulation bestimmen kann. Aufgaben Die Bedingung ist im Algorithmus noch umgangssprachlich in der Form "kein Pasch" dargestellt. Diese Bedingung soll jetzt präzisiert werden. Das folgende Testprogramm gibt die Werte von 4 Bedingungen aus. Welche dieser Bedingungen kann man zur Implementierung von "kein Pasch" benutzen? Implementiere den oben gezeigten Algorithmus. from random import randint # Würfelergebnisse w1 = randint(1, 6) w2 = randint(1, 6) w3 = randint(1, 6) print(w1, w2, w3) # Bedingung print((w1 != w2) and (w2 != w3)) print((w1 != w2) or (w1 != w3)) print(not((w1 == w2) and (w1 == w3))) print(not((w1 == w2) or (w2 == w3))) 64 Pasch Aufgabe: Aufgabe: Was leistet das folgende Programm? Analysiere hierzu den Quelltext und erkläre die einzelnen Anweisungen. Teste anschließend deine Vermutung. Was leistet das folgende Programm? Analysiere hierzu den Quelltext und erkläre die einzelnen Anweisungen. Teste anschließend deine Vermutung from random import randint # Verarbeitung pasch = False versuche = 0 while not pasch: w1 = randint(1, 6) w2 = randint(1, 6) w3 = randint(1, 6) versuche = versuche + 1 if w1 == w2 and w1 == w3: pasch = True # Ausgabe print(versuche) from random import randint # Initialisierung n=5 # Verarbeitung (mit Ausgaben) pasch = True w1 = randint(1, 6) print(w1) for i in range(n-1): w = randint(1, 6) print(w) if w != w1: pasch = False # Ausgabe if pasch: print('Pasch') else: print('kein Pasch') Logische Operatoren 65 A: Die Augenzahl von Würfel 1 ist gleich der Augenzahl von Würfel 2. B: Die Augenzahl von Würfel 2 ist gleich der Augenzahl von Würfel 3. "Pasch": A und B "kein Pasch": (nicht A) oder (nicht B) a not a a b a and b a b a or b False True False False False False False False True False False True False False True True True False False True False True True True True True True True nicht Negation Konjunktion und Disjunktion oder 66 Fachkonzept - Wahrheitswerte Zur Beschreibung von Werten von Bedingungen benutzt man die Wahrheitswerte wahr and falsch bzw. True and False. Wahrheitswerte treten bei der Auswertung von Bedingungen auf. Wahrheitswerte können aber auch direkt mit logischen Variablen verwaltet werden. from random import randint # Verarbeitung w1 = randint(1, 6) w2 = randint(1, 6) zusammengesetzte w3 = randint(1, 6) Bedingung versuche = 1 while not (w1 == w2 and w1 == w3): w1 = randint(1, 6) w2 = randint(1, 6) w3 = randint(1, 6) versuche = versuche + 1 # Ausgabe print(versuche) from random import randint # Initialisierung n = 5 # 5er-Pasch # Verabeitung logische Variable gefunden = False versuche = 0 while not gefunden: versuche = versuche + 1 pasch = True w1 = randint(1, 6) for i in range(n-1): w = randint(1, 6) pasch = pasch and (w == w1) if pasch: gefunden = True # Ausgabe print(versuche) 67 Anwendung - Craps Craps ist ein in den USA beliebtes Glücksspiel, das mit 2 Würfeln gespielt wird. Die beiden Würfel werden geworfen. Wenn die Augensumme 7 oder 11 beträgt, hat man gewonnen. Wenn die Augensumme 2, 3 oder 12 beträgt, hat man verloren. Ansonsten wird weitergewürfelt, bis eine Entscheidung gefallen ist. Bei jedem weiteren Wurf der beiden Würfel hat man verloren, wenn eine 7 fällt, und gewonnen, wenn die neue Augensumme gleich der Augensumme im ersten Wurf ist. Hier stellt sich die Frage, ob das Spiel fair ist, oder ob der Spieler bzw. der Spielanbieter auf lange Sicht im Vorteil ist. 68 Anwendung - Craps Bevor man anfängt, die Durchführung des Spiels zu automatisieren, sollte man das Spiel erst einmal selbst durchführen, um Erfahrungen zu sammeln. Aufgabe: Führe das Spiel mehrfach durch. Protokolliere auch die Würfelergebnisse. Aufgabe: Die folgende Tabelle zeigt einen möglichen Spielablauf. Ergänze die fehlenden Einträge. 69 Anwendung - Craps Aufgabe: Im Struktogramm fehlen noch die rechten Seiten der Zuweisungen. Ergänze sie passend. Orientiere dich an der Tabelle oben zum Spielablauf. 70 Anwendung - Craps Aufgaben: (a) Übersetze das Struktogramm in ein Programm. (b) Um zu beurteilen, ob das Spiel fair ist, muss man das Spiel sehr oft ausführen (z.B. 1000 mal). Entwickle ein geeignetes Struktogramm zur wiederholten Ausführung des Spiels. Übersetze es in ein Python-Programm. Führe das entwickelte Programme aus. Jetzt solltest du das Spiel beurteilen können. 71 Das Ziegenproblem Das Ziegenproblem (vgl. Wikipedia - Ziegenproblem) geht auf eine Spielsituation bei einer amerikanischen Spielshow zurück. Der Moderator der Spielshow zeigt dem Kandidaten 3 Türen: "Hinter einer der 3 Türen steht der Hauptgewinn, ein Auto. Hinter den beiden anderen Türen sind Ziegen. Welche Tür wählen Sie?" Nachdem sich der Kandidat entschieden hat (z.B. für Tür 1), öffnet der Moderator eine der beiden anderen mit einer Ziege (z.B. Tür 3) und stellt dem Kandidaten die folgende Frage: "Bleiben Sie nun bei Ihrer Wahl oder möchten Sie auf Tür 2 umwählen ?" Wie soll sich der Kandidat entscheiden? Gibt es für ihn eine Strategie, mit der er eher an das Auto kommt? Orientierung: Um die beiden Strategien (Wahl beibehalten / Umwahl) besser beurteilen zu können, muss man mit jeder Strategie das Spiel sehr oft ausführen (z.B. 1000 mal). Ziel ist es, ein Programm zu entwickeln, mit dem man das Spiel mit einer festen Strategie wiederholt ausführen kann. 72 Anwendung - Ziegenproblem Tür-Auto: 1 Tür-Kandidat: 1 Tür-Mod.: 2|3 Tür-Kandidat: 2 Tür-Mod.: 3 Tür-Auto: 2 Tür-Auto: 3 Tür-Kandidat: 3 Bevor man anfängt, die Durchführung des Spiels zu automatisieren, sollte man erst einmal selbst spielen, um Erfahrungen zu sammeln. Das geht ganz leicht mit Spielkarten und Würfeln. Schritt 1: Das Auto wird hinter einer Tür versteckt. Der Moderator kann hierzu einen Würfel benutzen, um die Tür zufällig auszuwählen. Schritt 2: Der Kandidat wählt jetzt eine Tür aus. Schritt 3: Der Moderator kennt die Tür, hinter der sich das Auto befindet, und die Tür, die der Kandidat ausgewählt hat. Er öffnet jetzt eine Tür, hinter der sich eine Ziege befindet. In der folgenden Tabelle oben sind die Wahlmöglichkeiten des Moderators eingetragen. Schritt 4: Der Kandidat hat jetzt die Möglichkeit, bei seiner Wahl zu bleiben oder umzuwählen. Wenn man die beiden Strategien vergleichen will, dann sollte man das Spiel mehrfach mit der gleichen Strategie wiederholen. Schritt 5: Der Moderator öffnet alle Türen und teilt das Ergebnis mit. Aufgabe: Ergänze zunächst die oben gezeigte Tabelle. Führe das Spiel mehrfach durch. Hast du bereits eine Vermutung, welche Strategie besser ist? 73 Anwendung - Ziegenproblem Ein Programm soll das Spiel anbieten und dabei die Rolle des Moderators übernehmen. Aufgabe - Ablaufmodellierung: (a) Im Struktogramm "tuerZiege bestimmen - Fall 1" fehlen moch Angaben in Zuweisungen. Ergänze die fehlenden Angaben. (b) Erstelle ein entsprechendes Struktogramm für einen der beiden anderen Fälle (das Auto steht hinter Tür 2 bzw. Tür 3). 74 Anwendung - Ziegenproblem Aufgabe Entwickle zunächst ein Programm, mit dem man einmal die Aktionen der Spielshow simulieren kann. Aufgabe Um die beiden Strategien (Wahl beibehalten / Umwahl) besser beurteilen zu können, muss man mit jeder Strategie das Spiel sehr oft ausführen (z.B. 1000 mal). Für eine solche wiederholte Ausführung ist es jedoch ungünstig, wenn das Spiel wie bisher interaktiv gestaltet ist. Ersetze die Eingaben des Kandidaten durch Anweisungen, mit denen die Wahl der Tür mit Hilfe von Zufallszahlen erfolgt. Führe die entwickelten Programme aus. Jetzt sollte klar sein, für welche Strategie man sich entscheiden sollte. 75 Teil 3 Verschlüsselung Modularisierung mit Funktionen Veschlüsselung 76 In einer Fallstudie zur Verschlüsselung geht es vor allem darum, Funktionen als eigenständige Programmierbausteine einzuführen, um komplexere Programme besser zu strukturieren und nach dem Bausteinprinzip zu entwickeln. PYLZFOWBNQCYBUVNCBLGYC HYAYBYCGMWBLCZNYHNTCZY LN VDOYH FDHVDU A B C D E F G H I J K L M N O P Q R S T U V W X Y Z D E F G H I J K L M N O P Q R S T U V W X Y Z A B C Quelltext: SALVECAESAR Schlüssel: 3 Geheimtext: VDOYHFDHVDU 77 >>> >>> 'A' >>> >>> 65 >>> >>> 68 >>> >>> 'D' Automatisierte Verschlüsselung zeichen = 'A' zeichen zahl = ord(zeichen) zahl neuezahl = zahl + 3 neuezahl neueszeichen = chr(neuezahl) neueszeichen Die Python-Dialoge zeigen, wie man - Buchstaben verschieben kann, - wie man auf einzelne Zeichen einer Zeichenkette zugreifen kann, - wie man neue Zeichketten aufbauen kann. Aufgabe: Kannst du mit den Informationen ein Programm zum Verschlüsseln von Texten, die nur aus Großbuchstaben bestehen, entwickeln? >>> klartext = 'HALLO' >>> klartext 'HALLO' >>> klartext[0] 'H' >>> klartext[1] 'A' >>> klartext[4] 'O' >>> len(klartext) 5 >>> klartext[len(klartext)-1] 'O' >>> geheimtext = '' >>> geheimtext '' >>> geheimtext = geheimtext + 'K' >>> geheimtext 'K' >>> geheimtext = geheimtext + 'D' >>> geheimtext 'KD' 78 Automatisierte Verschlüsselung # Vorgabe klartext = 'HALLO' # Verarbeitung geheimtext = '' for zeichen in klartext: zahl = ord(zeichen) neuezahl = zahl + 3 if neuezahl > ord('Z'): neuezahl = neuezahl - 26 neuesZeichen = chr(neuezahl) geheimtext = geheimtext + neuesZeichen # Ausgabe print(geheimtext) # Vorgabe text = 'Dieser Text müßte "bereingt" werden.' # Verarbeitung textNeu = '' for zeichen in text: if ord(zeichen) >= ord('A') and \ ord(zeichen) <= ord('Z'): # zeichen ist ein Großbuchstabe textNeu = textNeu + zeichen # ... # Ausgabe print(text) print(textNeu) Aufgabe: Aufgabe: Entwickle entsprechend ein Programm zur Entschlüsselung von Texten, die nach dem Caesar-Verfahren verschlüsselt wurden. Wir sind bisher davon ausgegangen, dass ein vorgegebener Text nur aus Großbuchstaben besteht. Einen beliebigen Text müsste man erst einmal in einen Text aus Großbuchstaben umwandeln, um das Verschlüsselungsverfahren anwenden zu können. 79 Fachkonzept - Zeichenkette Eine Zeichenkette ist eine (evtl. leere) Folge von Zeichen (aus einer vorgegebenen Zeichenmenge), die zu einer Dateneinheit zusammengefasst ist. Der Datentyp Zeichenkette beschreibt die Menge der möglichen Zeichenkette zusammen mit den Operationen, die mit Zeichenketten vorgesehen sind. >>> z1 = 'Hallo' >>> z1 'Hallo' >>> z2 = "Hallo" >>> z2 'Hallo' >>> z = '' >>> z >>> text = """Dieser Text geht ... über mehrere ... Zeilen.""" >>> text 'Dieser\nText geht\nüber mehrere\nZeilen.' >>> print(text) Dieser Text geht über mehrere Zeilen. Darstellung von Zeichenketten 80 Fachkonzept - Zeichenkette Eine Zeichenkette ist ein sequentielles Datenobjekt, das aus einer Folge einzelner Zeichen besteht. Die Elemente eines solchen sequentiellen Datenobjekts sind der Reihe nach durchnummeriert. Die Nummerierung beginnt dabei mit 0. Die Nummer eines sequentiellen Datenobjekts wird auch Index genannt. >>> klartext = 'HALLO' >>> klartext[0] 'H' >>> klartext[1] 'A' >>> klartext[4] 'O' >>> klartext[5] Traceback (most recent call last): ... klartext[5] IndexError: string index out of range >>> len(klartext) 5 >>> klartext[len(klartext)-1] 'O' lesender Zugriff auf einzelne Zeichen beachte den Indexbereich Länge einer Zeichenkette 81 Fachkonzept - Zeichenkette Zeichenketten kann man über den Indexbereich, oder direkt über die einzelnen Zeichen durchlaufen. # klartext = ... i=0 while i < len(klartext): # verarbeite klartext[i] i=i+1 # klartext = ... for zeichen in klartext: # verarbeite zeichen Schleife über den Indexbereich for-Schleife für sequentielle Datenobjekte 82 Fachkonzept - Zeichenkette Eine Zeichenkette ist in Python ein nicht-veränderbares Datenobjekt. In Python ist nur ein lesender Zugriff auf einzelne Zeichen einer Zeichenkette möglich ist. Abändern kann man einzelne Zeichen einer Zeichenkette mit Hilfe von Zuweisungen nicht. Eine veränderte Zeichenkette erhält man nur, indem man eine neue Zeichenkette - z.B. mit dem Konkatenationsoperator - aufbaut. >>> klartext = 'HALLO' >>> klartext[1] 'A' >>> klartext[1] = 'E' TypeError: 'str' object does not support item assignment >>> >>> >>> >>> >>> 'HE' klartext = 'HALLO' klartextNeu = '' klartextNeu = klartextNeu + klartext[0] klartextNeu = klartextNeu + 'E' klartextNeu kein schreibender Zugriff erlaubt Zusammenfügen von Zeichenketten 83 Strukturierung des Programms text = 'Alles Käse!' print(text) textNeu = '' for zeichen in text: if ord(zeichen) >= ord('A') and ord(zeichen) <= ord('Z'): # zeichen ist ein Großbuchstabe textNeu = textNeu + zeichen elif ord(zeichen) >= ord('a') and ord(zeichen) <= ord('z'): # zeichen ist ein Kleinbuchstabe neuesZeichen = chr(ord(zeichen)-32) textNeu = textNeu + neuesZeichen elif zeichen == 'Ä' or zeichen == 'ä': textNeu = textNeu + 'AE' elif zeichen == 'Ö' or zeichen == 'ö': textNeu = textNeu + 'OE' elif zeichen == 'Ü' or zeichen == 'ü': textNeu = textNeu + 'UE' elif zeichen == 'ß': textNeu = textNeu + 'SS' klartext = textNeu print(klartext) geheimtext = '' for zeichen in klartext: zahl = ord(zeichen) neuezahl = zahl + 3 if neuezahl > ord('Z'): neuezahl = neuezahl - 26 neuesZeichen = chr(neuezahl) geheimtext = geheimtext + neuesZeichen print(geheimtext) ... ... klartext = '' for zeichen in geheimtext: zahl = ord(zeichen) neuezahl = zahl - 3 if neuezahl < ord('A'): neuezahl = neuezahl + 26 neuesZeichen = chr(neuezahl) klartext = klartext + neuesZeichen print(klartext) Wenn Programme weiter wachsen - z.B. auf mehrere Millionen Programmzeilen -, dann kann keiner mehr alles Details eines Programms überblicken. Man benötigt dann Verfahren, um das Ganze in übersichtliche Programmeinheiten zu zerlegen. Aufgabe: Zerlege das Programm in sinnvolle Einheiten. Verdeutliche die Zerlegung durch Kommentare. 84 Zerlegung in Verarbeitungseinheiten text = 'Alles Käse!' print(text) # Text bereinigen textNeu = '' for zeichen in text: if ord(zeichen) >= ord('A') and ord(zeichen) <= ord('Z'): # zeichen ist ein Großbuchstabe textNeu = textNeu + zeichen elif ord(zeichen) >= ord('a') and ord(zeichen) <= ord('z'): # zeichen ist ein Kleinbuchstabe neuesZeichen = chr(ord(zeichen)-32) textNeu = textNeu + neuesZeichen elif zeichen == 'Ä' or zeichen == 'ä': textNeu = textNeu + 'AE' elif zeichen == 'Ö' or zeichen == 'ö': textNeu = textNeu + 'OE' elif zeichen == 'Ü' or zeichen == 'ü': textNeu = textNeu + 'UE' elif zeichen == 'ß': textNeu = textNeu + 'SS' klartext = textNeu print(klartext) # Text verschlüsseln geheimtext = '' for zeichen in klartext: zahl = ord(zeichen) neuezahl = zahl + 3 if neuezahl > ord('Z'): neuezahl = neuezahl - 26 neuesZeichen = chr(neuezahl) geheimtext = geheimtext + neuesZeichen print(geheimtext) # Text entschlüsseln klartext = '' for zeichen in geheimtext: zahl = ord(zeichen) neuezahl = zahl - 3 if neuezahl < ord('A'): neuezahl = neuezahl + 26 neuesZeichen = chr(neuezahl) klartext = klartext + neuesZeichen print(klartext) 85 Eigenständige Verarbeitungseinheit Funktion def verschluesselterText(klartext): geheimtext = '' for zeichen in klartext: zahl = ord(zeichen) neuezahl = zahl + 3 if neuezahl > ord('Z'): neuezahl = neuezahl - 26 neuesZeichen = chr(neuezahl) geheimtext = geheimtext + neuesZeichen return geheimtext >>> verschluesselterText('HALLO') 'KDOOR' >>> verschluesselterText('CAESAR') 'FDHVDU' # Text verschlüsseln geheimtext = '' for zeichen in klartext: zahl = ord(zeichen) neuezahl = zahl + 3 if neuezahl > ord('Z'): neuezahl = neuezahl - 26 neuesZeichen = chr(neuezahl) geheimtext = geheimtext + neuesZeichen print(geheimtext) Aufgabe: Entwickle und teste analog Funktionsdefinitionen zu den Funktionen entschluesselterText und bereinigterText. Funktionsdefinition Funktionsaufruf 86 Eigenständige Verarbeitungseinheit # Funktionsdefinitionen def verschluesselterText(klartext): geheimtext = '' for zeichen in klartext: zahl = ord(zeichen) neuezahl = zahl + 3 if neuezahl > ord('Z'): neuezahl = neuezahl - 26 neuesZeichen = chr(neuezahl) geheimtext = geheimtext + neuesZeichen return geheimtext Funktionsdefinition Aufgabe: Ergänze die fehlenden Teile und speichere den gesamten Quelltext in einer Datei ab. Teste das Programm auch mit anderen Texten. def entschluesselterText(geheimtext): # ... return klartext def bereinigterText(text): # ... return textNeu # Testprogramm text1 = 'Alles Käse!' text2 = bereinigterText(text1) text3 = verschluesselterText(text2) text4 = entschluesselterText(text3) print(text1) print(text2) print(text3) print(text4) Funktionsaufruf 87 Schachtelung von Funktionen # Funktionsdefinitionen def verschluesseltesZeichen(zeichen): zahl = ord(zeichen) neuezahl = zahl + 3 if neuezahl > ord('Z'): neuezahl = neuezahl - 26 neuesZeichen = chr(neuezahl) return neuesZeichen def verschluesselterText(klartext): for zeichen in klartext: neuesZeichen = verschluesseltesZeichen(zeichen) geheimtext = geheimtext + neuesZeichen return geheimtext Funktionsdefinition Funktionsaufruf ... # Testprogramm text1 = 'Alles Käse!' text2 = bereinigterText(text1) text3 = verschluesselterText(text2) text4 = entschluesselterText(text3) print(text1) print(text2) print(text3) print(text4) Aufgabe: Ergänze die fehlenden Teile und speichere den gesamten Quelltext in einer Datei ab. Teste das Programm auch mit anderen Texten. 88 Datenübergabe mit Parametern # Funktionsdefinitionen def verschluesseltesZeichen(zeichen, schluessel): # ... return neuesZeichen def verschluesselterText(klartext, schluessel): # ... return geheimtext Funktionsdefinition ... # Testprogramm text1 = 'Alles Käse!' text2 = bereinigterText(text1) text3 = verschluesselterText(text2, 4) text4 = entschluesselterText(text3, 4) print(text1) print(text2) print(text3) print(text4) Funktionsaufruf Aufgabe: An welchen Stellen im Quelltext muss die konkrete Verschiebezahl 3 durch die Funktionsvariable schluessel ersetzt werden? Probiere das aus und teste auch andere Verschiebezahlen. Funktionen 89 Eine Funktion ist eine Verarbeitungseinheit, die übergebene Daten verarbeitet und den berechneten Funktionswert als Ergebnis zurückgibt. Die Verarbeitung wird über eine Funktionsdefinition (man sagt oft auch Funktionsdeklaration) festgelegt. Aktiviert wird eine Verarbeitung durch einen Funktionsaufruf. Schlüsselwort Funktionsname(Parameter) def verschluesselterText(klartext, schluessel): geheimtext = '' for zeichen in klartext: zahl = ord(zeichen) neuezahl = zahl + schluessel if neuezahl > ord('Z'): neuezahl = neuezahl - 26 neuesZeichen = chr(neuezahl) geheimtext = geheimtext + neuesZeichen return geheimtext Doppelpunkt Funktionsdefinition Einrückung >>> verschluesselterText('HALLO', 4) 'LEPPS' Funktionsaufruf Parameter 90 Parameter sind Variablen, mit deren Hilfe die zur Verarbeitung übergebenen Daten bei einer Funktion verwaltet werden. Datenübergabe Datenrückgabe verschluesselterText 'HALLO' text return ... zu verarbeitende Daten Parameter Parameter def verschluesselterText(klartext, schluessel): ... return geheimtext 'KDOOR' zurückgelieferte Daten aktuelle Daten >>> verschluesselterText('HALLO', 4) 'LEPPS' Funktionsaufruf Funktionsdefinition 91 Übungen Aufgabe: A. verschlüsselt ihre Texte, indem sie sie einfach umdreht. Entwickle eine geeignete Funktionsdefinition und teste die Funktion mit verschiedenen Funktionsaufrufen. Aufgabe: B. verfremdet ihre Texte, indem sie alle vorkommenden Vokale durch einen vorgegebenen ersetzt. Entwickle eine geeignete Funktionsdefinition und teste die Funktion mit verschiedenen Funktionsaufrufen. Übungen 92 Aufgabe: Analysiere die Funktionsdefinitionen und ergänze den folgenden Python-Dialog. Überprüfe die Ergebnisse mit Python. Begründe: Man kann zum Ver- und Entschlüsseln von Texten nach der Verschiebemethode dieselben Funktionen benutzen. def verschobenesZeichen(zeichen, verschiebezahl): zahl = ord(zeichen) neuezahl = zahl + verschiebezahl if neuezahl > ord('Z'): neuezahl = neuezahl - 26 if neuezahl < ord('A'): neuezahl = neuezahl + 26 neuesZeichen = chr(neuezahl) return neuesZeichen def verschobenerText(text, verschiebezahl): neuerText = '' for zeichen in text: neuesZeichen = verschobenesZeichen(zeichen, verschiebezahl) neuerText = neuerText + neuesZeichen return neuerText >>> ??? >>> ??? >>> ??? >>> ??? verschobenesZeichen('H', 1) verschobenesZeichen('H', -2) verschobenerText('HUND', 7) verschobenerText('OBUK', 19) 93 Laden und Speichern def verschluesseltesZeichen(zeichen, schluessel): # ... def verschluesselterText(klartext, schluessel): # ... def entschluesseltesZeichen(zeichen, schluessel): # ... def entschluesselterText(geheimtext, schluessel): # ... def bereinigterText(text): # ... def textAusDatei(dateiname): datei = open(dateiname, 'r', encoding='iso-8859-1') text = datei.read() datei.close() return text def textInDateiSpeichern(dateiname, text): datei = open(dateiname, 'w', encoding='iso-8859-1') datei.write(text) datei.close() # Testprogramm text1 = textAusDatei('klartext.txt') text2 = bereinigterText(text1) text3 = verschluesselterText(text2, 5) textInDateiSpeichern('geheimtext.txt', text3) Aufgabe: Teste das Programm. Bevor das möglich ist, solltest du eine Datei 'klartext.txt' anlegen und einen Text in dieser Datei speichern. Beachte, dass du die im Quelltext angegebene Codierung (hier: iso-8859-1) verwendest. Beachte auch, dass das Python-Programm und die Datei sich im gleichen Verzeichnis befinden. Wenn das Programm ausgeführt ist, dann schaue in die Datei 'geheimtext.txt' rein. Hier müsste jetzt der verschlüsselte Text zu finden sein. Aufgabe: Die Funktionen textAusDatei und textInDateiSpeichern unterscheiden sich in einem wesentlichen Punkt. Das sieht man in den Funktionsdefinitionen und auch bei den Funktionsaufrufen. Zudem wird es durch die Namensgebung angedeutet. Worin besteht der Unterschied? 94 Prozedur Eine Prozedur ist eine Funktion, die keine Rückgabe erzeugt. Funktion mit Rückgabe Funktion ohne Rückgabe text = textAusDatei('klartext.txt') # ... textInDateiSpeichern('geheimtext.txt', 'ZPTZHSHIPT') Funktionsaufruf Prozeduraufruf Ein Aufruf der Funktion textAusDatei liefert ein Ergebnis, das verwaltet oder weiterverarbeitet werden muss. Der Funktionsaufruf textAusDatei('klartext.txt') ist hier daher (als Term auf der rechten Seite) in eine Zuweisung eingebunden. Ganz anders wird die Funktion textInDateiSpeichern benutzt. Der Aufruf textInDateiSpeichern('geheimtext.txt', 'ZPTZHSHIPT') erzeugt kein Ergebnis und stellt eine eigenständige Anweisung dar. 95 Häufigkeitsanalyse Der folgende Geheimtext soll entschlüsselt werden. Wir gehen davon aus, dass der Geheimtext mit der Verschiebemethode (nach Caesar, aber mit belielibiger Verschiebezahl) erzeugt wurde. Leider ist der Schlüssel (d.h. die Verschiebezahl) nicht bekannt. Warum könnte "Buchstaben zählen" hier weiterhelfen? XREQXRCCZVEZJKZEUIVZKVZCVXVKVZCKVZEVEURMFESVNFYEVEUZVSVCXVIUVEREUVI VEUZVRHLZKREVILEUUVEUIZKKVEUZVUZVZEZYIVIVZXVEVEJGIRTYVBVCKVEXVEREEK NVIUVELEUZELEJVIVIXRCCZVIYVZJJVEJZVRCCVLEKVIJTYVZUVEJZTYLEKVIVZEREU VIZEJGIRTYVXVNFYEYVZKVELEUXVJVKQVEUZVXRCCZVIKIVEEKMFEUVERHLZKREVIEU VIWCLJJXRIFEEVMFEUVESVCXVIEUZVDRIEVLEUUZVJVZEVUZVKRGWVIJKVERCCUZVJV IJZEUUZVSVCXVILEUQNRIUVJNVXVENVZCJZVMFEUVIBLCKLILEUCVSVEJNVZJVUVIIF VDZJTYVEGIFMZEQRDNVZKVJKVEVEKWVIEKJZEUJVYIJVCKVEBRLWCVLKVQLUZVJVEBF DDVELEUURJNRJQLIMVINVZTYCZTYLEXUVIXVDLVKVIWLVYIKVZEWLVYIVELEUNVZCJZ VUVEXVIDREVEUZVAVEJVZKJUVJIYVZEVJCVSVELEUDZKUVEVEJZVJKRVEUZXBIZVXWL VYIVEJVYIERYVJZEURLJUZVJVDXILEULVSVIKIVWWVERLTYUZVYVCMVKZVIUZVLVSIZ XVEXRCCZVIREKRGWVIBVZKNVZCJZVJZTYZEWRJKKRVXCZTYVEXVWVTYKVEDZKUVEXVI DREVEDVJJVENFSVZJZVUZVJVVEKNVUVIMFEZYIVDXVSZVKWVIEYRCKVEFUVIJVCSJKZ EUVIVEXVSZVKBRVDGWVEVZEKVZCURMFEUVENZVXVJRXKUZVXRCCZVISVNFYEVEWZEUV KJVZEVELIJGILEXREUVIIYFEVNZIUSVXIVEQKMFEUVIXRIFEEVUVDFQVRELEUUVDXVS ZVKUVISVCXVIVIJKIVTBKJZTYMFEUVEJVHLREVIELEUYVCMVKZVIEYZESZJQLDIYVZE LEUQVZXKERTYEFIUVEURJXVSZVKUVISVCXVISVXZEEKREUVERVLJJVIJKVEXIVEQVEX RCCZVEJVIJKIVTBKJZTYSZJREUVELEKVIVEKVZCUVJIYVZEJLEUCZVXKERTYEFIUFJK VERHLZKREZVEVIJKIVTBKJZTYMFEUVDWCLJJXRIFEEVSZJREUZVGPIVERVVELEUQLUV DAVEZXVEKVZCUVJFQVREJUVISZJJGREZVEIVZTYKVJCZVXKZEIZTYKLEXEFIUNVJKVE 96 text = """ ABRAKADABRA SIMSALABIM """ anzahl = 0 for zeichen in text: if zeichen == 'A': anzahl = anzahl + 1 print('A', ':', anzahl) anzahl = 0 for zeichen in text: if zeichen == 'B': anzahl = anzahl + 1 print('B', ':', anzahl) anzahl = 0 for zeichen in text: if zeichen == 'C': anzahl = anzahl + 1 print('C', ':', anzahl) ... Häufigkeitsanalyse Wenn ein Programmteil - bis auf geringfügige Abweichungen - mehrfach in einem Programm vorkommt, spricht man von Codeduplizierung. Codeduplizierung versucht man in der Regel zu vermeiden. Zum einen werden Programme hierdurch in der Regel kürzer. Zum anderen erspart man sich Arbeit, wenn der Programmteil noch einmal überarbeitet wird. Man muss dann nur einen einzigen Programmteil bearbeiten anstatt mehrere. 97 Häufigkeitsanalyse def haeufigkeit(buchstabe, text): anzahl = 0 for zeichen in text: if zeichen == buchstabe: anzahl = anzahl + 1 return anzahl Aufgabe: # Testprogramm (b) Benutze das vorliegende Programm, um den oben gezeigten verschlüsselten Text zu analysieren. Mit den erzielten Häufigkeitswerten kannst du dann bestimmt den noch fehlenden Schlüssel ermitteln. aktuellerText = """ ABRAKADABRA SIMSALABIM """ print('A', ':', haeufigkeit('A', aktuellerText)) print('B', ':', haeufigkeit('B', aktuellerText)) print('C', ':', haeufigkeit('C', aktuellerText)) print('D', ':', haeufigkeit('D', aktuellerText)) # ... (a) Vergleiche das vorliegende Program (mit Funktionsdefinition) und das oben gezeigte (ohne Funktionsdefinition). Was ist der Vorteil des vorliegenden Programms? Siehst du auch noch Nachteile? (s.u.) 98 Häufigkeitsanalyse ... # Testprogramm aktuellerText = """ ABRAKADABRA SIMSALABIM """ abc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' for zeichen in abc: ... Aufgabe: Du kannst die Folge von print-Anweisungen im oben gezeigten Programm auch durch eine geeignete Wiederholungsanweisung ersetzen. Ergänze hierzu das folgende Programm. Bemerkung: Das hier entwickelte Programm hat noch den Nachteil, dass der zu analysierende Text für jeden Buchstaben einmal durchlaufen wird. Natürlich geht das auch noch einfacher. Wenn du dich bereits mit Listen auskennst, dann kannst du ja versuchen, das Programm so zu gestalten, dass man mit einem Durchlauf über den Text auskommt. 99 Verwendung von Funktionen # Funktionsdefinitionen def verschluesseltesZeichen(zeichen, schluessel): # ... return neuesZeichen def verschluesselterText(klartext, schluessel): # ... return geheimtext ... # Testprogramm text1 = 'Alles Käse!' text2 = bereinigterText(text1) text3 = verschluesselterText(text2, 4) text4 = entschluesselterText(text3, 4) print(text1) print(text2) print(text3) print(text4) Strukturierung des Quelltextes Programme werden zur Steuerung von Computern entwickelt. Einem Computer ist es egal, wie lang und unübersichtlich ein Programm ist. Der Computer muss ja nur die Anweisungen des Programms Schritt für Schritt abarbeiten. Programme werden (in der Regel) von Menschen entwickelt. Wenn Programme zu unübersichtlich werden, dann können sich leicht Fehler einschleichen. Je unübersichtlicher ein Programm ist, desto schwieriger gestaltet sich die Fehlersuche. Ein zentrales Ziel bei der Programmentwicklung ist es daher, den Quelltext möglichst strukturiert zu gestalten. Funktionen sind ein Mittel zur Strukturierung von Programmen. Mit Funktionen kann man Verarbeitungseinheiten bilden und diese als Unterprogramme in einem größeren Programm verwenden. 100 Verwendung von Funktionen def haeufigkeit(buchstabe, text): anzahl = 0 for zeichen in text: if zeichen == buchstabe: anzahl = anzahl + 1 return anzahl # Testprogramm aktuellerText = """ ABRAKADABRA SIMSALABIM """ abc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' for zeichen in abc: hZeichen = haeufigkeit(zeichen, aktuellerText) print(zeichen, ':', hZeichen) Vermeidung von Codeduplizierung Wenn ein Programmteil - bis auf geringfügige Abweichungen - mehrfach in einem Programm vorkommt, spricht man von Codeduplizierung. Codeduplizierung versucht man in der Regel zu vermeiden. Zum einen werden Programme hierdurch in der Regel kürzer. Zum anderen erspart man sich Arbeit, wenn der Programmteil noch einmal überarbeitet wird. Man muss dann nur eine einzige Verarbeitungseinheit bearbeiten anstatt mehrere. Funktionen helfen bei der Vermeidung von Codeduplizierung. Eine Verarbeitungseinheit, die in ähnlicher Form mehrfach in einem Programm vorkommt, lässt sich mit einer Funktion als eigenständiges Unterprogramm konzipieren. 101 Veschlüsselung mit Zuordnungstabelle Das bisher benutzte Verfahren zum Verschlüsseln von Texten nach der Verschiebemethode ist sehr einfach. Das hat den Nachteil, dass es relativ leicht "geknackt" werden kann. Wenn man die Verschiebezahl kennt, kann man direkt einen Text entschlüsseln. Wir versuchen im Folgenden, das Verfahren zu verbessern. Statt Buchstaben systematisch um eine feste Zahl im Alphabet zu verschieben, benutzen wir eine Zuordnungstabelle, die Buchstaben willkürlich neue Buchstaben zuordnet - jedoch so, dass die Zuordnung eindeutig bleibt. Zuordnungstabelle A B C D E F G H I J K L M N O P Q R S T U V W X Y Z G K X C S L Z U A H W D B M T Y E N J V P O I R F Q Quelltext: Schlüssel: GKXCSLZUAHW.. SALVECAESAR J U L I U S C A E S A R | | | | | | | | | | | | H P D A P J . . . . . . Geheimtext: JGDOSXGSJGN . . . . . . . . . . . . | | | | | | | | | | | | J G D O S G J V S N A R 102 Veschlüsselung mit Zuordnungstabelle def verschluesseltesZeichen(zeichen, schluessel): # zeichen: Großbuchstabe # schluessel: Zuordnungstabelle in Form einer Zeichenkette # return: zum übergebenen Buchstaben zugeordneter Buchstabe # Beispiel: """ >>> verschluesseltesZeichen('B', 'GKXCSLZUAHWDBMTYENJVPOIRFQ') 'K' """ nummer = ord(zeichen)-ord('A') neuesZeichen = schluessel[nummer] return neuesZeichen def verschluesselterText(klartext, schluessel): # klartext: Zeichenkette aus Großbuchstaben # schluessel: Zuordnungstabelle in Form einer Zeichenkette # return: neue, mit der Zuordnungstabelle konstruierte Zeichenkette # Beispiel: """ >>> verschluesselterText('HUND', 'GKXCSLZUAHWDBMTYENJVPOIRFQ') 'UPMC' """ geheimtext = '' for zeichen in klartext: neuesZeichen = verschluesseltesZeichen(zeichen, schluessel) geheimtext = geheimtext + neuesZeichen return geheimtext Funktion als fertiger Baustein 103 Veschlüsselung mit Zuordnungstabelle Aufgabe: Funktionen kann man selbstverständlich auch benutzen, wenn man sie nicht selbst definiert hat. Voraussetzung dafür ist, dass man genau weiß, welche Daten man der Funktion zur Verarbeitung übergeben muss und was die Funktion als Ergebnis zurückgibt. In einer Schnittstellenbeschreibung werden alle diese Informationen genau beschrieben. Speichere jetzt die gezeigten Funktionsdefinitionen in einer Datei mit dem Dateinamen verschluesselung_mit_zuordnungstabelle.py ab. Erstelle anschließend ein Testprogramm wie das folgende, das die Funktionen verwendet. # Import der Funktionen from verschluesselung_mit_zuordnungstabelle import * # Verwendung der Funktionen zuordnung = 'GKXCSLZUAHWDBMTYENJVPOIRFQ' geheimtext = verschluesselterText('SALVECAESAR', zuordnung) print(geheimtext) Verwendung des fertigen Bausteins 104 Veschlüsselung mit Zuordnungstabelle Aufgabe: Fertige entsprechende Schnittstellenbeschreibungen zu den folgenden Funktionen an. Erstelle auch ein Testprogramm, das diese Funktionen verwendet. def entschluesseltesZeichen(zeichen, schluessel): # ... i=0 ... return neuesZeichen def entschluesselterText(geheimtext, schluessel): # ... klartext = '' ... return klartext def bereinigterText(text): # ... textNeu = '' ... return textNeu 105 Funktion als Baustein Modularisierung ist ein Prinzip, nach dem viele Systeme entwickelt werden. Die Idee besteht darin, das Gesamtsystem nach dem Baukastenprinzip aus wiederverwendbaren Einzelbausteinen (den sogenannten "Modulen") zusammenzusetzen. Modularisierung erleichtert es, komplexe Systeme zu entwickeln. Funktionen können also als einfache Umsetzungen des Modularisierungsprinzips angesehen werden. Wenn man Funktionen als eigenständige Verarbeitungseinheiten konzipiert, dann lassen sie sich als Bausteine verwenden und und bei der Entwicklung von Programmen nach dem Baukastenprinzip verwenden. Man muss bei der Verwendung als Baustein nur die Schnittstelle der Funktion genau kennen. # Import der Funktionen from verschluesselung_mit_zuordnungstabelle import * Strukturierung des Quelltextes # Verwendung der Funktionen zuordnung = 'GKXCSLZUAHWDBMTYENJVPOIRFQ' geheimtext = verschluesselterText('SALVECAESAR', zuordnung) print(geheimtext) Vermeidung von Codeduplizierung 106 Schnittstelle einer Funktion Die Schnittstelle einer Funktion legt sämtliche Informationen fest, die man wissen muss, um diese Verarbeitungseinheit benutzen zu können. Man kann diese Informationen aus dem Quelltext der Funktion erschließen. Oft will man aber nicht alle Details des Quelltextes genau analysieren, um an die erforderlichen Informationen heranzukommen, sondern die Funktion direkt nutzen. Man benötigt dann Antworten auf die folgenden Fragen: Welchen Namen hat die Funktion? Wie viele Parameter gibt es? Welche Werte darf man für sie einsetzen? In welcher Reihenfolge treten sie auf? Wofür stehen sie? Welche Werte werden als Rückgaben erzeugt? Wie verhält sich die Funktion? 107 Schnittstellenbeschreibung Wir verwenden verschiedene Formen der Schnittstellenbeschreibung. Oft begnügen wir uns mit einem Black-Box-Diagramm. Im Quelltext stellen wir die erforderlichen Informationen in unten gezeigten Weise dar. def verschluesselterText(klartext, schluessel): # # # # klartext: Zeichenkette aus Großbuchstaben schluessel: Zuordnungstabelle in Form einer Zeichenkette return: neue, mit der Zuordnungstabelle konstruierte Zeichenkette Beispiel: """ >>> verschluesselterText('HUND', 'GKXCSLZUAHWDBMTYENJVPOIRFQ') 'UPMC' """ geheimtext = '' for zeichen in klartext: neuesZeichen = verschluesseltesZeichen(zeichen, schluessel) geheimtext = geheimtext + neuesZeichen return geheimtext Signatur Verhalten 108 Modultest in Python Python stellt eine Möglichkeit zur Verfügung, wie man Testfälle automatisiert überprüfen kann. Hierzu muss man nur die Funktion testmod des Moduls doctest ausführen (s.u.). def verschluesseltesZeichen(zeichen, schluessel): # zeichen: Großbuchstabe # schluessel: Zuordnungstabelle in Form einer Zeichenkette # return: zum übergebenen Buchstaben zugeordneter Buchstabe # Beispiel: """ >>> verschluesseltesZeichen('B', 'GKXCSLZUAHWDBMTYENJVPOIRFQ') Testfälle 'K' """ >>> Trying: nummer = ord(zeichen)-ord('A') verschluesselterText('HUND', 'GKXCSLZUAHWDBMTYENJVPOIRFQ') neuesZeichen = schluessel[nummer] Expecting: return neuesZeichen 'UPMC' ok def verschluesselterText(klartext, schluessel): Trying: verschluesseltesZeichen('B', 'GKXCSLZUAHWDBMTYENJVPOIRFQ') ... Expecting: 'K' if __name__ == '__main__': ok from doctest import testmod 2 items passed all tests: testmod(verbose=True) 1 tests in __main__.verschluesselterText 1 tests in __main__.verschluesseltesZeichen 2 tests in 2 items. automatisierter Modultest in 2 passed and 0 failed. Testprotokoll Python Test passed. 109 Experimente mit Variablen Das Zusammenspiel von Variablen des Hauptprogramms, von Variablen von Unterprogrammen und von Parametern ist nicht so einfach. Mit Hilfe von Experimenten sollen die Zusammenhänge erschlossen werden. def verschluesselterText(klartext, schluessel): geheimtext = '' for zeichen in klartext: nummer = ord(zeichen)-ord('A') neuesZeichen = schluessel[nummer] geheimtext = geheimtext + neuesZeichen print() print('lokale Variablen:') print(locals()) print('globale Variablen:') print(globals()) return geheimtext Programm 1 Aufgabe: Führe das Programm aus. Kannst du anhand der ausgegebenen Daten erschließen, was die Hilfsoperatoren locals() und globals() als Ergebnisse zurückliefern? # Testprogramm zuordnung = 'GKXCSLZUAHWDBMTYENJVPOIRFQ' aktuellerKlartext = 'OBELIX' geheimtext = None aktuellerGeheimtext = verschluesselterText(aktuellerKlartext, zuordnung) print() print(klartext) print(geheimtext) print() print('globale Variablen:') print(globals()) 110 Experimente mit Variablen def verschluesselterText(klartext, schluessel): geheimtext = '' for zeichen in klartext: nummer = ord(zeichen)-ord('A') neuesZeichen = schluessel[nummer] geheimtext = geheimtext + neuesZeichen print() print('lokale Variablen:') print(locals()) print('globale Variablen:') print(globals()) return geheimtext # Testprogramm schluessel = 'GKXCSLZUAHWDBMTYENJVPOIRFQ' klartext = 'OBELIX' geheimtext = None geheimtext = verschluesselterText(klartext, schluessel) print() print(klartext) print(geheimtext) print() print('globale Variablen:') print(globals()) Programm 2 Aufgabe: Beachte, dass hier die Parameter der Funktion verschluesselterText dieselben Namen haben wie die Variablen im Tesprogramm zur Verwaltung der aktuellen Daten. Führe das Programm aus. Welche Schlüsse kannst du anhand der ausgegebenen Daten ziehen? 111 Experimente mit Variablen def textVerschluesseln(): global zuordnung global klartext global geheimtext geheimtext = '' for zeichen in klartext: nummer = ord(zeichen)-ord('A') neuesZeichen = zuordnung[nummer] geheimtext = geheimtext + neuesZeichen print() print('lokale Variablen:') print(locals()) print('globale Variablen:') print(globals()) # Testprogramm zuordnung = 'GKXCSLZUAHWDBMTYENJVPOIRFQ' klartext = 'OBELIX' geheimtext = None textVerschluesseln() print() print(klartext) print(geheimtext) print() print('globale Variablen:') print(globals()) Programm 3 Aufgabe: Führe das Programm aus. Was bewirken Anweisungen wie global geheimtext? Warum funktioniert die Datenverarbeitung mit einer Funktion ohne Rückgabe hier? 112 Experimente mit Variablen def textVerschluesseln(): #global zuordnung global klartext global geheimtext geheimtext = '' for zeichen in klartext: nummer = ord(zeichen)-ord('A') neuesZeichen = zuordnung[nummer] geheimtext = geheimtext + neuesZeichen print() print('lokale Variablen:') print(locals()) print('globale Variablen:') print(globals()) # Testprogramm zuordnung = 'GKXCSLZUAHWDBMTYENJVPOIRFQ' klartext = 'OBELIX' geheimtext = None textVerschluesseln() print() print(klartext) print(geheimtext) print() print('globale Variablen:') print(globals()) Programm 4 Aufgabe: Wir lassen jetzt Schritt für Schritt die Anweisungen global ... weg, indem wir sie auskommentieren. Führe die modifizierten Programme aus. Was stellst du fest? Hast du eine Erklärung hierfür? 113 lokale / globale Variable Eine globale Variable ist (vereinfacht gesagt) eine Variable, die im Hauptprogramm eingeführt wird. Eine lokale Variable ist (vereinfacht gesagt) eine Variable, die nur innerhalb eines Unterprogramms benutzt wird. Beachte, dass die (formalen) Parameter eines Unterprogramms auch zu diesen lokalen Variablen zählen. def d(x): y=x+x return y # Test a=2 b = d(a) print(a) >>> 4 a=2 {a -> 2} b = d(a) -------------------------Funktionsaufruf: d(a) -------------------------{a -> 2} {x -> 2} y=x+x {x -> 2, y -> 4} return y -------------------------Ergebnis: 4 -------------------------{a -> 2; b -> 4} x=2 {x -> 2} y = d(x) ----------------------Funktionsaufruf: d(x) ----------------------{x -> 2} {x -> 2} x=x+x {x -> 4} return x ----------------------Ergebnis: 4 ----------------------{x -> 2; y -> 4} siehe auch: http://people.csail.mit.edu/pgbovine/python/tutor.html#mode=visualize def d(x): x=x+x return x # Test x=2 y = d(x) print(y) >>> 4 114 lokale / globale Variable Eine lokale Variable ist nur innerhalb der Funktionsdefinition, in der sie eingeführt ist, gültig bzw. sichtbar. Anders verhält es sich bei globalen Variablen. Auf eine globale Variable kann man innerhalb einer Funktion lesend zugreifen - sofern dort nicht eine gleichlautende Variable eingeführt ist. def d(): y=x+x return y # Test x=2 y = d() print(y) >>> 4 x=2 {x -> 2} y = d() -------------------------Funktionsaufruf: d() -------------------------{x -> 2} {} y=x+x {y -> 4} return y -------------------------Ergebnis: 4 -------------------------{x -> 2; y -> 4} x=2 {x -> 2} d() ----------------------Funktionsaufruf: d() ----------------------{x -> 2} {} y=x+x {x -> 2; y -> 4} {} ----------------------{x -> 2; y -> 4} def d(): global x global y y=x+x # Test x=2 d() print(y) >>> 4 Verändert man innerhalb einer Prozedur den Wert einer globalen Variablen, so spricht man auch von einem Seiteneffekt. 115 Teil 4 Lotto Datenverwaltung mit Datenstrukturen 116 Lottosimulation Wie wahrscheinlich ist es, dass man beim Lottospielen gewinnt? Diese Frage soll hier mit einem Simulationsprogramm beantwortet werden. Bei der Programmentwicklung werden wir uns mit der Verwaltung vieler Daten beschäftigen. 117 Datenverwaltung mit Listen Die Zahlen einer Lotto-Ziehung (ohne Zusatzzahl) sollen mit Hilfe von Python verwaltet werden. Im Folgenden findest du zwei Vorschläge, wie man das machen könnte. # Version 1 >>> ziehung1 = 25 >>> ziehung2 = 40 ... >>> ziehung1 25 >>> ziehung2 40 ... Verwendung vieler Variablen Aufgaben: (a) Welche Vorteile hat es, wenn man Listen als eine Art "Datencontainer" benutzt? (b) Wie bildet man in Python eine Liste? Wie greift man auf einzelne Listenelemente zu? Wie sind diese durchnummeriert? Was leistet die Funktion len? # Version 2 >>> ziehung = [25, 40, 44, 1, 45, 21] >>> ziehung [25, 40, 44, 1, 45, 21] >>> ziehung[0] 25 >>> ziehung[1] 40 >>> len(ziehung) 6 >>> ziehung[0] = 23 >>> ziehung [23, 40, 44, 1, 45, 21] Verwendung einer Variablen sowie einer Liste als "Datencontainer" 118 Datenverwaltung mit Listen Aufgabe: Ein Tipp soll verwaltet werden. Zwei Möglichkeiten stehen zur Auswahl. Für welche Version sollte man sich entscheiden? Begründe deine Entscheidung. # Version 1 >>> tipp1 = True >>> tipp2 = False ... # Version 2 >>> tipp = [ True , False, False, False, False, False, False, \ False, False, False, False, True , False, False, \ False, False, False, False, False, False, True , \ False, False, False, False, False, False, False, \ False, False, True , False, False, False, False, \ False, True , False, False, False, False, False, \ False, False, False, True , False, False, False, \ ] 119 Eine Liste durchlaufen Die Zahlen einer Lotto-Ziehung (ohne Zusatzzahl) sollen mit einer Liste verwaltet werden. Die Listenelemente sind dabei - beginnend mit 0 - mit einem Index durchnummeriert. # Initialisierung ziehung = [25, 40, 44, 1, 45, 21] # Verarbeitung und Ausgabe i=0 while i < len(ziehung): print(ziehung[i]) i = i+1 Aufgabe: (a) Was leistet das Programm? Kannst du es ohne vorheriges Testen vorhersagen? (b) Ergänze das Programm so, dass man folgende Ausgabe erhält: >>> Kugel Kugel Kugel Kugel Kugel Kugel Liste über den Index durchlaufen 1 2 3 4 5 6 : : : : : : 25 40 44 1 45 21 120 Eine Liste durchlaufen Die Zahlen einer Lotto-Ziehung (ohne Zusatzzahl) sollen mit einer Liste verwaltet werden. Die Listenelemente sind dabei - beginnend mit 0 - mit einem Index durchnummeriert. # Initialisierung ziehung = [25, 40, 44, 1, 45, 21] # Verarbeitung und Ausgabe for z in ziehung: print(z) Aufgabe: Teste auch dieses Programm. Was leistet es? Liste über die Elemente durchlaufen 121 Anzahl der Richtigen Ziel ist, es, die Anzahl der richtig getippten Zahlen mit einem Programm zu ermitteln. Aufgabe: Die folgenden Programm liefert ein Gerüst, mit dem man jede Zahl der Liste ziehung mit jeder Zahl der Liste tipp vergleichen kann. # Initialisierung ziehung = [25, 40, 44, 1, 45, 21] tipp = [1, 12, 21, 31, 37, 46] # Verarbeitung richtige = 0 i=0 while i < len(ziehung): j=0 while j < len(tipp): print('vergleiche: ', ziehung[i], tipp[j]) # ... j = j+1 i = i+1 # Ausgabe print(richtige) # Initialisierung ziehung = [25, 40, 44, 1, 45, 21] tipp = [1, 12, 21, 31, 37, 46] # Verarbeitung richtige = 0 for z in ziehung: for t in tipp: print('vergleiche: ', z, t) # ... # Ausgabe print(richtige) Ergänze die noch fehlenden Vergleiche. 122 Anzahl der Richtigen Ziel ist, es, die Anzahl der richtig getippten Zahlen mit einem Programm zu ermitteln. Aufgabe: Ergänze das Programm so, dass man die Anzahl der Richtigen erhält. # Initialisierung ziehung = [ True , False, False, False, False, False, False, \ False, False, False, False, False, False, False, \ ... ] tipp = [ True , False, False, False, False, False, False, \ False, False, False, False, True , False, False, \ ... ] # Verarbeitung richtige = 0 # ... # Ausgabe print(richtige) Elemente hinzufügen 123 Der folgende Python-Dialog zeigt, wie man eine Liste zur Verwaltung der Zahlen eines Tipps schrittweise aufbauen kann. >>> tipp >>> tipp [] >>> tipp >>> tipp [12] >>> tipp >>> tipp [12, 15] >>> tipp >>> tipp ... >>> tipp >>> tipp ... >>> tipp >>> tipp ... = [] = tipp + [12] = tipp + [15] = [7] + tipp = tipp + [42, 47] = [3] + tipp Listen aneinanderhängen Aufgabe: Analysiere den gezeigten Python-Dialog. Stelle Vermutungen auf, was an Stelle der drei Punkte ... steht. Überprüfe deine Vermutung. 124 Elemente löschen Aus Listen kann man auch wieder Elemente löschen. Bei der hier gezeigten Version wird hierzu eine neue Liste aus Teillisten der gegebenen Liste erzeugt. >>> tipp [3, 7, 12, 15, 42, 47] >>> tipp[0:3] [3, 7, 12] >>> tipp[4:6] [42, 47] >>> tipp[0:3] + tipp[4:6] [3, 7, 12, 42, 47] >>> tipp [3, 7, 12, 15, 42, 47] >>> tipp = tipp[0:3] + tipp[4:6] >>> tipp [3, 7, 12, 42, 47] Zugriff auf Teillisten Aufgabe: Führe selbst entsprechende Dialoge. Kannst du erklären, wie die neue Liste zustande kommt? 125 Ziehung automatisiert erzeugen Ziel ist es, eine Ziehung der Lottozahlen mit Hilfe des Zufallsgenerators von Python zu simulieren. Hier ein erster Vorschlag: from random import * # Verarbeitung ziehung = [] for i in range(6): zahl = randint(1, 49) ziehung = ziehung + [zahl] # Ausgabe print(ziehung) Aufgabe: (a) Analysiere das Programm und erkläre die einzelnen Anweisungen. (b) Teste das Programm mehrfach. Welche Schwierigkeit tritt hier auf. (c) Kannst du die Schwierigkeit beheben? 126 Ziehung automatisiert erzeugen Ziel ist es, eine Ziehung der Lottozahlen mit Hilfe des Zufallsgenerators von Python zu simulieren. Hier ein weiterer Vorschlag: from random import * # Verarbeitung ziehung = [] for i in range(49): ziehung = ziehung + [False] for i in range(6): zahl = randint(1, 49) ziehung[zahl-1] = True # Ausgabe for i in range(49): if ziehung[i] == True: print(i+1) Aufgabe: (a) Teste das folgende Programm. Erkläre, wie hier die Liste zur Verwaltung der Wahrheitswerte aufgebaut wird. (b) Auch hier ist noch etwas nicht in Ordnung. Kannst du die Schwierigkeit durch Ergänzungen im Programm beheben? 127 Fachkonzept - Liste Eine Liste ist eine Datenstruktur zur Verwaltung endlicher Folgen von Daten, bei der man flexibel neue Daten hinzufügen und vorhandene Daten entfernen kann. Zur Darstellung von Listen verwenden wir - wie in Python - eckige Klammern. Alle Elemente einer Liste werden mit Kommata getrennt. Eine besondere Liste ist die leere Liste. Sie enthält keine Elemente. Die Elemente einer Liste können (in unserer Darstellung hier) von beliebigem - also auch unterschiedlichem - Typ sein. Eine Liste kann selbst wieder Listen als Elemente haben. Listen können also geschachtelt werden. [1, 12, 21, 31, 37, 46] ['[email protected]', '[email protected]', '[email protected]', ...] [] [1, 21, 25, 40, 44, 45, ("Zusatzzahl", 3), ("Superzahl", 5)] [[1, 12, 21, 31, 37, 46], [3, 8, 10, 30, 31, 49], [5, 12, 20, 22, 29, 40]] Brot Butter Joghurt Äpfel Schokolade Mehl Eier ... 128 Liste als sequentieller Datentyp Sequentielle Datenobjekte sind in Python zusammengesetzte Datenobjekte, die aus einer Folge von (gleichartigen oder auch verschiedenen) Datenobjekten bestehen. Die Elemente eines solchen sequentiellen Datenobjekts sind durchnummeriert Die Nummerierung beginnt immer bei 0. Die Nummer wird auch Index genannt. Element ziehung 25 40 44 1 45 21 0 1 2 3 4 5 Index Während ein lesender Zugriff auf jedes Element der Sequenz bei allen sequentiellen Datenobjekten möglich ist, ist ein schreibender Zugriff nur bei veränderbaren Datenobjekten (wie Listen) möglich. >>> L = [10, 15, 21, 33, 37, 40] >>> L[0] 10 >>> L[1] lesender Zugriff 15 >>> L = [10, 15, 21, 33, 37, 40] >>> L [10, 15, 21, 33, 37, 40] >>> L[4] = 36 schreibender Zugriff >>> L [10, 15, 21, 33, 36, 40] 129 Liste als sequentieller Datentyp ziehung >>> ziehung = [25, 40, 44, 1, 45, 21] >>> ziehung[0:2] [25, 40] >>> ziehung[2:5] [44, 1, 45] >>> ziehung[3:3] [] Teillisten 25 40 44 1 45 21 0 1 2 3 4 5 Ein Zugriff auf eine Teilliste ist möglich: Wenn L eine Liste bezeichnet, dann beschreibt der Ausdruck L[i:j] die Liste, die alle Elemente der Ausgangsliste L mit den Nummern von i bis j-1 enthält. Beachte, dass diese Teilliste auch leer sein kann. >>> ziehung = [25, 40, 44, 1, 45, 21] >>> ziehung[:2] [25, 40] >>> ziehung[2:] [44, 1, 45, 21] >>> ziehung[:] [25, 40, 44, 1, 45, 21] 130 Liste als sequentieller Datentyp ziehung 25 40 44 1 45 21 0 1 2 3 4 5 Bei der Konkatenation von Listen werden diese zu einer Gesamtliste verbunden. Wenn L und M zwei Listen bezeichnen, dann beschreibt der Ausdruck L+M die Liste, die zunächst alle Elemente von L und danach alle Elemente von M enthält. >>> tippAnfang = [2, 6] >>> tippEnde = [16, 40, 41, 43] >>> tippAnfang + tippEnde [2, 6, 16, 40, 41, 43] >>> tippAnfang + [10] [2, 6, 10] >>> [] + tippAnfang [2, 6] Konkatenation Da Listen dynamisch wachsen oder schrumpfen können, benötigt man häufig eine Operation zur Bestimmung der Länge der Liste. Die Länge einer Liste beschreibt dabei die Anzahl der Listenelemente. Wenn L eine Listen bezeichnet, dann beschreibt der Ausdruck len(L) die Länge der Liste. >>> tippAnfang = [2, 6, 16, 40] >>> len(tippAnfang) 4 >>> len([]) 0 Länge 131 Liste als sequentieller Datentyp ziehung 25 40 44 1 45 21 0 1 2 3 4 5 Mit einem Ausdruck der Gestalt e in L kann man überprüfen, ob das von e verwaltete Datenobjekt in der von L verwalteten Liste vorkommt. Mit einer for-Anweisung der Gestalt for e in L: ... kann man alle Elemente einer Liste (Sequenz) der Reihe nach durchlaufen. >>> >>> >>> False >>> >>> True >>> ziehung = [25, 40, 44, 1, 45, 21] >>> for z in ziehung: ... print(z) ... 25 40 44 1 Durchlaufen einer Liste 45 21 tippAnfang = [2, 6, 16, 40] t = 25 t in tippAnfang t = 40 t in tippAnfang Elementbeziehung Übungen 132 Aufgabe: Teste die Funktion. Entwickle analog eine Funktionsdefinition mit Schnittstellenbeschreibung zur Funktion mitLetztem(e, L). def mitErstem(e, L): # # # # e: Element, das hinzugefügt wird L: bestehende Liste return: neue Liste mit e als erstem Element Beispiele: """ >>> mitErstem('H', ['A', 'L', 'T']) ['H', 'A', 'L', 'T'] >>> mitErstem('O', []) ['O'] """ N = [e] + L return N # neue Liste Übungen 133 Aufgabe: Mit eine Variablen daten wird eine Liste mit Daten (die der Einfachheit halber in Form von Zeichenketten dargestellt sind) verwaltet. Ergänze im folgenden Python-Dialog die Ergebnisse und überprüfe sie anschließend mit Python. >>> >>> ... >>> ... >>> ... >>> ... >>> ... daten = ['a', 'b', 'c', 'd', 'e'] daten[3] ['x'] + daten + ['y'] daten[1:] daten[0:2] + daten[3:len(daten)] daten[0:2] + ['z'] + daten[2:len(daten)] 134 Übungen Aufgabe: Die Funktion ohneElement soll aus der übergebenen Liste L das Element mit der übergebenen Nummer i entfernen. def ohneElement(i, L): N = L[0:i] + L[(i+1):len(L)] return N (a) Warum liefert der Ausdruck L[0:i] + L[(i+1):len(L)] die gewünschte neue Liste? (b) Teste die Funktion mit verschiedenen Testdaten. 135 Übungen Aufgabe: Die Funktion mitNeuemElement soll das übergebene Element x an die Stelle mit der übergebenen Nummer i in einer übergebenen Liste L einfügen. def mitNeuemElement(x, i, L): N = ... return N (a) Ergänze den noch fehlenden Ausdruck in der Funktionsdeklaration. (b) Teste deinen Implementierungsvorschlag. Übungen 136 Aufgabe: Entwickle ein Ersetzungsverfahren und ergänze entsprechend die Funktionsdefinition. def mitErsetztemElement(a, b, L): # # # # a: zu ersetzendes Element b: neues Element L: Liste Beispiele: """ >>> mitErsetztemElement('A', 'U', ['H', 'A', 'N', 'D']) ['H', 'U', 'N', 'D'] >>> mitErsetztemElement('E', 'I', ['H', 'A', 'N', 'D']) ['H', 'A', 'N', 'D'] >>> mitErsetztemElement('L', 'S', ['B', 'A', 'L', 'L']) ['B', 'A', 'S', 'S'] """ # ... 137 Achtung: Fehlerquelle Simone hat sich folgende Lottozahlen als Tipp ausgedacht: 4, 13, 21, 33, 34, 42. Anne gefallen diese Zahlen. Nur die 13, die hätte sie gerne ersetzt durch eine 8. Im folgenden Python-Dialog wird ein Lotto-Tipp kopiert und anschließend etwas abgeändert. Probiere das selbst einmal aus. Was fällt auf? Entsprechen alle Ergebnisse deinen Erwartungen? >>> tipp1 = [4, 13, 21, 33, 34, 42] >>> tipp2 = tipp1 >>> tipp2[1] = 8 >>> tipp2 [4, 8, 21, 33, 34, 42] >>> tipp1 [4, 8, 21, 33, 34, 42] >>> tipp1 = [4, 13, 21, 33, 34, 42] >>> tipp1 [4, 13, 21, 33, 34, 42] >>> tipp2 [4, 8, 21, 33, 34, 42] >>> tipp2[0] = 3 >>> tipp2 [3, 8, 21, 33, 34, 42] >>> tipp1 [4, 13, 21, 33, 34, 42] 138 Verwaltung von Listen mit Variablen Jedes Datenobjekt hat (in Python) eine Identitätsnummer, einen Typ und einen bestimmten Wert. >>> id([4, 13, 21, 33, 34, 42]) 12289008 >>> type([4, 13, 21, 33, 34, 42]) <type 'list'> >>> [4, 13, 21, 33, 34, 42] [4, 13, 21, 33, 34, 42] >>> tipp1 = [4, 13, 21, 33, 34, 42] >>> id(tipp1) 12289008 >>> type(tipp1) <type 'list'> >>> tipp1 [4, 13, 21, 33, 34, 42] Variablen dienen in der Informatik dazu, Datenobjekte zu verwalten. Variablen werden an Datenobjekte angebunden, um die betreffenden Datenobjekte verwalten zu können. Eine Variable, die ein Datenobjekt referenziert, ist eine Art Name für das betreffende Datenobjekt. Mit dem Variablennamen kann man sich die Identitätsnummer, den Typ und den Wert des referenzierten Datenobjekts verschaffen. 139 Verwaltung von Listen mit Variablen >>> tipp1 = [4, 13, 21, 33, 34, 42] >>> tipp2 = tipp1 >>> tipp2[1] = 8 >>> tipp2 [4, 8, 21, 33, 34, 42] >>> tipp1 [4, 8, 21, 33, 34, 42] >>> tipp1 = [4, 13, 21, 33, 34, 42] >>> tipp1 [4, 13, 21, 33, 34, 42] >>> tipp2 [4, 8, 21, 33, 34, 42] >>> tipp2[0] = 3 >>> tipp2 [3, 8, 21, 33, 34, 42] >>> tipp1 [4, 13, 21, 33, 34, 42] 140 Exkurs: Listenverarbeitung funktional: L = Listenkonstruktor >>> L = [1, 2, 3] >>> L [1, 2, 3] >>> id(L) 12283056 >>> L = L + [4] >>> L [1, 2, 3, 4] >>> id(L) 12289008 >>> L = L[1:] >>> L [2, 3, 4] >>> id(L) 12257720 >>> L = [1, 2] + L[1:] >>> L [1, 2, 3, 4] >>> id(L) Erzeugung neuer Listen 12289008 objektorientiert: L.Listenoperation >>> L = [1, 2, 3] >>> L [1, 2, 3] >>> id(L) 12283536 >>> L[1] = 5 >>> L [1, 5, 3] >>> id(L) 12283536 >>> L.remove(5) >>> L [1, 3] >>> id(L) 12283536 >>> L.insert(1, 2) >>> L [1, 2, 3] >>> id(L) 12283536 Veränderung einer bestehenden Listen 141 Anwendung - Gewinnchancen b. Lotto Die Gewinnchancen beim Lottospiel kann man näherungsweise ermitteln, indem man sehr oft einen (evtl. festen) Tipp vorgibt und eine Lotto-Ziehung durchführt. Man muss dann nur mitzählen, wie oft man 0, 1, ..., 6 Richtige hatte. Tipp Ziehung Richtige --------------------------------------------------------------------------[ 1, 12, 21, 31, 37, 46] [25, 40, 44, 1, 45, 21] [0, 0, 1, 0, 0, 0, 0] [ 1, 12, 21, 31, 37, 46] [11, 15, 3, 20, 40, 30] [1, 0, 1, 0, 0, 0, 0] [ 1, 12, 21, 31, 37, 46] [ 6, 49, 32, 18, 19, 24] [2, 0, 1, 0, 0, 0, 0] ... Aufgaben: (a) Entwickle ein Programm zur Ermittlung der Gewinnchancen beim Lotto. Mit dem Programm soll es z.B. möglich sein, zu ermitteln, wie oft man 0, 1, 2, ..., 6 Richtige erhält, wenn man 10000 mal Lotto spielt. Strukturiere das Programm mit Hilfe von Funktionen. (b) Wie lange dauert es im wirklichen Leben, bis 10000 Ziehungen durchgeführt wurden, wenn man von einer Ziehung am Mittwoch und einer am Samstag ausgeht und wenn man einen Tipp pro Spiel abgibt? Beurteile mit den Ergebnissen die Gewinnaussichten beim Lottospiel. 142 Anwendung - Gewinnchancen b. Lotto Wenn man ein Programm zur wiederholten Simulation entwickeln möchte, dann sollte man Funktionen zur Durchführung von Teilaufgaben nutzen. Hier ein Vorschlag zur Modellierung von Funktionen. Datenmodellierung mit Listen 143 Beim Lotto werden insgesamt 7 Kugeln gezogen - 6 Kugeln mit den eigentlichen Lottozahlen und eine weitere Kugel mit der sogenannten Zusatzzahl. Ziel ist es, die Daten einer Ziehungen mit Zusatzzahl zu erfassen. >>> >>> >>> >>> ... >>> ... >>> ... >>> ... gewinnzahlen = [33, 13, 8, 12, 40, 41] zusatzzahl = 2 ziehung = [gewinnzahlen, zusatzzahl] ziehung ziehung[1] ziehung[0] ziehung[0][2] Aufgabe: Was steht vermutlich an Stelle der drei Punkte? Überprüfe deine Vermutung. 144 Datenmodellierung mit Listen Wer beim Lotto mitspielt, füllt einen Tippschein aus. Hier hat man die Möglichkeit, mehrere Tipps auf den Zahlenfeldern einzutragen. Aufgabe: Mache einen Vorschlag, wie man solche Mehrfachtipps mit Listen erfassen könnte. Teste deinen Vorschlag mit einem Python-Dialog. 145 Datenmodellierung mit Tupeln Bisher haben wir Folgen von Daten immer mit Listen erfasst. In Python geht das aber auch anders - mit sogenannten Tupeln. Liste als Datencontainer >>> gewinnzahlen = [33, 13, 17, 12, 40, 41] >>> gewinnzahlen[1] 13 >>> gewinnzahlen[1] = 17 >>> gewinnzahlen [33, 17, 17, 12, 40, 41] >>> gewinnzahlen = [33, 13, 8, 12, 40, 41] >>> zusatzzahl = 2 >>> ziehung = (gewinnzahlen, zusatzzahl) >>> ziehung ([33, 13, 8, 12, 40, 41], 2) >>> ziehung[1] 2 Tupel und Liste >>> ziehung[0] als Datencontainer [33, 13, 8, 12, 40, 41] Tupel als Datencontainer >>> gewinnzahlen = (33, 13, 17, 12, 40, 41) >>> gewinnzahlen (33, 13, 17, 12, 40, 41) >>> gewinnzahlen[1] 13 >>> gewinnzahlen[1] = 17 Traceback (most recent call last): File ... gewinnzahlen[1] = 17 TypeError: 'tuple' object does not support item assignment Aufgabe: Vergleiche die verschiedenen Datenmodellierungen. 146 Fachkonzept - Tupel Eine Tupel ist eine Datenstruktur, bei der mehrere Daten zu einer Einheit zusammengefasst werden. Tupel weisen einige Ähnlichkeiten mit Listen auf, unterscheiden sich aber in einigen Punkten wesentlich von Listen. Ein lesender Zugriff auf Tupelelemente erfolgt genau wie bei Listen. Ein schreibender Zugriff auf Tupelelemente ist anders als bei Listen - nicht möglich. Man kann ein Tupel nicht dadurch abändern, dass man ein Element durch ein anderes ersetzt. Wenn man ein Tupel abändern will, muss man ein neues Tupelobjekt erzeugen. >>> gewinnzahlen = (33, 13, 8, 12, 40, 41) >>> zusatzzahl = 2 >>> ziehung = (gewinnzahlen, zusatzzahl) >>> ziehung ((33, 13, 8, 12, 40, 41), 2) Tupel zusammenpacken >>> gewinnzahlen = (33, 13, 17, 12, 40, 41) >>> gewinnzahlen (33, 13, 17, 12, 40, 41) >>> gewinnzahlen[1] 13 >>> gewinnzahlen[1] = 17 Traceback (most recent call last): File ... gewinnzahlen[1] = 17 TypeError: 'tuple' object does not support item assignment >>> ziehung = ((33, 13, 8, 12, 40, 41), 2) >>> (gewinnzahlen, zusatzzahl) = ziehung >>> gewinnzahlen (33, 13, 8, 12, 40, 41) >>> zusatzzahl 2 Tupel auspacken 147 Fachkonzept - Datenstruktur Datenstrukturen ermöglichen es, strukturierte Daten als Einheit zu verwalten. >>> lottoschein = [ [(3, 6, 19, 21, 38, 41), (4, 9, 17, 37, 38, 39), (18, 23, 28, 36, 38, 46)], ('Losnummer', 4440674), ('Superzahl', 4), ('Ziehungstage', 'Sa + Mi'), ('Laufzeit', 1), ('Spiel 77', False), ('Super 6', False), ('Gluecksspirale', False) ] Datenmodellierung mit den Datenstrukturen "Liste" und "Tupel" 148 Übungen - Datenstruktur Aufgabe 1: Zugriff auf geschachtelte Daten Ergänze die Ergebnisse. Überprüfe sie mit Python. >>> lottoschein = [ [(3, 6, 19, 21, 38, 41), (4, 9, 17, 37, 38, 39), (18, 23, 28, 36, 38, 46)], ('Losnummer', 4440674), ('Superzahl', 4), ('Ziehungstage', 'Sa + Mi'), ('Laufzeit', 1), ('Spiel 77', False), ('Super 6', False), ('Gluecksspirale', False) ] >>> lottoschein[0][1] ... >>> lottoschein[0][1][2] ... >>> lottoschein[1][0] ... Aufgabe 2: Datenmodellierung Wie könnte man die Gewinnquoten beim Lotto mit Datenstrukturen erfassen?