zurück rauf weiter Englisch Index

Kapitel 4

Verzweigung und Rekursion

4.1 Der Modulo-Operator

Der Modulo-Operator arbeitet mit Ganzzahlen (und ganzzahligen Ausdrücken) und ermittelt den Rest bei der Division des ersten Operanden durch den zweiten. In Python ist der Modulo-Operator ein Prozentzeichen (%). Die Syntax ist dieselbe wie für andere Operatoren:

>>> quotient = 7 / 3
>>> print quotient
2
>>> remainder = 7 % 3
>>> print remainder
1

Also ist 7 dividiert durch 3 gleich 2 mit 1 Rest.

Es stellt sich heraus, dass der Modulo-Operator unerwartet nützlich ist. Zum Beispiel kann man mit ihm überprüfen, ob eine Zahl durch eine andere teilbar ist ... wenn x % y null ist, dann ist x durch y teilbar.

Ebenso kann man mit ihm die am weitesten rechts stehende(n) Ziffer(n) einer Zahl ermitteln. Zum Beispiel liefert x % 10 die rechteste Ziffer von x (in der Basis 10). Ähnlich liefert x % 100 die letzten zwei Ziffern.

4.2 Boolesche Ausdrücke

Ein boolescher Ausdruck ist eine Ausdruck, der entweder wahr oder falsch ist. In Python hat ein Ausdruck, der wahr ist, den Wert True (in älteren Versionen: 1) und ein Ausdruck, der falsch ist, den Wert False (in älteren Versionen 0).

Der Operator == vergleicht zwei Werte und erzeugt einen booleschen Ausdruck:

>>> 5 == 5
True
>>> 5 == 6
False
>>>

In der ersten Anweisung sind die zwei Operanden gleich, daher wird der Ausdruck zu True (wahr) ausgewertet; in der zweiten Anweisung ist 5 nicht gleich 6, daher erhalten wir False (falsch).

Der == - Operator ist einer der Vergleichsoperatoren; die anderen sind:

      x != y               # x ist nicht gleich y
      x > y                # x ist größer als y
      x < y                # x ist kleiner als y
      x >= y               # x ist grö&szlig;er oder gleich y
      x <= y               # x is kleiner oder gleich y

Obwohl diese Operatoren dir vielleicht vertraut sind, sind doch die Symbole, die Python verwendet, verschieden von den mathematischen Symbolen. Ein häufiger Fehler ist es, eine einzelnes Gleichheitszeichen (=) anstatt eines doppelten Gleichheitszeichens (==) zu verwenden. Merke dir, dass = ein Zuweisungsoperator ist und => ein Vergleichsoperator. Des Weiteren gibt es (in Python) auch weder =< noch =>.

4.3 Logische 0peratoren

Es gibt drei logische Operatoren: and, or, und not. Die Semantik (Bedeutung) dieser Operatoren ist ähnlich ihrer Bedeutung in der Umgangssprache. Zum Beispiel ist x > 0 und x < 10 nur wahr, wenn x größer als 0 und kleiner als 10 ist.

n%2 == 0 or n%3 == 0 ist wahr, wenn eine der beiden Bedingungen wahr ist, d.h wenn die Zahl durch 2 oder durch 3 teilbar ist.

Schließlich liefert der not Operator die Negation eines booleschen Ausdrucks, so ist etwa not(x > y) wahr, wenn (x > y) falsch ist, d. h. wenn x kleiner oder gleich y ist.

Streng genommen sollten die Operanden eines logischen Operators boolesche Ausdrücke sein, aber Python ist hier nicht sehr streng. Jede Zahl ungleich null wird als "wahr" interpretiert.

>>>  x = 5
>>>  x and True
True
>>>  y = 0
>>>  y and True
0                  # (!) ist äquivalent zu False

Im allgemeinen werden Dinge dieser Art nicht als guter (Programmier-)stil betrachtet. Wenn du eine Zahl mit null vergleichen möchtest, dann tu es explizit.

4.4 Bedingte Ausführung

Damit wir nützliche Programme schreiben können, müssen wir fast immer im Stande sein, Bedingungen zu überprüfen und das Verhalten des Programms entsprechend zu ändern. Bedingte Anweisungen, (oder Verzweigungen,) ermöglichen uns das. Die einfachste Form ist die if - Anweisung.

if x > 0:
  print "x is positive"

Den booleschen Ausdruck nach dem Schlüsselwort if nennt man die Bedingung. Wenn sie wahr ist, dann werden die eingerückten Anweisungen danach ausgeführt. Wenn nicht, dann geschieht nichts.

