Lektion 1.1

Algorithmik und Programmierung

20 min Lesezeit

Ein Programm soll den Durchschnitt einer Notenliste berechnen. Es läuft ohne Fehlermeldung durch und zeigt 3,1, obwohl 2,6 richtig wäre. Der Rechner hat genau das getan, was im Programm steht. Der Fehler liegt in der Beschreibung des Vorgehens.

Du vertiefst, was du aus der Mittelstufe in Python kennst: Algorithmen genau beschreiben, Typumwandlungen, Kontrollstrukturen, Funktionen, Testen und ereignisgesteuerte Programme.

Definition

Algorithmus und seine Eigenschaften

Ein Algorithmus ist eine Vorschrift aus endlich vielen Einzelschritten, die jede Eingabe einer Problemklasse in eine Ausgabe überführt. Man verlangt meist:

  • Eindeutigkeit und Ausführbarkeit: Jeder Schritt ist genau festgelegt und wirklich ausführbar.
  • Endlichkeit: Die Vorschrift hat endliche Länge.
  • Terminierung: Für jede zulässige Eingabe endet der Ablauf nach endlich vielen Schritten.
  • Allgemeinheit: Er löst eine ganze Problemklasse, nicht nur eine einzelne Aufgabe.
  • Determiniertheit: Gleiche Eingabe liefert gleiches Ergebnis.

Ein Algorithmus hängt nicht von einer Sprache ab. Ein Programm ist seine Umsetzung in einer Programmiersprache.

Definition

Variablen, Datentypen, Typumwandlung und Zeichenketten

Eine Variable hat einen Namen und verweist auf einen Wert mit einem Datentyp (int\texttt{int}, float\texttt{float}, bool\texttt{bool}, str\texttt{str}). Bei der Zuweisung x = x + 1\texttt{x = x + 1} wird zuerst die rechte Seite berechnet und dann in x gespeichert. Eine Typumwandlung überführt einen Wert in einen anderen Typ:

AusdruckErgebnisArt
int("12") + 1\texttt{int("12") + 1}13explizit
2 + 0.5\texttt{2 + 0.5}2.5implizit, int wird zu float
"3" + 4\texttt{"3" + 4}Laufzeitfehler (TypeError)keine Umwandlung
int("abc")\texttt{int("abc")}Laufzeitfehler (ValueError)nicht umwandelbar

Eine Zeichenkette ist eine Folge von Zeichen mit Index ab 0. Für s = "Informatik"\texttt{s = "Informatik"} gilt: len(s)\texttt{len(s)} ist 10, s[0]\texttt{s[0]} ist "I", s[-1]\texttt{s[-1]} ist "k", und die Teilkette s[2:5]\texttt{s[2:5]} ist "for" (Index 2 bis 4). Mit +\texttt{+} hängt man Zeichenketten aneinander. Sie sind unveränderlich: s[0] = "i"\texttt{s[0] = "i"} ist ein Fehler, man erzeugt eine neue Zeichenkette.

Definition

Kontrollstrukturen, Pseudocode und Struktogramm

Strukturierte Algorithmen bestehen aus Sequenz, Verzweigung und Wiederholung. Eine Schleife prüft ihre Bedingung vor dem Durchlauf (kopfgesteuert, läuft eventuell nie) oder danach (fußgesteuert, läuft mindestens einmal). Eine zählergesteuerte Schleife hat eine bekannte Zahl von Durchläufen. Pseudocode ist Text ohne feste Syntax. Ein Struktogramm (Nassi-Shneiderman) zeichnet dieselben Bausteine als ineinander liegende Rechtecke mit je einem Eingang und Ausgang.

BausteinPseudocodeStruktogrammPython
SequenzAnweisungen untereinanderRechtecke übereinanderZeilen untereinander
Verzweigungfalls B dann ... sonst ...Rechteck mit Dreieck, Spalten „ja“ und „nein“if B:\texttt{if B:} ... else:\texttt{else:}
kopfgesteuertsolange B tue ...Rahmen, Bedingung obenwhile B:\texttt{while B:}
fußgesteuertwiederhole ... bis BRahmen, Bedingung untenwhile True:\texttt{while True:} und am Ende if B: break\texttt{if B: break}
zählergesteuertfür i von 1 bis nRahmen, Kopf „i = 1 bis n“for i in range(1, n + 1):\texttt{for i in range(1, n + 1):}

Eine Tracetabelle hält nach jedem Schritt die Werte aller Variablen fest. Mit ihr führst du einen Algorithmus von Hand aus und erkennst, was er berechnet und ob er terminiert.

