Die Maximumsfunktion mit drei Parametern können wir auf die mit zwei zurückführen.
def max(x,y):
if x > y:
return x
else:
return y
def max3(x,y,z):
return max(x,max(y,z))
Beispielaufrufe:
>>> max3(1,2,3)
3
>>> max3(3,1,2)
3
>>> max3(3,4,3)
4
Bei der Implementierung der logischen Funktionen führen wir die Disjunktion mit Hilfe der Gesetze von de Morgan auf die beiden anderen Definitionen zurück.
def nicht(x):
if x:
return False
else:
return True
def und(x,y):
if x:
return y
else:
return False
def oder(x,y):
return nicht(und(nicht(x),nicht(y)))
Die Summe der ersten n
Zahlen berechnen wir mit einer Zählschleife.
def sum_up_to(n):
sum = 0
for i in range(n+1):
sum = sum + i
return sum
Beispielaufruf:
>>> sum_up_to(100)
5050
Analog dazu berechnen wir die Fakultät als Produkt der ersten n
Zahlen und initialisieren dazu die Variable prod
mit dem neutralen
Element 1
der Multiplikation.
def factorial(n):
prod = 1
for i in range(1,n+1):
prod = prod * i
return prod
Beispielaufrufe:
>>> factorial(3)
6
>>> factorial(10)
3628800
Zur Wurzelberechnung mit dem Heron-Verfahren verbessern wir eine initial gewählte Näherung solange, bis sie nah genug an der Wurzel ist. Um die Lesbarkeit zu erhöhen, führen wir zwei Hilfsfunktionen ein. Eine zum Testen, ob die Nährerung gut genug ist und eine um eine Näherung zu verbessern.
def is_close_enough(x,sqrt,eps):
diff = sqrt**2 - x
return -eps < diff and diff < eps
def improve(x,sqrt):
return (sqrt + x/sqrt)/2
Mit Hilfe dieser Funktionen können wir die Wurzeliteration nun mit einer einfachen bedingten Schleife programmieren.
def heronIter(x,eps):
sqrt = 1.0
while not is_close_enough(x,sqrt,eps):
sqrt = improve(x,sqrt)
return sqrt
Beispielaufrufe:
>>> heronIter(9,1)
3.023529411764706
>>> heronIter(9,0.1)
3.00009155413138
>>> heronIter(9,1e-10)
3.0
Mit Zählschleife:
def sum_from_to(n,m):
sum = 0
for i in range(n,m+1):
sum = sum + i
return sum
Mit bedingter Schleife:
def sum_from_to(n,m):
sum = 0
i = n
while i <= m:
sum = sum + i
i = i + 1
return sum
Die Variante mit Zählschleife ist weniger fehleranfällig, weil die Manipulation der Zahlvariablen i
automatisch geschieht und keine Gefahr besteht, dass die Schleife versehentlich nicht terminiert.
def binary(n):
bin = ""
while n > 0:
bin = str(n % 2) + bin
n = n // 2
return bin
Die Eingabezahlen lesen wir mit der Funktion get_int()
, die eine Eingabeaufforderung als Parameter erhält und als Ergebnis eine eingelesene Zahl liefert. Dabei wird so lange nach Eingaben gefragt, bis eine gültige Zahl eingegeben
wird.
def get_int(aufforderung):
valid = False
while not valid:
eingabe = input(aufforderung)
if eingabe.isnumeric():
zahl = int(eingabe)
valid = True
else:
print("Ungültige Eingabe!")
return(zahl)
Das folgende Programm liest zwei ganze Zahlen ein und vergleicht sie der Größe nach.
min = get_int("Gib eine ganze Zahl ein: ")
max = get_int("Gib noch eine ganze Zahl ein: ")
if min > max:
num = min
min = max
max = num
if min == max:
print("Die eingegebenen Zahlen sind gleich.")
else:
print(str(max) + " ist größer als " + str(min) + ".")
Hier ist eine Beispiel-Interaktion mit diesem Programm.
Gib eine ganze Zahl ein: zwölf
Ungültige Eingabe!
Gib eine ganze Zahl ein: 12
Gib noch eine ganze Zahl ein: 8
12 ist größer als 8.
Das Programm zum Spielen von “Stein, Schere, Papier” liest eine Benutzereingabe und wählt dann entsprech um zu gewinnen.
s = input("Stein, Schere oder Papier? ")
while s == "Stein" or s == "Schere" or s == "Papier":
if s == "Stein":
print("Ich hatte Papier genommen. Gewonnen!")
if s == "Schere":
print("Ich hatte Stein genommen. Gewonnen!")
if s == "Papier":
print("Ich hatte Schere genommen. Gewonnen!")
s = input("Stein, Schere oder Papier? ")
Das Programm terminiert, sobald etwas anderes als Stein
, Schere
oder Papier
eingegeben wird.
Die Funktion
def is_small_prime(n):
if n == 2 or 3 or 5 or 7:
return True
else:
return False
liefert bei jedem Aufruf den Wert True
zurück. Beabsichtigt ist
hingegen, dass nur für die Eingaben 2, 3, 5 und 7 der Wert True
und
sonst der Wert False
geliefert wird.
Der Fehler liegt in der Formulierung der Bedingung, die implizit wie folgt geklammert ist.
(n == 2) or 3 or 5 or 7
Der Wert dieses Ausdrucks ist True
, falls n
gleich 2 ist und sonst
gleich 3. Beide Ergebnisse führen zur Ausführung des then
-Zweiges
der bedingten Verzweigung.
Beabsichtigt ist hier jedoch nicht die Oderverknüpfung des Vergleiches
n == 2
mit den Zahlen drei, fünf und sieben sondern die
Oderverknüpfung von Vergleichen der Variable n
mit den vier
kleinsten Primzahlen. Diese schreiben wir in python wie folgt.
n == 2 or n == 3 or n == 5 or n == 7
Nach einer entsprechen Korrektur liefert die Funktion is_small_prime()
das beabsichtigte Ergebnis.
Die Funktion
def describe_text(s):
if len(s) >= 10:
print("10 Zeichen oder mehr")
if len(s) > 20:
print("Auch mehr als 20")
else:
print("Weniger als 10 Zeichen")
liefert für ein Argument mit einer Länge zwischen 10 und 20 Zeichen
(z.B. Hallo Welt!
) eine widersprüchliche Ausgabe.
10 Zeichen oder mehr
Weniger als 10 Zeichen
Dies ist vermutlich nicht beabsichtigt. Die Einrückung suggeriert,
dass der else:
-Zweig zur äußeren bedingten Anweisung gehören sollte
und nicht zur inneren. Wir erreichen dies, indem wir die Einrückung von else:
und den zugehörigen Block um eine Position ausrücken.
def describe_text(s):
if len(s) >= 10:
print("10 Zeichen oder mehr")
if len(s) > 20:
print("Auch mehr als 20")
else:
print("Weniger als 10 Zeichen")
Nach dieser Korrektur wird beim obigen Beispiel nur noch die erste Ausgabe erzeugt.
Die Funktion nums_from_to()
liefert eine Zeichenkette durch Leerzeichen getrennter Zahlen.
def nums_from_to(lower,upper):
nums = ""
for i in range(lower,upper):
nums = nums + str(i) + " "
if lower <= upper:
nums = nums + str(upper)
return nums
Die bedingte Anweisung sorgt dafür, dass eine leere Zeichenkette geliefert wird, wenn upper
kleiner als lower
ist.
Die folge Prozedur verwendet die definierte Funktion um Zahlen in einem abgefragten Bereich auszugeben.
def print_nums():
fro = int(input("Erste Zahl: "))
to = int(input("Letzte Zahl: "))
print(nums_from_to(fro,to))
Wenn die obere Grenze kleiner ist als die untere, erzeugt die Prozedur als Ausgabe des Zahlenbereiches nur eine Leerzeile, weil die Funktion nums_from_to()
in dem Fall die leere Zeichenkette zurückliefert und print()
einen Zeilenumbruch erzeugt.
Die Prozedur zum Zeichnen eines Dreiecks gegebener Größe verwet die
Hilfsfunktion repeat()
, die eine gegebene Zeichenkette wiederholt
aneinander hängt.
def repeat(times,string):
result = ""
for i in range(0,times):
result = result + string
return result
Mit Hilfe dieser Funktion können wir Dreiecke zeichnen, indem wir zuerst den obersten Stern zeichnen, dann in einer Schleife jeweils zwei Sterne mit wachser Anzahl Leerzeichen schreiben und schließlich eine Zeile nur aus Sternen zeichnen. Ein Sonderfall sind Dreiecke der Größe eins, da sie nur aus dem ersten Stern bestehen.
def put_dreieck(n):
print("*")
if n > 1:
for i in range(0, n-2):
print("*" + repeat(i," ") + "*")
print(repeat(n,"*"))
Auch die Prozedur put_hailstone()
verwet die Prozedur repeat()
. Sie
berechnet die Zahlen gemäß der angegebenen Vorschrift in einer
bedingten Schleife bis eins erreicht wird und gibt den aktuellen Wert
vor und nach jedem Durchlauf als Sterne aus.
def put_hailstone(n):
while n > 1:
print(repeat(n,"*"))
if n % 2 == 0:
n = n // 2
else:
n = 3*n + 1
print(repeat(n,"*"))
def put23(n)
for i in range(n):
print (str(i) + " " + zwei3(i))
def zwei3(n)
if n % 2 == 0 and n % 3 == 0:
return "geht durch 2 und 3"
else:
if n % 2 == 0:
return "geht durch 2"
else:
if n % 3 == 0:
return "geht durch 3"
else:
return "geht weder durch 2 noch 3"
Das gezeigte Programm definiert zwei Funktionen (is_divisible()
und
is_prime()
) und eine Prozedur (print_prime_twins()
). Letztere wird am Ende
des Programms mit dem Argument 100
aufgerufen, wodurch alle
Primzahlzwillinge aus Primzahlen kleiner als 100 ausgegeben werden.
Die Funktion is_divisible()
hat zwei Parameter n
und k
und gibt einen
Wahrheitswert zurück, der beschreibt, ob n
durch k
teilbar ist.
Die Funktion is_prime()
hat einen Parameter n
. Der Rückgabewert ist ein
Wahrheitswert, der beschreibt, ob n
eine Primzahl ist. Der Rumpf von
is_prime()
enthält eine bedingte Schleife, in deren Rumpf mit Hilfe eines
Aufrufs der Funktion is_divisible()
getestet wird, ob der Parameter n
durch den Wert der Variablen k
teilbar ist. Falls ja, wird durch eine optionale
Anweisung die Ausführung des Funktionsrumpfes beendet und False
als Ergebnis
zurückgeliefert. Die Zählvariable k
wird durch die Zuweisung k = 2
initialisiert und so lange erhöht, bis ihr Quadrat den Parameter n
erreicht
oder übersteigt. Auf diese Weise wird die Ausführung beim kleinsten gefundenen
Teiler, der größer als eins ist, beendet. Falls die Ausführung des
Funktionsrumpfes nicht innerhalb der Schleife abgebrochen wird, wird zurückgegeben, ob der Parameter n
größer als eins ist. Es wird also genau dann
True
zurückgeliefert, wenn diese Zahl eine Primzahl ist.
Die Prozedur print_prime_twins()
hat einen Parameter to
und gibt Paare
n,n+2
von Zahlen aus, die beide Primzahlen sind. Solche Paare heißen
Primzahlzwillinge. Dazu werden im Rumpf der Prozedur in einer Zählschleife
alle Paare n,n+2
bis zu n = to
daraufhin getestet, ob beide Zahlen
Primzahlen sind. Ist das der Fall, wird das Zahlenpaar mit Hilfe einer
optionalen Anweisung ausgegeben.
Die Definition von Hilfsfunktionen is_divisible()
und is_prime()
erhöht die
Lesbarkeit des Programms durch sprechende Namen. Im Fall von is_prime()
ermöglicht die Definition des Primzahltests als Funktion außerdem, diesen im Rumpf der Zählschleife von print_prime_twins()
mehrfach mit unterschiedlichen Argumenten aufzurufen, wodurch Code-Duplikation vermieden wird.
Die bedingte Schleife im Rumpf von is_prime()
ist eigentlich eine Zählschleife,
die bis zur Quadratwurzel des Parameters n
läuft. Da die Abbruchbedingung mit
Hilfe einer for
-Schleife nicht so kurz beschrieben werden kann wie hier, kann man die Verwendung einer bedingten Schleife als gerechtfertigt betrachten, obwohl sie es erforderlich macht, die Zählvariable explizit zu initialisieren und zu erhöhen. Wegen der return
-Anweisung im Schleifenrumpf kann dieser verlassen werden, bevor die Abbruchbedingung erreicht ist. Solch vorzeitige Beendigung der Ausführung einer Schleife (hier sogar des gesamten Funktionsrumpfes) erschwert das Verständnis des Programms, weil nicht einfach ersichtlich ist, unter welchen Umständen welche Programmteile erreichbar sind. Die Verwendung einer return
-Anweisung im Schleifenrumpf ließe sich vermeiden, indem man das Ergebnis des Aufrufs von is_divisible()
in einer Variablen speichert und deren Wert in der Schleifenbedingung abfragt, um diese zu beenden, wenn ein Teiler gefunden wurde. Auf diese Weise würde zwar die Abbruchbedingung komplexer, aber nicht die Ausführung der Schleife. Dadurch würde erreicht, dass man allein anhand der Abbruchbedingung erkennen kann, wann die Ausführung der Schleife beendet wird.