Wie andere zusammengesetzte Anweisungen, wird die if-Anweisung aus einem Kopf und einem Anweisungsblock gebildet:

KOPF:
  ERSTE ANWEISUNG
  ...
  LETZTE ANWEISUNG

Der Kopf beginnt in einer neuen Zeile und endet mit einem Doppelpunkt (:). Die darauf folgenden eingerückten Anweisungen werde ein Block genannt. Die erste nicht mehr eingerückte Anweisung kennzeichnet das Ende des Blocks. Ein Anweisungs-Block im Inneren einer zusammengesetzten Anweisung wird der Körper dieser Anweisung genannt.

Es gibt keine Obergrenze für die Anzahl der Anweisungen, die der Körper einer if-Anweisung enthalten kann, aber er muss mindestens eine Anweisung enthalten. Gelegentlich ist es nützlich einen Körper ohne Anweisungen zu haben (im Allgemeinen als Platzhalter für Code, der erst geschrieben werden muss). In diesem Fall, kann man die pass-Anweisung verwenden, die nichts tut (außer eine Anweisung zu sein).

4.5 Alternative Ausführung

Eine zweite Form der if-Anweisung ist die alternative Ausführung, bei der es zwei Möglichkeiten gibt und die Bedingung entscheidet, welche von beiden ausgeführt wird. Ihre Syntax schaut so aus:

if x%2 == 0:
  print x, "ist gerade"
else:
  print x, "ist ungerade"

Wenn der Rest bei der Division von x durch 2 gleich 0 ist, dann wissen wir, dass x gerade ist und das Programm zeigt eine Meldung an, dass das so ist. Wenn die Bedingung falsch ist, wird der zweite Block ausgeführt. Da die Bedingung wahr oder falsch sein muss, wird genau eine dieser beiden Alternativen ausgeführt. Diese Alternativen heißen Zweige, weil sie Zweige in der Programmausführung sind.

Nebenbei gesagt, wenn du die Eigenschaft einer Zahl, gerade oder ungerade zu sein, oft prüfen musst, kannst du diesen Code in eine Funktion "verpacken":

def printParity(x):
  if x%2 == 0:
    print x, "ist gerade"
  else:
    print x, "ist ungerade"

Für jeden Wert von x wird die Funktion printParity eine passende Meldung ausgeben. Wenn du sie aufrufst, kannst du ihr jeden beliebigen Ausdruck vom Typ int als Argument übergeben.

>>> printParity(17)
17 ist ungerade
y = 99
>>> printParity(y+1)
100 ist gerade

4.6 Mehrfache Verzweigungen

Manchmal gibt es mehr als zwei Möglichkeiten und wir brauchen mehr als zwei Zweige. Ein Weg eine Berechnung dieser Art auszudrücken ist eine Mehrfache Verzweigung:

if x < y:
  print x, "ist kleiner als", y
elif x > y:
  print x, "is grö&szlig;er als", y
else:
  print x, "und", y, "sind gleich"

elif ist eine Abkürzung von "else if". Wieder wird genau ein Zweig ausgeführt. Es gibt keine Obergrenze für die Anzahl von elif-Anweisungen, aber der letzte Zweig muss eine else-Anweisung sein.

if choice == 'A':
  functionA()
elif choice == 'B':
  functionB()
elif choice == 'C':
  functionC()
elif choice == 'D':
  functionD()
else:
  print "Invalid choice."

Alle Bedingungen werden der Reihe nach überprüft. Wenn die erste falsch ist, wird die nächste geprüft und so weiter. Wenn eine wahre Bedingung angetroffen wird, wird der entsprechende Zweig ausgeführt und die ganze if-Anweisung beendet. Auch wenn mehr als eine Bedingung wahr ist, wird nur der erste wahre Zweig ausgeführt.

Als Übung formuliere diese Beispiele als Funktionen mit den Namen compare(x, y) und dispatch(choice). Um die zweite zu testen sind dann natürlich noch vier Funktionen functionA(), ... functionD() zu schreiben, die im einfachsten Fall vielleicht nur ausgeben, dass sie aufgerufen wurden.

4.7 Verschachtelte Verzweigungen

Verzweigungsanweisungen können auch ineinander geschachtelt werden. Wir hätten das Beispiel mit der Dreifachverzweigung auch so schreiben können:

if x == y:
  print x, "und", y, "sind gleich"
else:
  if x < y:
    print x, "ist kleiner als", y
  else:
    print x, "ist grö&szlig;er als", y