Beispiel

Analyse mit Tracetabelle

Die Funktion wird mit f(4729)\texttt{f(4729)} aufgerufen. Was berechnet sie?

ZeileEinrückungAnweisung
10def f(n):\texttt{def f(n):}
21s = 0\texttt{s = 0}
31while n > 0:\texttt{while n > 0:}
42s = s + n % 10\texttt{s = s + n \% 10}
52n = n // 10\texttt{n = n // 10}
61return s\texttt{return s}

Die Tracetabelle zeigt die Werte nach jedem Durchlauf der Schleife:

Zeitpunktns
vor der Schleife47290
nach Durchlauf 14729
nach Durchlauf 24711
nach Durchlauf 3418
nach Durchlauf 4022

Dann ist n > 0 falsch, und die Funktion gibt 22 zurück. Jeder Durchlauf addiert die letzte Ziffer (n % 10\texttt{n \% 10}) und schneidet sie ab (n // 10\texttt{n // 10}). Die Funktion berechnet die Quersumme: 4 + 7 + 2 + 9 = 22. Sie terminiert, weil n in jedem Durchlauf kleiner wird und nach so vielen Durchläufen 0 erreicht, wie n Ziffern hat. Spezifikation: Eingabe ist eine ganze Zahl n≥0n \geq 0, Ausgabe ihre Quersumme.

Definition

Funktionen, Parameter und Gültigkeitsbereich

Eine Funktion liefert mit return\texttt{return} einen Rückgabewert, eine Prozedur keinen (in Python automatisch None\texttt{None}). Die Namen in def f(a, b):\texttt{def f(a, b):} sind formale Parameter, die Werte beim Aufruf f(3, 4)\texttt{f(3, 4)} aktuelle Parameter.

Eine lokale Variable (auch jeder Parameter) gilt nur während des Aufrufs und nur in der Funktion. Eine globale Variable steht außerhalb von Funktionen und ist überall lesbar. Weist eine Funktion einem Namen einen Wert zu, entsteht eine neue lokale Variable, auch bei gleichem globalem Namen. Nur mit global x\texttt{global x} ändert sie die globale. Besser sind Parameter und Rückgabewert.

Bei einem Wertparameter bekommt die Funktion eine Kopie, das Original bleibt unverändert. Bei einem Referenzparameter bekommt sie einen Verweis auf das Original, Änderungen sind danach außen sichtbar. Python übergibt immer Verweise. Bei Zahlen und Zeichenketten (unveränderlich) wirkt das wie ein Wertparameter, bei Listen (veränderlich) wie ein Referenzparameter.

Definition

Vom Problem zum getesteten Programm

Einen einfachen Algorithmus entwickelst du in vier Schritten. Die Spezifikation legt Eingabe, Ausgabe und Sonderfälle fest. Der Entwurf in Pseudocode oder Struktogramm klärt die Idee ohne Syntax. Die Implementierung setzt ihn in Python um. Das Testen prüft ihn gegen die Spezifikation.

Die Fehlerarten kennst du: Syntaxfehler verhindern den Start, Laufzeitfehler brechen den Ablauf ab, Semantikfehler liefern ein falsches Ergebnis. Auch eine Schleife, die nie endet, ist ein solcher Fehler: Die Terminierung ist verletzt. Ein Testfall besteht aus einer Eingabe und dem erwarteten Ergebnis, das du aus der Spezifikation bestimmst, nicht aus dem Programm. Wähle normale Fälle und Grenzfälle (leere Eingabe, kleinster und größter Wert). Testen kann Fehler zeigen, aber nicht beweisen, dass keiner mehr vorhanden ist.

Definition

Grafische Oberflächen und ereignisgesteuerte Programmierung

Eine grafische Benutzeroberfläche (GUI) besteht aus Bedienelementen wie Fenster, Beschriftung, Knopf und Textfeld. Das Programm läuft nicht von oben nach unten durch, sondern startet eine Ereignisschleife, die auf Ereignisse (Klick, Tastendruck) wartet. Für jedes Ereignis ruft sie eine vorher angemeldete Funktion auf, die Ereignisbehandlung. Diese Arbeitsweise heißt ereignisgesteuert.

Beispiel

Ein Zähler-Knopf

Was passiert bei drei Klicks auf den Knopf, und was, wenn Zeile 4 fehlt?

ZeileEinrückungAnweisung
10import tkinter as tk\texttt{import tkinter as tk}
20zaehler = 0\texttt{zaehler = 0}
30def klick():\texttt{def klick():}
41global zaehler\texttt{global zaehler}
51zaehler = zaehler + 1\texttt{zaehler = zaehler + 1}
61anzeige.config(text=str(zaehler))\texttt{anzeige.config(text=str(zaehler))}
70fenster = tk.Tk()\texttt{fenster = tk.Tk()}
80anzeige = tk.Label(fenster, text="0")\texttt{anzeige = tk.Label(fenster, text="0")}
90anzeige.pack()\texttt{anzeige.pack()}
100tk.Button(fenster, text="+1", command=klick).pack()\texttt{tk.Button(fenster, text="+1", command=klick).pack()}
110fenster.mainloop()\texttt{fenster.mainloop()}

Zeile 10 meldet klick\texttt{klick} ohne Klammern als Ereignisbehandlung an: Die Funktion soll erst beim Klick laufen. Zeile 11 startet die Ereignisschleife. Bei jedem Klick wird zaehler um 1 größer, nach drei Klicks zeigt die Beschriftung 3. str\texttt{str} wandelt die Zahl in die Zeichenkette um, die text\texttt{text} erwartet. Fehlt Zeile 4, ist zaehler in klick\texttt{klick} lokal, und Zeile 5 bricht beim Lesen mit einem Laufzeitfehler (UnboundLocalError) ab.

Häufige Fehler

Eine Funktion ändert die Variable des Aufrufers

„Mit x = x + 1\texttt{x = x + 1} in der Funktion habe ich die Variable a geändert, die ich übergeben habe.“

Richtig ist

Der Parameter x ist lokal. Die Zuweisung bindet x an einen neuen Wert, a bleibt unverändert. Von außen ändern lässt sich nur ein veränderliches Objekt wie eine Liste, etwa mit append\texttt{append}. Sonst gibt man das Ergebnis mit return\texttt{return} zurück.

Häufige Fehler

Alle Tests bestanden heißt fehlerfrei

„Meine fünf Testfälle laufen durch, also ist das Programm richtig.“

Richtig ist

Testen zeigt nur, dass bei den geprüften Eingaben das Richtige herauskommt. Ein Fehler in einem nicht geprüften Fall bleibt unbemerkt. Darum gehören Grenzfälle in jede Testreihe.

Zusammenfassung

  • Ein Algorithmus ist eindeutig, ausführbar, endlich beschrieben, terminiert und löst eine Problemklasse.

  • Typumwandlungen sind explizit (int\texttt{int}, str\texttt{str}) oder implizit. Zeichenketten sind unveränderlich, der Index beginnt bei 0.

  • Sequenz, Verzweigung und Schleife lassen sich als Pseudocode, Struktogramm und Python darstellen. Die Tracetabelle zeigt, was ein Algorithmus berechnet.

  • Parameter und lokale Variablen gelten nur im Aufruf. Zahlen wirken wie Wertparameter, Listen wie Referenzparameter.

  • Spezifikation, Entwurf, Implementierung und Test folgen aufeinander. Testfälle kommen aus der Spezifikation und enthalten Grenzfälle.

  • Eine GUI wartet in einer Ereignisschleife und ruft für jedes Ereignis die angemeldete Ereignisbehandlung auf.

Übungen

Aufgabe 1Numerisch

Es läuft a = 1\texttt{a = 1}, danach für i von 1 bis 4 (for i in range(1, 5):\texttt{for i in range(1, 5):}) die Anweisung a = a * i + 1\texttt{a = a * i + 1}. Welchen Wert hat a am Ende?

Aufgabe 2Multiple Choice

Was liefert der Ausdruck "7" + "5"\texttt{"7" + "5"}?

Aufgabe 3Multiple Choice

Es gilt a = [1]\texttt{a = [1]} und b = 1\texttt{b = 1}. Die Funktion p(l)\texttt{p(l)} führt l.append(9)\texttt{l.append(9)} aus, die Funktion q(n)\texttt{q(n)} führt n = n + 1\texttt{n = n + 1} aus. Was sind a und b nach p(a)\texttt{p(a)} und q(b)\texttt{q(b)}?

Aufgabe 4Multiple Choice

Eine Funktion startet mit n = 7 und wiederholt while n != 0:\texttt{while n != 0:} die Anweisung n = n - 2\texttt{n = n - 2}. Welche Eigenschaft eines Algorithmus ist verletzt?

Aufgabe 5Freitext

Eine Funktion anzahl(text, zeichen)\texttt{anzahl(text, zeichen)} soll zählen, wie oft ein Zeichen in einer Zeichenkette vorkommt. Beschreibe den Algorithmus und nenne drei Testfälle mit erwartetem Ergebnis, darunter einen Grenzfall.