Beruflich Dokumente
Kultur Dokumente
skriptsprachenorientierten
Programmierens
Prof. Dr. Manuel Mayer
Teil 3
© FOM Hochschule für Oekonomie & Management
gemeinnützige Gesellschaft mbH (FOM), Leimkugelstraße 6, 45141 Essen
Dieses Werk ist urheberrechtlich geschützt und nur für den persönlichen Gebrauch im Rahmen der Veranstaltungen der FOM
bestimmt.
Die durch die Urheberschaft begründeten Rechte (u.a. Vervielfältigung, Verbreitung, Übersetzung, Nachdruck) bleiben dem
Urheber vorbehalten.
Das Werk oder Teile daraus dürfen nicht ohne schriftliche Genehmigung der FOM reproduziert oder unter Verwendung
elektronischer Systeme verarbeitet, vervielfältigt oder verbreitet werden.
2
https://xkcd.com/889/
v Eine Schildkröte (Turtle) kann über den Bildschirm bewegt werden. Dabei hinterlässt
die Turtle auf ihrer Bewegungsbahn eine Linie. So können Grafiken erstellt werden.
v Dokumentation: https://docs.python.org/3/library/turtle.html
4
Challenges (7)
C13:
Zeichnen Sie mit den Turtle Graphics
a) ein Quadrat C14:
b) ein Dreieck Zeichnen Sie folgenden Stern:
c) einen Kreis
C15:
Knobelaufgabe: Wie kann folgendes Muster erzeugt werden?
5
https://xkcd.com/534/
0b1011. Funktionen
Funktionen
v Eine Funktion beinhaltet eine Reihe von Anweisungen, die einen Wert an
den Aufrufer zurückliefert.
7
Funktionsdefinitionen erzeugen ein Objekt
Variable Objekt
9
Funktionsparameter (Round 1): Default, Position, Keyword
10
1. Default- hinter Nicht-Default-Argumenten
Gegenbeispiel:
def add(a=5,b,c):
^
SyntaxError: non-default argument follows default argument
11
2. Keyword- hinter Positions-Argumenten
Gegenbeispiel:
print (add(a=10,3,4))
^
SyntaxError: positional argument follows keyword argument
12
3. Keywords müssen bekannt sein; die Reihenfolge ist irrelevant
Gegenbeispiel:
print (add(a=10,b1=5,c=12))
TypeError: add() got an unexpected keyword argument 'b1'
13
4. Ein Wert pro Argument
Gegenbeispiel:
print (add(a=10,b=5,b=10,c=12))
^
SyntaxError: keyword argument repeated
14
5. Default-Argumente sind optional
15
Funktionsparameter (Round 2): Variable Parameteranzahl
16
1. Positionsargumente variabler Länge
17
2. Keyword-Argumente variabler Länge
18
Ein Beispiel zu args und kwargs
Die Funktion doubler gibt eine Funktion (g) zurück, die den Funktionswert
der übergebenen Funktion (f) verdoppelt.
19
Ein Beispiel zu args und kwargs (II)
a) g(3)
b) g(-1)
20
Ein Beispiel zu args und kwargs (III)
Warum?
21
Ein Beispiel zu args und kwargs (IV)
22
Argumente entpacken
23
Ein einfaches Beispiel (*)
24
Ein einfaches Beispiel (**)
25
Funktionsparameter (Round 3): Spezielle Parameter
26
Funktionsparameter (Round 3): Spezielle Parameter
v / bzw * sind spezielle Symbole und dienen der Definition von reinen
Positionsargumenten oder reinen Keyword-Argumenten.
v Bei falscher Verwendung (Keyword für Nur-Position oder Position für Nur-
Keyword) wird ein TypeError erzeugt.
27
Beispiel reine Positionsarguemnte
28
Beispiel reine Keyword-Argumente
29
Beispiel Kombination beider
30
Anmerkungen
v Verwenden Sie rein positionsbezogene Argumente nur dann, wenn der Name
keine Bedeutung hat und Sie die Reihenfolge der übergebenen Argumente
erzwingen wollen.
v Verwenden Sie reine Keyword-Argumente nur dann, wenn der Name des
Arguments für ein besseres Verständnis sorgt und Sie verhindern möchten,
dass sich der Aufrufer lediglich auf die Position des Arguments beruft.
31
Call by name/reference
v Python verwendet ein System, das auch als „call by object reference“ oder
„call by assignment“ bekannt ist.
32
Denksport (V)
v Welches Ergebnis erhalten Sie nach den folgenden Modifikationen? Begründen Sie.
33
Challenges (8)
C16:
Schreiben Sie eine Funktion, die ihr Argument um 1 erhöht
zurückgibt. Es soll gelten: increment(42) = 43
Optional soll es auch möglich sein, um mehr als 1 zu erhöhen,
d.h. es soll zudem gelten: increment(11, 3) == 14
C17:
Schreiben Sie eine Funktion givemefive, die 5 Elemente aus
einer Liste ab einem bestimmten Index zurückgibt. Wird kein
Index angegeben, sollen die ersten 5 Elemente zurückgegeben
werden.
34
https://xkcd.com/2453/
Beispiel:
36
Lambda-Funktionen lambda arguments : expression
Der Begriff Lambda geht das von Alonzo Church und Stephen Cole Kleene eingeführte Lambda-Kalkül
zurück, einer formalen Sprache zur Untersuchung von Funktionen. Eine Funktion 𝑥 ⟼ 𝑥 − 5 wird im
Lambda-Kalkül mit 𝜆𝑥. 𝑥 − 5 notiert. Man sagt, dass die freie Variable 𝑥 durch 𝜆-Abstraktion gebunden
wird. Da λ-Terme als Funktionen gesehen werden, kann man sie auf ein Argument anwenden: (𝜆𝑥. Φ)𝛼
38
Closures
Bevor wir uns im Detail mit Closures beschäftigen, müssen wir zuerst klären, was
eine innere Funktion (nested function) und nicht-lokale (nonlocal) Variablen sind.
Eine innere Funktion haben wir bereits bei dem args/kwargs-Beispiel gesehen:
39
Exkurs: Scope - Globale Variablen
VS.
40
Exkurs: Scope - Globale Variablen (II)
v Damit – wie gerade gesehen – keine weitere lokale Variable erstellt wird, kann
das Keyword global verwendet werden.
41
Exkurs: Scope - Globale Variablen (III)
42
Exkurs: Scope - Globale Variablen (IV)
VS.
43
Exkurs: Scope - Globale Variablen (V)
v Auch wenn das vorangegangene Beispiel eine offensichtliche Manipulation der globalen Variable
Ergebnissen führen.
v Zum Lesen globaler Variablen wird das Keyword global nicht benötigt, sondern erst dann, wenn
v Es gibt wenige Gründe, global zu verwenden (Stichwort Seiteneffekte), aber es gibt sie. So
können bspw. Globals als Konstanten verwendet werden (Python hat keine echten Konstanten),
44
Scope - Nicht-lokale Variablen (nonlocal)
v Globale Variablen sind auch hilfreich bei inneren Funktionen (nested functions).
v Python 3 führte das Keyword nonlocal ein und schafft somit einen
Gültigkeitsbereich zwischen lokal und global, speziell für Verschachtelungen.
45
Aber jetzt: Closure (de: Funktionsabschluss)
v Nachdem wir bereits wissen, dass eine Methode eine an ein Objekt gebundene
Funktion ist (2. Semester) und ein Lambda-Ausdruck (Funktionsliteral) eine
anonym definierte Funktion, ist eine Closure ein Muster der funktionalen
Programmierung und auf den Punkt gebracht:
Eine Closure ist ein Funktionsobjekt, das an eine Umgebung gebunden ist.
v „Eine Closure ist eine anonyme Funktion“ – falsch!! Dies ist nur eine
Eigenschaft von Lambdas.
46
Closure: Lambda-Ausdruck
v Nehmen wir an, dass es neben der gebundenen Variable eines Lambda-Ausdrucks
(auch Symbol genannt) weitere Symbole gibt:
𝑥
𝜆𝑥. + 2
𝑦
v 𝑥 ist durch die Lambda-Abstraktion gebunden. 𝑦 ist nicht gebunden und somit frei.
v Eine Closure schließt (closes) somit einen offenen Lambda-Ausdruck – daher auch
der Name Closure.
47
Closures: Code-Beispiel
ohne
Klammern
48
Closures: Wann sollten sie genutzt werden?
v Innere Funktionen sind nur innerhalb der äußeren Funktion gültig. Mit Closures
wird eine Ausführung auch außerhalb ihres Gültigkeitsbereichs möglich.
v Bei nur wenigen Funktionen in einem Programm, können Closures eine effiziente
Methode sein. Ansonsten sollte man eine Klassenstruktur vorziehen.
Der Compiler muss in der Lage sein, zu erkennen, dass der Wert (Zustand) der Variablen außerhalb deren
eigentlichen Gültigkeitsbereich (scope) benötigt wird, und dies bei der Kompilierung aktiv berücksichtigen.
Technisch werden diese Variablen dann meist nicht mehr auf dem Stack abgelegt, sondern dies wird anders
gelöst, z. B. indem tatsächlich im Hintergrund eine (anonyme) Klasse samt Instanz erzeugt wird, die die
benötigten (Member-)Variablen und die innere Funktion (als Memberfunktion) enthält.
Durch Verwendung des falschen Scopes können sich leicht Fehler einschleichen.
Führen Sie die folgenden Zellen aus. Machen Sie sich bewusst, wie deren
Ergebnisse zustande kommen und was eventuell schief läuft:
a) b)
c)
50
Challenges (9)
C18:
1) Erzeugen Sie (programmatisch) eine Liste von Funktionen, die die Vorschriften (konkret
Potenzierungen) 𝑥 ! , 𝑥 " , 𝑥 # , …, 𝑥 $% ausführen.
2) Erzeugen Sie eine zweite Liste, die die Strings ”x^2”, “x^4”... enthält.
3) Nutzen Sie die beiden Listen, um für ein gegebenes 𝑥 eine Bildschirmausgabe der Form
(hier für 𝑥 = 2)
x^2 = 4
x^4 = 16
...
x^18 = 262144
0b1110. Komplexität
53
Laufzeitanalyse
v 1. Ansatz: Direktes Messen der Laufzeit (z.B. in ms)
v Aber (!): In der Praxis eine gängige Methode, um die Performanz grundsätzlich
sicherzustellen oder z. B. um Flaschenhälse zu identifizieren (lang laufende DB-Queries etc.)
v 2. Ansatz: Zählen der Elementaroperationen in Abhängigkeit von der Größe 𝑛 der Eingabe.
v Die Charakterisierung dieser elementaren Operationen ist abhängig von der jeweiligen
Problemstellung und dem zugrundeliegenden Algorithmus.
v Die Laufzeit einzelner Elementaroperationen ist abhängig von der eingesetzten Hardware:
v GPU
v Laufzeit
v Speicherplatz
56
Asymptotisches Laufzeitverhalten
Die Laufzeit des Programms ist eher bestimmt durch die Initialisierungskosten (OS,
v Interessanter:
v Wie verändert sich die Laufzeit, wenn ich die Problemgröße variiere?
→ asymptotisches Laufzeitverhalten
57
Schranken
v Es ist sehr schwer, die genaue Verteilung der Daten zu finden, die dem realen Fall entspricht.
Deswegen betrachten wir in der Regel den schlimmsten Fall und finden auf diese Weise eine
v Wenn diese, als Funktion formulierte, obere Schranke korrekt ist, garantieren wir, dass für
beliebige Eingabedaten die Laufzeit unseres Algorithmus immer kleiner oder gleich dieser
Schranke ist.
v Selbstverständlich können auch weitere Schranken (z.B. die untere Schranke: mindestens) von
Interesse sein.
58
Addition in der Schule
Eingabegröße:
𝑛 = 𝑍𝑎ℎ𝑙𝑒𝑛𝑏𝑟𝑒𝑖𝑡𝑒
Berechnungsschritt:
Komplexitätsanalyse:
Im schlimmsten Fall:
59
Multiplikation zweier Zahlen mit n (Schulmethode)
Eingabegröße:
Berechnungsschritt:
Komplexitätsanalyse:
60
Multiplikation zweier Zahlen mit n (Schulmethode) (II)
p=0 : ℕ Pseudocode
for j:=0 to n-1 do
p:=p
+ // n+1 Ziffernadditionen (optimiert)
a * b[j] // n Add. und n Mult. (siehe Backup-Folie)
* B^j // schieben (keine Ziffernarithmetik)
𝑛 " 𝑀𝑢𝑙𝑡𝑖𝑝𝑙𝑖𝑘𝑎𝑡𝑖𝑜𝑛𝑒𝑛
𝑛 " + 𝑛 − 1 𝑛 + 1 = 2𝑛 " + 1 𝐴𝑑𝑑𝑖𝑡𝑖𝑜𝑛𝑒𝑛
61
Summe und Multiplikation in der Schule
Allgemein:
𝑛! 62
Die O-Notation
v Mit der O-Notation haben Informatiker einen Weg gefunden, die asymptotische Komplexität
v Einordnung:
63
Rechnen mit der O-Notation
$
2 ∗ 𝑛 ∈ 𝑂(𝑛) + 1 ∈ 𝑂(𝑛)
"
9𝑛 % + 2𝑛 & + 10𝑛 + 20 ∈ 𝑂 𝑛 %
Beachte: 1000 ∗ 𝑛 " ist nach diesem Leistungsmaß immer “besser” als 0,001 ∗ 𝑛 ' , auch wenn das 𝑛# ,
ab dem die 𝑂(𝑛 " )-Funktion unter der 𝑂(𝑛 ' )-Funktion verläuft, in diesem Fall sehr groß ist (𝑛# = 1
Million).
64
Rechnen mit der O-Notation – Noch 2 Beispiele
65
Anmerkungen zur O-Notation
v 𝑐 ist unabhängig von 𝑛: 𝑐 muss dieselbe Konstante sein, die für alle 𝑛 ∈ ℕ garantiert, dass
𝑇 𝑛 ≤ 𝑐 ∗ 𝑔(𝑛).
v Existiert keine solche Konstante 𝑐, ist 𝑇(𝑛) nicht von der Größenordnung 𝑂(𝑔).
v Man findet in der Literatur häufig T(n) = 𝑂(𝑔) statt 𝑇(𝑛) ∈ 𝑂(𝑔).
Aber Vorsicht: es gelten nicht die entsprechenden Gesetze, bspw. 𝑂 𝑔 = 𝑇(𝑛) gilt nicht!
v Man trifft bei der O-Notation üblicherweise weitere Annahmen. So wird zum Beispiel für das
Addieren von 2 Zahlen eine Laufzeit von 𝑂(1) verwendet und nicht 𝑂(𝑛). Dies wird
vernachlässigt, da man sich meist mit Zahlen mit fixer Größe (heutzutage 32 oder 64 Bit)
zufrieden gibt. Es spielt jedoch durchaus eine Rolle, wenn es beispielsweise um Mathematik-
𝑂 𝑛# , 𝑘 ≥ 2 polynommiell
...
𝑂(2$ ) exponentiell Ausprobieren von Kombinationen
67
Visualisierung des Größenwachstums
68
Abschließende Bemerkungen
v Die O-Notation hilft insbesondere bei der Beurteilung, ob ein Algorithmus für großes n noch
geeignet ist bzw. erlaubt einen Effizienzvergleich zwischen verschiedenen Algorithmen für
große n.
v Schlechtere als polynomielle Laufzeit gilt als nicht effizient, kann aber für manche Probleme
v … d.h. für manche Probleme gibt es untere Schranken, d.h. kein Algorithmus kann schneller
b) 1) O(N) 2) O(sqrt(N))
3) O(N/2) 4) O(log N)
C20:
Zwei Algorithmen, A und B, haben eine Worst-Case-Laufzeit von O(n) bei A und O(log n) bei
B. Die Aussage, dass Algorithmus B immer schneller ist als A ist a) wahr oder b) falsch.
70
https://xkcd.com/1739/
v Die Funktion sum berechnen für ein gegebenes 𝑛 > 0, 𝑛 ∈ Ν die Summe aller Zahlen von 1 bis 𝑛.
Iterativ:
𝑇 𝑛 = 𝑐( + 𝑐" 𝑛, c" = Zeitkosten eines Schleifendurchgangs
𝑇 𝑛 = 𝑂(𝑛)
Rekursiv:
𝑇 𝑛 = 𝑐( + 𝑐" 𝑛, c" = Zeitkosten eines Funktionsaufrufs
𝑇 𝑛 = 𝑂(𝑛)
Direkt:
Formel von Gauß, 𝑇 𝑛 = 𝑂(1)
72
Beispiel: Die Funktion factorial
Rekursiv:
Rechenzeit: 𝑇 𝑛 = 𝑂(𝑛)
Speicherplatz: 𝑇 𝑛 = 𝑂(𝑛)
Iterativ:
Rechenzeit: 𝑇 𝑛 = 𝑂(𝑛)
Speicherplatz: 𝑇 𝑛 = 𝑂(1)
73
Warum ist Rekursion oftmals ineffizient?
v Eine rekursive Funktion verursacht eine Kette von Funktionsaufrufen.
v Werte aller lokaler Variablen und die Stelle, an der die Ausführung der Funktion sich
gerade befindet.
v Wenn innerhalb einer Funktion f(…) eine Funktion g(…) aufgerufen wird:
v Die gesamte lokale Umgebung von f wird gespeichert. Die Werte der Parameter von g
werden gesetzt. Das Programm springt zum Anfang der Funktion g und die Funktion g
wird entsprechend ausgeführt. Das Programm springt zurück zu f und das Ergebnis der
Funktion g wird an f übergeben. Die gesamte Umgebung von f wird zurückgesetzt und
Laufzeitkeller
75
Spezielle Rekursionsarten
v Lineare Rekursion
Rekursive Funktionen, die in jedem Zweig ihre Definition maximal einen rekursiven Aufruf
Linear rekursive Funktionen werden als endrekursive Funktionen klassifiziert, wenn der
rekursive Aufruf in jedem Zweig der Definition die letzte Aktion zur Berechnung der
Funktion ist. D.h. keine weiteren Operationen müssen nach der Auswertung der Rekursion
berechnet werden.
76
Lineare Rekursion
v Eine nicht endrekursive Funktion ist folgende Definition der Fakultätsfunktion:
factorial 0 = 1
78
Endrekursion – Ablauf der Berechnung
abgewandelt werden.
79
Endrekursion – Mögliche Probleme
...
def recurse(counter): Depth 2977
print("Depth", counter) Depth 2978
counter += 1 ...
recurse(counter) RecursionError: maximum recursion depth exceeded while
calling a Python object
Rekursiv:
81
Rekursive Berechnung der Fibonacci-Zahlen
82
Rekursive Berechnung der Fibonacci-Zahlen (II)
wiederholte
Berechnungen
83
Rekursive Berechnung der Fibonacci-Zahlen (III)
Rechenzeit: 𝑇 𝑛 = 𝑂(𝑛)
Speicherplatz: 𝑇 𝑛 = 𝑂(𝑛)
fib_tail_rec(2978)
10368302567224130725402808720944745269169846358949206566546969004533913542980215297205069908032363496696295407802127339192049934635164785051
30546204118661607980410950286585809535664784349752677513026617225896806817079373241043331134208597785649274963993818359352645662049244104479
84598985541328622216290673559387996294654439808580297380157421264514600913663805613498253061272525605861214069246722624684271906736421352658
59269726057763349069496969887205666371709866621751717323402940230024941883749617398064589257637762440693437179535768949926413523261190831382
310521657050401255725846464639099512865670912780076725915476289
fib_tail_rec(2979)
...
RecursionError: maximum recursion depth exceeded while calling a Python object 😞
85
Iterative Berechnung der Fibonacci-Zahlen
Rechenzeit: 𝑇 𝑛 = 𝑂(𝑛)
Speicherplatz: 𝑇 𝑛 = 𝑂(1)
fib_iter(8000)
35615332044606267397689149054274603871413695391101540829735006389918858194987118153048292462239633737498734230832168897820342285216932671755
94214186111978816819236959743284321273097535654614718808050244321699002512466203835566030351092652496815708455980825654877181538741827129421
68912899187964953324613616899859004496573503581085677460538362837897929058053913579198506348499287793247348705406889947693739929519390552742
07929759029138360121990626870635375101517537581006264025917511839258831516176483750053134534932716812482330598584969517901132558974295395606
54496639601132039360167542277472498901884679404509894269174519328918160745655327632006736189766801968534195725815421784083495026969542066047
75888502969525726333071922395630904319565393034798349683080175557298241982188127556917992297341573601028956170069947702148863550978450916801
95896401902343500216738028568363657674462494249072730166890533880007856374449215234146023608600015301399336152153832209270847505282937794910
02813557093860863839463287251443115581618266959802005566973874793475256663122039030056061200186123236430592279484254766158650545069933528061
68014104657411510301453210159584182247476421388938511417454335213785668069468724409796809992418381568965277930293732972925367857964921588407
83344283380373274512207228105876801722558787954495247815549730971091741406326231676590274505504610450558838722256597968128470752864752082059
23875668405160707778568995306926178023176315799965539425437791083258303238592641010878264249883586034912756021070468742995902773902487497010
335873840408520900059054071283266816325489230566003110549946685475230821114509971542662742044237174282248020953398789607528748909125
86
tl;dr
v Wann soll in Python eine Iteration und wann eine Rekursion verwendet werden?
v Ist die Implementierung übersichtlicher durch eine Rekursion und sowohl die
→ Rekursion
→ Iteration
87
Dynamische Programmierung am Bsp. der Fibonacci-Zahlen
𝑇 𝑛 = 𝑂(𝑛)
88
Exkurs: Dekoratoren
Dekoratoren sind „syntaktischer Zucker“, um die Definition von Funktionen zu beinflussen:
@some_decorator
def some_function(arg1, arg2, ...):
statement1
statement2
D.h. nach der ersten
...
Definition von some_function
wird some_decorator
Dies ist äquivalent zu:
ausgeführt und kann eine
some_function = some_decorator(some_function)
89
Exkurs: Dekoratoren - Beispiel
*args ist hierbei das Tupel aller Argumente der Funktion func.
print_sum(3, 4)
Output:
-- print_sum called with arguments (3, 4)
7
-- print_sum returned 7
90
Exkurs: Dekoratoren - Metadaten
Dummerweise:
print_sum.__name__ print_sum.__doc__
Output: Output:
'new_func' None
Lösung: functools.wraps
91
Exkurs: Memoization mittels functools.lru_cache
# recursive
def fib(n):
if n <= 1:
return n
else:
return fib(n - 1) + fib(n - 2)
Mit lru_cache werden Ergebnisse gespeichert und wiederverwendet. Da Python nicht wissen kann,
ob eine Funktion referenziell transparent ist, geben wir es mit dem Dekorator des Moduls explizit
an. maxsize bestimmt die maximale Größe der gespeicherten Ergebnisse (hier: unbegrenzt).
@lru_cache(maxsize=None)
def fib_memoized(n):
if n <= 1:
return n
else:
return fib_memoized(n - 1) + fib_memoized(n - 2)
Output:
Fibonacci with DP: 1.371900000002313e-05
93
Challenges (11)
C21:
Wir betrachten eine typische Dateistruktur der Form
{'documents':{‘diss.docx':3, 'skripte':{'matheskript.pdf':15, 'pythontutorial.pdf':22}}, 'selfie.jpg':100}
und somit ein Dictionary, dessen values entweder Zahlen oder wiederum Dictionaries sein können.
Schreiben Sie eine Funktion disk_usage, die die Summe aller Zahlen-Werte ermittelt. Gestalten Sie sie
so, dass sie für ein beliebig tief verschachteltes Dictionary verwendbar ist.
C22:
In der Veranstaltung haben wir das Modul timeit kurz kennengelernt, um damit die
Ausführungszeit zu messen. Eine einfachere, aber oft ausreichende Variante ist das Modul
time. Schreiben Sie mithilfe des Moduls time einen Dekorator, der bei Funktionsaufruf ausgibt,
wie lange das Ausführen einer Funktion dauert. Lesen Sie sich dazu die Modulbeschreibung
von time durch.
94
Challenges 11 – Pro-Tour
C23:
Schreiben Sie eine rekursive Funktion, die aus gegebenen 𝑥 und 𝑛 ∈ ℕ die Potenz 𝑥 $ berechnet.
Verwenden Sie dazu die Rekursion
C24:
Schreiben Sie einen Dekorator für die Funktion aus Aufgabe C23, der die Rekursionstiefe zählt und bei
jedem Aufruf ausgibt. Hinweis: Jede Funktion, die dekoriert wird, braucht einen eigenen Rekursions-
Zähler. Eine Möglichkeit ist das Erzeugen einer solchen Variable im Scope des Decorators, so dass bei
jedem Verwenden des Decorators ein neuer Zähler erzeugt wird.
95
Challenges 11 – Pro-Tour (II)
C25:
Schreiben Sie eine Funktion, die die Primfaktoren einer Zahl zählt (mit Vielfachheit, d.h. 8 hat 3
Primfaktoren). Dabei kann man wie folgt vorgehen:
1. Testen Sie der Reihe nach alle Zahlen zwischen 2 und √𝑛, ob sie Teiler von 𝑛 sind.
2. Der kleinste Teiler 𝑘, den Sie finden, ist automatisch eine Primzahl. Die Anzahl der Primfaktoren
von 𝑛 ist dann um eins größer als die Anzahl der Primfaktoren von 𝑛/𝑘.
3. Wenn Sie keinen Teiler finden, dann ist 𝑛 selbst prim, d.h. das Resultat ist 1.
Bestimmen Sie die Summe der Anzahl der Primfaktoren für alle Zahlen zwischen 2 und 100000.
Verwenden Sie das Modul timeit, um die Berechnungsdauer der Summe zu bestimmen. Wie können
Sie diese Berechnung auf einfache Weise beschleunigen?
C26:
Schreiben Sie einen Dekorator, der einen Cache der bisherigen Funktionsaufrufe anlegt, bestehend
aus Argumenten und resultierenden Funktionswerten. Speichern Sie dieses in Form eines Dictionaries,
das bei jedem Funktionsaufruf auf dem Bildschirm ausgegeben wird.
Hinweis: Hier ist der Exkurs zum Thema Scope hilfreich. 96
https://xkcd.com/1171/
98
Formale Sprachen - Semantik
v Auch bei grammatikalisch korrektem Aufbau kann ein Text sinnlos sein:
99
Formale Grammatik
100
Chomsky-Hierarchie
Typ 0: Unbeschränkte Grammatik
v Enthält alle formalen Grammatiken
v Zugehörige Sprachen werden von einer Turingmaschine akzeptiert.
Typ 1: Kontextsensitive Grammatik
v Produktionsregeln der Form 𝛼𝐵𝛾 → 𝛼𝛽𝛾
(B Nichtterminal, griechische Buchstaben Worte aus 𝑉 ∗ )
v Sprache wird von linear beschränkter Turingmaschine erkannt.
Typ 2: Kontextfreie Grammatik
v Produktionsregeln der Form A → 𝛼
v Sprache wird von einem Kellerautomaten erkannt
v Programmiersprachen sind Typ 2.
Typ 3: Reguläre Grammatik
v Produktionsregeln der Form A → 𝛼 und A → 𝑎𝐵
v Sprache wird von linear beschränkter Turingmaschine erkannt.
101
Fragen zu allgemeinen Regelsprachen (Typ 0)
v Es ergeben sich sofort einige interessante Fragen, die zu Grammatiken gestellt werden können:
v Ist die Sprache zu einer Grammatik leer?
v Ist die Sprache zu einer Grammatik endlich?
v Gehört ein vorgegebenes Wort zu der von einer vorgegebenen Grammatik erzeugten
Sprache?
v Kann man ein Wort in ein anderes ableiten?
v Erzeugen zwei Grammatiken dieselbe Sprache?
v Leider sind alle diese Fragen nicht entscheidbar (Beweis: siehe Fachliteratur theoretische
Informatik). Unter nicht entscheidbar versteht man, dass es kein Verfahren gibt, das diese
Fragen für beliebige Worte und Grammatiken korrekt beantwortet. Entscheidbarkeit ist sehr
eng mit einem präzisen Algorithmusbegriff und mit dem Begriff der Berechenbarkeit gekoppelt.
Letztlich liegt die Ursache der Nichtentscheidbarkeit dieser Fragen darin, dass die Regelmenge
ohne jede Struktur sein darf. Dies ist für die Praxis nicht geeignet. Daher will man sich
einschränken und nur bestimmte Regeltypen zulassen (z.B. Reguläre Sprachen).
102
Reguläre Grammatiken (Typ 3) und reguläre Sprachen
v Wie gerade ausgeführt, bleiben zu viele Fragen offen, wenn man alle möglichen Regeln ohne
Einschränkung zulässt (allgemeine Regelsprachen). Wir lassen daher nur noch ganz wenige
Regeln zu und definieren:
Eine Sprache 𝐿(𝐺) heißt regulär, falls es eine reguläre Grammatik 𝐺 gibt,
die diese Sprache erzeugt.
103
Beispiel: Reguläre Grammatik der Exponentialzahlen
v Einschränkende Annahmen:
𝐺 = (𝑉, Σ, 𝑃, 𝑆)
𝑉 =Σ∪𝑁
Σ = −, +, 0,1,2,3,4,5,6,7,8,9, . , 𝑒
𝑧 ∈ {0 − 9}
104
z ist ein Platzhalter
Beispiel: Reguläre Grammatik der Exponentialzahlen (II)
v Produktionsregeln:
105
Reguläre Sprache und Ausdrücke??
v Die Menge der regulären Sprachen über einem Alphabet Σ und die zugehorigen
regulären Ausdrücke (regular expressions, kurz: regex) sind rekursiv definiert:
v Die leere Sprache ∅ ist eine reg. Sprache und der zugehörige reg. Ausdruck ist ∅.
v Der leere String {∧} ist eine reg. Sprache und der zugehörige reg. Ausdruck ist ∧.
v Für jedes 𝑎 in Σ ist die einelementige Sprache {𝑎} eine reguläre Sprache und der
zugehörige reguläre Ausdruck ist 𝑎.
v Wenn 𝐴 und 𝐵 reg. Sprachen sind mit zug. reg. Ausdrücken 𝑟1 und 𝑟2, dann:
v ist 𝐴 ∪ 𝐵 (Vereinigung) eine reg. Sprache und der zug. reg. Ausdruck ist
𝑟1 𝑟2
v ist 𝐴𝐵 (Verknüpfung) eine reg. Sprache und der zug. reg. Ausdruck ist 𝑟1𝑟2
v ist 𝐴∗ (Kleene star) eine reg. Sprache und der zug. reg. Ausdruck ist (𝑟1∗ )
106
Beispiel: Regulärer Ausdruck für Exponentialzahlen
v Es gibt unendlich viele Sprache über jedem endlichen, nichtleeren Alphabet.
v z.B. für Σ = {𝑎} existieren die Sprachen: {}, 𝑎 , 𝑎𝑎 , 𝑎, 𝑎𝑎 , 𝑎𝑎𝑎 , ...
v Wir wollen zeigen, dass sich die Sprache der Exponentialzahlen und der zugehörige
reguläre Ausdruck rekursiv herleiten lassen:
v Alphabet: {−, +, 0,1,2,3,4,5,6,7,8,9, . , 𝑒}
v Einelementige Sprachen: 𝑆( = − , 𝑆) = {+}, 𝑆* = {0}, ... , 𝑆+ = {𝑒}
v Zugehörige reguläre Ausdrücke: 𝑟( = −, 𝑟) = + , 𝑟* = 0, ... , 𝑟+ = 𝑒
v Vereinigung zweier Sprachen:
𝑎, 𝑏 ∪ 0,1,2 = 𝑎, 𝑏, 0,1,2 ⇒ 𝑆$ + 𝑆! 𝐸𝑙𝑒𝑚𝑒𝑛𝑡𝑒
v Verknüpfung zweier Sprachen:
𝑎, 𝑏 0,1,2 = 𝑎0, 𝑎1, 𝑎2, 𝑏0, 𝑏1, 𝑏2 ⇒ 𝑆$ \ 𝑆! 𝐸𝑙𝑒𝑚𝑒𝑛𝑡𝑒
v Kleensche Hülle einer Sprache:
0,1 ∗ = ∅, 0,1,00,01,10,11,000, … ⇒ ∞ 𝐸𝑙𝑒𝑚𝑒𝑛𝑡𝑒
107
Beispiel: Regulärer Ausdruck für Exponentialzahlen (II)
108
Beispiel: Regulärer Ausdruck für Exponentialzahlen (II)
109
Beispiel: Regulärer Ausdruck für Exponentialzahlen (III)
Der reguläre Ausdruck 𝑟, beschreibt die Sprache 𝑆, , die die zuvor vorgestellte Sprache
der Exponentialzahlen darstellt.
In Python: rI = r"[-+][0-9]\.[0-9]+e[-+][0-9]+”
kürzer: rI = r"[-+]\d\.\d+e[-+]\d+”
r = raw string
110
So what...
111
That’s why...
v Zeichen mit einer speziellen Bedeutung (., *, ...) können mit ihrer eigentlichen
Bedeutung durch voranstellen eines Backslashs (\) verwendet werden.
v Bekannte Maskierungszeichen (\n, \t, ...) funktionieren wie erwartet, z.B. entspricht
r‘\n+\‘ einem oder mehreren Zeilenumbrüche.
Syntax Beschreibung
\number entspricht dem n-ten zuvor gefundenen Text (Index startet mit 1)
\d entspricht [0-9]
\D entspricht [^0-9]
\S entspricht beliebigem Whitespace (also [\t\n\r\f\v])
\S alles außer Whitespace
\w beliebiges alphanumerisches Zeichen
\W beliebiges nicht alphanumerisches Zeichen
\A entspricht dem Beginn eines Strings
\Z entspricht dem Ende eines Strings
\b (Neg.: \B) Leerer String am Anfang oder Ende eines Wortes. Formell gesehen die Grenze
(boundary) zwischen \w und \W (oder umgekehrt). Somit matcht r’\bfoo\b’
‘foo’, ‘foo.’, ‘(foo)’, ‘bar foo baz’ aber nicht ‘foobar’ oder ‘foo3’ 114
Ein Beispiel
v Email-Adresse:
\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b
115
RegEx in Python – Das Module re
116
Das Module re: Zentrale Flags
117
Das Module re: Zentrale Funktionen
Syntax Beschreibung
re.compile(pattern, flags=0) Kompiliert ein Muster in ein RegEx-Objekt (was ein RegEx-
Objekt ist, sehen wir gleich)
re.search(pattern, string, flags=0) Sucht nach dem ersten Auftreten des Musters im String und
gibt ein Match-Objekt oder None bei erfolgloser Suche zurück.
re.match(pattern, string, flags=0) Prüft, ob das Muster am Anfang(!) des Strings auftritt (auch bei
re.M) und gibt ein passendes Match-Objekt oder None zurück.
re.fullmatch(pattern, string, flags=0) Prüft, ob der gesamte String das Muster matcht.
re.split(pattern, string, maxsplit=0, Trennt den String an den Stellen, wo das Muster gefunden wird
flags=0) und gibt das Ergebnis als Liste zurück.
re.findall(pattern, string, flags=0) Gibt alle nicht-überlappenden Matches als Liste zurück.
re.finditer(pattern, string, flags=0) Dasselbe wie findall, nur wird ein Iterator zurückgegeben.
re.sub(pattern, repl, string, count=0, Liefert einen String, in dem die gefundenen Muster im
flags=0) übergebenen String durch repl ersetzt wurden. repl kann ein
String oder eine Funktion sein.
re.subn(pattern, repl, string, Dasselbe wie sub, gibt aber ein Tupel
count=0, flags=0) (neuer_string, anzahl_substitutionen) zurück
re.escape(pattern) Escaped spezielle Regex-Zeichen in pattern.
118
Das Module re: RegEx-Objekte
v Ein mit compile kompiliertes RegEx-Objekt ist bereit, einen übergebenen String zu
verarbeiten.
v Kompilierte RegEx-Objekte bieten Methoden, die den soeben gesehen Funktionen sehr
ähnlich sind:
search, match, fullmatch, split, findall, finditer, sub, subn
v Es stehen zudem einige Attribute zur Verfügung, mit denen man die Flags, das Pattern
selbst und Informationen zu den Gruppierungen abrufen kann.
119
Das Module re: Match-Objekte
Syntax Beschreibung
Match.expand(template) Das übergebene String-Template wird mit den Inhalten des
Match-Objektes ausgewertet (ähnlich zu sub()), nur dass man
hier bereits mit einem Match-Objekt arbeitet.
Match.group([group1, ...]) Gibt die jeweilige Gruppe des Matches zurück (siehe
Gruppierungs-Syntax). Wird 0 als Argument übergeben, werden
alle Match-Strings (somit aller Gruppen) zurückgegeben.
Match.groups(default=None) Gibt alle Gruppen als Tupel zurück. Der Default-Wert gibt an,
welchen Wert leere Gruppen (also solche, die nicht gematcht
wurden) annehmen sollen.
120
Beispiel
v Datumsformat:
121
Challenges (12)
C27:
Schreiben Sie ein Programm, das eine String-
Ersetzung durchführt und dabei die
Großkleinschreibung nicht beachtet (case C28:
insensitive). Das Wort eine soll durch das Wort Schreiben Sie ein Programm, das in
DIE ersetzt werden: einem String sämtliche Werte
122
Challenges (12) – cont.
C29:
Laden Sie vom Onlinecampus die c29.zip runter. Darin enthalten finden Sie mehrere html-Dateien, die die
beliebtesten Babynamen in den USA für verschiedene Jahre enthalten. Zudem finden Sie eine Rahmendatei
(names.py), die Sie ausimplementieren sollen:
o Implementieren Sie die Funktion extract_filenames(filename), die einen Dateinamen (z.B. names1990.html
entgegennimmt) und die Daten der Datei als Liste zurückgibt (Jahreszahl, gefolgt von dem Namen und den Rang),
also z.B. [‘2020’,’Aaron 34’]. Die Liste soll alphabetisch nach dem Namen sortiert sein.
o Implementieren Sie main() so, dass sie die Funktion aufruft und das Ergebnis ausgibt. Die Ausgabe soll in etwa
folgendermaßen aussehen (hier nur ein Bsp., nicht Teil der echten Ausgabe):
2006
Aaron 34
Abbey 68
Abbie 99
...
Hinweise:
o Jungen- und Mädchennamen werden in einem gemeinsamen Pool erfasst.
o Manchmal kommen Namen doppelt vor, dann soll nur 1 Rang verwendet werden (Pro: verwenden Sie den
niedrigsten Rang).
o Es gibt nicht nur eine Lösung für die regulären Ausdrücke, die Sie zum Extrahieren der Daten verwenden.
Manche sind strikter, andere weniger (Diskussion). Eine funktionierende für diesen Datensatz ist ausreichend.
123
Challenges (12) – cont.
C29b:
Das soeben erstellte Script von der Challenge 29 soll nun um einen kleinen Punkt erweitert werden:
o Die Ausgabe soll nun nicht mehr im Standard-Out ausgegeben serden, sondern in eine Textdatei geschrieben
werden.
o Wir haben nicht detailliert behandelt, wie Dateien in Python geöffnet und geschrieben werden. Es ist aber kein
Hexenwerk und eine kurze Internetrecherche führt zu den benötigten Informationen.
o Im Zusammenhang mit der zu erzeugenden Textdatei soll sich unser Kommandozeilenprogramm folgendermaßen
verhalten:
o Ein Flag --summary soll optional zur Verfügung stehen. Falls dieses beim Aufruf des Programms übergeben
wird, wird die Ausgabe in eine Textdatei geschrieben, ansonsten direkt ausgegeben.
o Die Summary-Datei soll den Suffix .summary erhalten, somit wird aus names1990.html die Summary-Datei
names1990.html.summary.
o In der Kommandozeile können Wildcards verwendet werden. Gibt man also z.B. den Befehl
names.py --summary names*.html ein, werden die jeweiligen Dateinamen als Liste dem Script übergeben. Ihr
Script sollte also darauf ausgelegt sein, dass potentiell mehrere Dateinamen verarbeitet werden sollen.
124