Die äußere Verzweigung enthält zwei Zweige. Der erste Zweig enthält eine einfache Ausgabe-Anweisung. Der zweite Zweig enthält eine weitere if-Anweisung, die selbst wieder zwei Zweige enthält. Diese zwei Zweige sind beide Ausgabe-Anweisungen, obwohl sie ebensogut wieder Verzweigungs-Anweisungen hätten sein können.

Obwohl die Einrückung von Anweisungen die Struktur verdeutlicht, können verschachtelte Verzweigungen sehr schnell schwer lesbar werden. Im Allgemeinen ist es eine gute Idee, sie zu vermeiden, wo es geht.

Logische Operatoren ermöglichen es oft, geschachtelte Verzweigungen zu vereinfachen. Man kann zum Beispiel den folgenden Code auch mit einer Verzweigung schreiben:

if 0 < x:
  if x < 10:
    print "x is a positive single digit."

Die print-Anweisung im Körper der inneren bedingten Anweisung wird nur ausgeführt wenn beide Bedingungen wahr sind, daher können wir dafür den and - Operator verwenden:

if 0 < x and x < 10:
  print "x is a positive single digit."

Diese Art von Bedingungen kommt häufig vor, daher stellt Python dafür eine alternative Syntax zur Verfügung, die der mathematischen Schreibweise ähnlich ist:

if 0 < x < 10:
  print "x is a positive single digit."

Diese Bedingung ist semantisch die gleiche wie der zusammengesetzte boolesche Ausdruck und wie die geschachtelte bedingte Anweisung.

4.8 Die return Anweisung

Die return Anweisung ermöglicht dir, die Ausführung einer Funktion zu beenden, bevor das Ende des Codes erreicht ist. Ein Grund, sie zu verwenden, ist das Eintreten einer Fehlerbedingung.

import math

def printLogarithm(x):
  if x <= 0:
    print "Nur positive Zahlen, bitte."
    return

  result = math.log(x)
  print "Der log von x ist", result

Die Funktion printLogarithm hat einen Parameter mit Namen x. Die erste Sache, die sie tut, ist zu überprüfen ob x kleiner oder gleich 0 ist. In diesem Fall zeigt sie eine Fehlermeldung an und benützt die return-Anweisung um die Funktion zu verlassen. Der Programmablauf kehrt unmittelbar zum aufrufenden Code zurück und die restlichen Zeilen der Funktion werden nicht ausgeführt.

Denke daran: um eine Funktion aus dem math-Modul zu verwenden, musst du diesen importieren.

4.9 Rekursion

Wie erwähnten, dass es legal ist (also den Syntax-Regeln entspricht), dass eine Funktion eine andere aufruft und du hast mehrere Beispiel dafür gesehen. Wir vergaßen dabei zu erwähnen, dass es ebenso legal ist, dass eine Funktion sich selbst aufruft. Es mag vielleicht nicht offensichlich sein, warum das eine gute Sache ist, aber es wird sich als eines der geradezu magischsten und interessantesten Dinge herausstellen, die ein Programm tun kann.

Betrachten wir zum Beispiel folgende Funktion:

def countdown(n):
  if n == 0:
    print "Blastoff!"
  else:
    print n
    countdown(n-1)

countdown() erwartet, dass der Parameter n eine positive ganze Zahl ist.Wenn n gleich 0 ist, wird das Wort "Blastoff!" ausgegeben. Andernfalls wird n ausgegeben und dann and eine Funktion namens countdown()     sie selbst     aufgerufen, wobei ihr n-1 als Argument übergeben wird.

Was geschieht, wenn wir diese Funktion in folgender Weise aufrufen:

>>> countdown(3)

Die Ausführung von countdown() beginnt mit n=3 und weil n nicht gleich 0 ist, wird der Wert 3 ausgegeben und sie selbst aufgerufen ...

Die Ausführung von countdown() beginnt mit n=2 und weil n nicht gleich 0 ist, wird der Wert 3 ausgegeben und sie selbst aufgerufen ...
Die Ausführung von countdown() beginnt mit n=1 und weil n nicht gleich 0 ist, wird der Wert 3 ausgegeben und sie selbst aufgerufen ...
Die Ausführung von countdown() beginnt mit n=0 und weil n gleich 0 ist, wird das Wort "Blastoff!" ausgegeben und die Programmausführung endet.

Das countdown() das den Parameterwert n=1 erhalten hat, beendet seine Ausführung.

Das countdown() das den Parameterwert n=2 erhalten hat, beendet seine Ausführung.

Das countdown() das den Parameterwert n=3 erhalten hat, beendet seine Ausführung.

Und dann sind wir zurück in __main__ (was für ein Trip!) Daher sieht die ganze Ausgabe so aus:

3
2
1
Blastoff!

Als zweites Beispiel betrachten wir noch einmal die Funtionen newLine() and threeLines():

def newline():
  print

def
threeLines():
  newLine()
  newLine()
  newLine()

Die funktionieren zwar, aber sie sind nur von mäßigem Nutzen, wenn wir 2 oder 106 Leerzeilen ausgeben möchten. Eine bessere Alternative wäre dies:

def nLines(n):
  if n > 0:
    print
    nLines(n-1)

Diese Programm ist ähnlich zu countdown(); solange n größer als 0 ist, gibt es eine Leerzeile aus und ruft dann sich selbst auf, um n-1 weitere Leerzeilen auszugeben. Daher ist die Gesamtzahl von ausgegebenen Leerzeilen 1 + (n - 1), was , wenn du deine Algebrakenntnisse richtig anwendest, n ergibt.

Der Prozess, der darin besteht, dass eine Funktion sich selbst aufruft heißt Rekursion und solche Funktionen nennt man rekursiv.

4.10 Stackdiagramme für rekursive Funktionen

Im Abschnitt 3.11, haben wir ein Stackdiagramm benutzt, um den Zustand eines Programms während eines Funktionsaufrufs darzustellen. Dieselbe Art von Diagramm kann auch hilfreich sein, um eine rekursive Funktion zu interpretieren.

Jedesmal, wenn eine Funktion aufgerufen wird, erzeugt Python einen neuen Funktions-Rahmen, der die lokalen Variablen und die Parameter der Funktion enthält. Für eine rekursive Funktion, kann durchaus mehr als ein Rahmen gleichzeitig auf dem Stapel (stack) sein.

Die folgende Abbildung zeigt ein Stackdiagramm für countdown aufgerufen mit n = 3:

Wie üblich, ist der oberste Rahmen des Stacks der Rahmen für __main__(). Er ist leer, denn wir haben keine Variablen in __main__() erzeugt oder Parameter an es übergeben.

Die vier countdown() Rahmen haben verschiedene Werte für den Parametern n. Der unterste Rahmen des Stacks, wo n=0 steht, heißt der Basis-Fall. Er macht keinen rekursiven Aufruf, daher gibt es auch keine weiteren Rahmen.

Als Übung zeichne eine Stackdiagramm für nLines(), das mit n=4 aufgerufen wurde.

4.11 Unendliche Rekursion

Wenn eine Rekursion nie einen Basis-Fall erreicht, dann fährt sie ohne Ende fort, rekursive Aufrufe zu machen und das Programm endet niemals. Das ist als unendliche Rekursion bekannt, und wird im allgemeinen nicht als eine gute Idee betrachtet. Hier ist eine minimales Programm mit einer unendlichen Rekursion:

def recurse():
  recurse()

In den meisten Progammierumgebungen läuft ein Programm mit unendlicher Rekursion nicht wirklich endlos. Pythen gibt eine Fehlermeldung aus, wenn die maximale Rekursionstiefe erreicht ist.

  File "<stdin>", line 2, in recurse
  (98 repetitions omitted)
  File "<stdin>", line 2, in recurse
RuntimeError: Maximum recursion depth exceeded

Diese Zurückverfolgung der Aufrufe ist etwas größer, als die, die wir im vorigen Kapitel gesehen haben. Der Fehler tritt auf, wenn 100 recurse-Rahmen auf dem Stack sind.

Als Übung schreibe eine Funktion mit unendlicher Rekursion und lasse sie vom Python-Interpreter ausführen.

4.12 Tastatureingabe

Die Programme, die wir bis jetzt geschrieben haben, sind insofern ein bißchen primitiv, als sie keine Eingaben des Benutzers verarbeiten können. Sie machen jedesmal dasselbe, wenn sie ablaufen.

Python stellt eingebaute Funktionen zur Verfügung, die Eingaben von der Tastatur entgegennehmen. Die einfachste heißt raw_input. Wenn diese Funktion aufgerufen wird, hält das Programm an und wartet darauf, dass der Benutzer etwas eintippt. Wenn der Benutzer danach die Eingabetaste dürckt, fährt das Programm fort und raw_input gibt aus, was der Benutzer als string eingetippt hat.

>>> input = raw_input ()
What are you waiting for?
>>> print input
What are you waiting for?

Bevor man raw_input aufruft, ist es eine gute Idee, eine Meldung auf den Bildschirm zu schreiben, die dem Benutzer mitteilt, was er da eingeben soll. Eine solche Meldung nennt man Prompt. Wir können einen Prompt als Argument der raw_input-Funktion übergeben:

>>> name = raw_input ("What...is your name? ")
What...is your name? Arthur, King of the Britons!
>>> print name
Arthur, King of the Britons!

Wenn wir erwarten, dass die Antwort eine Ganzzahl sein wird, können wir auch die input-Funktion verwenden:

prompt = "Was ... ist die Fluggeschwindigkeit einer unbelasteten Schwalbe?\n"
speed = input(prompt)

Wenn der Benutzer nun einen String von Ziffern eingibt, wird dieser in eine Ganzzahl umgewandelt und der Variablen speed zugewiesen. Leider wird das Programm aber abstürzen, wenn der Benutzer einen Buchstaben eingibt, der keine Ziffer ist.

>>> speed = input (prompt)
Was ... ist die Fluggeschwindigkeit einer unbelasteten Schwalbe?
Meinst du eine afrikanische oder eine europäische Schwalbe?
SyntaxError: invalid syntax

Um diese Art von Fehler zu vermeiden, ist es im Allgemeinen eine gute Idee, raw_input zu verwenden und damit einen String zu erhalten, der dann mit Umwandlungsfunktionen in andere Typen umgewandelt werden kann.

4.13 Glossar

Modulo-Operator
Ein Operator, dargestellt durch das Prozent-Zeichen (%), der mit Ganzzahlen arbeitet und für die Division einer Zahl durch eine andere den Rest berechnet.
modulus operator
An operator, denoted with a percent sign (%), that works on integers and yields the remainder when one number is divided by another.
boolescher Ausdruck
Ein Ausdruck, der entweder wahr oder falsch ist.
boolean expression
An expression that is either true or false.
Vergleichsoperator
Einer der Operatoren, die zwei Werte vergleichen: ==, !=, >, <, >=, und <=.
comparison operator
One of the operators that compares two values: ==, !=, >, <, >=, and <=.
logischer Operator
Einer der Operatoren, die boolesche Ausdrücke kombinieren: and, or, und not.
logical operator
One of the operators that combines boolean expressions: and, or, and not.
bedingte Anweisung
Eine Anweisung, die die Programmausführung in Abhängigkeit von einer Bedingung kontrolliert.
conditional statement
A statement that controls the flow of execution depending on some condition.
Bedingung
Der boolesche Ausdruck in einer bedingten Anweisung, der entscheidet, welcher Zweig auszuführen ist.
condition
The boolean expression in a conditional statement that determines which branch is executed.
zusammengesetzte Anweisung
Eine Anweisung, die aus einem Kopf und einem Körper besteht. Der Kopf endet mit einem Doppelpunkt (:). Der Körper wird relativ zum Kopf eingerückt.
compound statement
A statement that consists of a header and a body. The header ends with a colon (:). The body is indented relative to the header.
Block
Eine Gruppe von aufeinanderfolgenden Anweisungen die gleich weit eingerückt sind.
block
A group of consecutive statements with the same indentation.
Körper
Der Block in einer zusammengesetzten Anweisung, der dem Kopf folgt.
body
The block in a compound statement that follows the header.
geschachtelt
Eine Programm-Struktur innerhalb einer anderen, wie etwa eine bedingte Anweisung innerhalb eines Zweiges einer anderen bedingten Anweisung.
nesting
One program structure within another, such as a conditional statement inside a branch of another conditional statement.
Rekursion
Der Prozess, dass die Funktion aufgerufen wird, die gerade ausgeführt wird .
recursion
The process of calling the function that is currently executing.
Basis-Fall
Ein Zweig der bedingten Anweisung in einer rekursiven Funktion, der keinen rekursiven Aufruf enthält.
base case
A branch of the conditional statement in a recursive function that does not result in a recursive call.
unendliche Rekursion
Eine Funktion, die sich selbst rekursiv aufruft, ohne jemals den Basis-Fall zu erreichen. Schließlich wird eine unendliche Rekursion einen Laufzeit-Fehler verursachen.
infinite recursion
A function that calls itself recursively without ever reaching the base case. Eventually, an infinite recursion causes a runtime error.
Prompt
Eine visuelles Signal, dass dem Benutzer anzeigt, dass er Daten eingeben soll.
prompt
A visual cue that tells the user to input data.


zurück rauf weiter Englisch Index