Stellen Sie sich vor, Sie sitzen in einem Taxi. Es gibt zwei Arten, ans Ziel zu kommen:
Beide bringen Sie ans Ziel. Im ersten Fall bestimmen Sie jeden Schritt – Sie müssen den Weg kennen und tragen die Verantwortung für jeden Abzweiger. Im zweiten Fall beschreiben Sie nur das Ziel – wie der Fahrer dorthin kommt, ist seine Sache.
Genau das ist der Unterschied zwischen imperativer und deklarativer Programmierung. Der erste Stil beschreibt das Wie, der zweite das Was.
Diskussionsfrage für den Einstieg
Wann ist welche Variante besser? Sammeln Sie Argumente, bevor Sie weiterlesen.
Mögliche Antworten: Bei einer Baustelle auf der Strecke will man vielleicht selbst steuern (Kontrolle). Kennt man den Weg nicht, ist die Zielangabe klar überlegen (Abstraktion). Der Taxifahrer kennt zudem Abkürzungen, auf die man selbst nie käme (Optimierung durch das System).
Diese Übung macht den Unterschied körperlich erfahrbar.
Aufbau: Eine Person spielt den Roboter, eine Person gibt die Befehle. Ziel: Der Roboter soll einen Stift vom Pult holen und der Kommandogeberin überreichen.
| Runde | Erlaubte Sprache | Beobachtung |
|---|---|---|
| 1 – Imperativ | Nur Einzelbefehle: SCHRITT VOR, DREHE LINKS 90°, ARM HEBEN, GREIFEN, ARM SENKEN, LOSLASSEN. Kein Nennen von Objekten oder Zielen. | Es braucht viele Befehle. Ein Zählfehler („noch ein Schritt„) führt zum Absturz. Die Kommandogeberin muss den Zustand des Roboters ständig im Kopf mitführen. |
| 2 – Deklarativ | Nur ein einziger Satz, der das Ergebnis beschreibt: Gib mir den roten Stift vom Pult. | Ein Befehl genügt. Der Roboter (also das „System“) entscheidet selbst über Weg, Reihenfolge und Griff. Dafür gibt die Kommandogeberin die Kontrolle über das Wie ab. |
Auswertung im Plenum:
Imperative Programmierung wird oft als die traditionellste Art der Programmierung betrachtet. Sie ist eine Art Anweisungsliste oder Rezept, das dem Computer sagt, was er tun soll.
Deklarative Programmierung ist eher eine Art, dem Computer zu sagen, was er erreichen soll, ohne zu beschreiben, wie er es tun soll.
Wir lösen ein und dieselbe Aufgabe in beiden Stilen. Diese Aufgabenstellung begleitet uns durch das ganze Kapitel.
Aufgabe: Aus einer Klassenliste sollen die Namen aller Lernenden mit einer Note von 5.0 oder besser ermittelt werden – in Grossbuchstaben.
noten = [("Alina", 5.5), ("Ben", 3.5), ("Chiara", 4.0), ("Dario", 5.0), ("Elif", 4.5)]
bestanden = [] # 1. Leeres Gefäss vorbereiten for eintrag in noten: # 2. Jedes Element einzeln ansteuern name = eintrag[0] # 3. Bestandteile herauslösen note = eintrag[1] if note >= 5.0: # 4. Bedingung prüfen bestanden.append(name.upper()) # 5. Zustand verändern print(bestanden) # ['ALINA', 'DARIO']
Fünf gedankliche Schritte, und der Zustand von bestanden ändert sich während des Durchlaufs mehrfach.
Live-Demo: Zeichnen Sie die Liste bestanden an die Tafel und lassen Sie die Klasse nach jedem Schleifendurchgang den neuen Inhalt diktieren. Genau das ist ein Trace Table – siehe LU01d.
bestanden = [name.upper() for name, note in noten if note >= 5.0] print(bestanden) # ['ALINA', 'DARIO']
Gelesen wird das als Satz: „Nimm den Namen in Grossbuchstaben, für jeden Namen und jede Note in der Notenliste, sofern die Note mindestens 5.0 ist.„
bestanden = list(map(lambda p: p[0].upper(), filter(lambda p: p[1] >= 5.0, noten))) print(bestanden) # ['ALINA', 'DARIO']
Diese Bausteine – map, filter und reduce – sind das Herzstück des Moduls und werden später ausführlich behandelt.
SELECT UPPER(name) FROM noten WHERE note >= 5.0;
Auffällig: Die SQL-Abfrage und die List Comprehension sind fast identisch aufgebaut – Was auswählen, woher, unter welcher Bedingung. Deklarativ ist also keine Erfindung der funktionalen Programmierung, sondern ein Prinzip, das Sie längst kennen.
Stellen Sie der Klasse diese Frage und vergleichen Sie Variante 1 mit Variante 2:
| In Variante 1 vorhanden | In Variante 2 verschwunden, weil … |
|---|---|
Die leere Liste bestanden = [] | … kein Zwischenzustand mehr aufgebaut werden muss. |
Die Schleife for | … die Iteration im Sprachkonstrukt steckt. |
Der Index-Zugriff eintrag[0] | … direkt entpackt wird. |
Das append | … nichts mehr verändert, sondern ein neuer Wert erzeugt wird. |
| Die Reihenfolge der fünf Schritte | … nur noch das Ergebnis beschrieben wird. |
Merksatz für die Wandtafel: Imperativ baut ein Ergebnis Schritt für Schritt auf. Deklarativ beschreibt das Ergebnis.
Dieses Beispiel eignet sich hervorragend, um den Preis der Zustandsverwaltung zu zeigen. Lassen Sie den Code laufen, bevor Sie ihn erklären:
def bestanden_imperativ(liste, ergebnis=[]): for name, note in liste: if note >= 5.0: ergebnis.append(name) return ergebnis print(bestanden_imperativ(noten)) # ['Alina', 'Dario'] print(bestanden_imperativ(noten)) # ['Alina', 'Dario', 'Alina', 'Dario'] ← ?!
Derselbe Aufruf, dasselbe Argument – zwei verschiedene Ergebnisse. Der Grund: Die Liste ergebnis wird in Python nur einmal beim Definieren der Funktion angelegt und bleibt zwischen den Aufrufen bestehen. Sie ist veränderlicher Zustand, der die Funktion überlebt.
Genau solche Fehler will die funktionale Programmierung mit Unveränderlichkeit und puren Funktionen ausschliessen – siehe LU01c und LU02.
Diese Paare eignen sich gut zum Sammeln an der Wandtafel. Lassen Sie die Klasse jeweils raten, welche Spalte welchem Paradigma entspricht.
| Situation | Imperativ (das Wie) | Deklarativ (das Was) |
|---|---|---|
| Kaffee | „Mahle 18 g Bohnen, verteile sie gleichmässig, presse mit 30 kg an, brühe 25 Sekunden …“ | „Einen Espresso, bitte.„ |
| Möbel | Die IKEA-Anleitung mit 34 nummerierten Schritten | „Stell mir ein Bett ins Zimmer.“ |
| Bildbearbeitung | Über jedes Pixel iterieren und den Helligkeitswert um 20 erhöhen | Schieberegler „Helligkeit +20 %„ |
| Webseite | Mit Canvas Linien, Rechtecke und Text an Koordinaten zeichnen | <h1>Titel</h1> – der Browser entscheidet über Schriftgrösse und Position |
| Wäsche | „Fülle Wasser ein, heize auf 40 °C, drehe die Trommel 12 Minuten …“ | Programm „Buntwäsche 40°„ wählen |
| Daten | Über alle Datensätze schleifen und passende sammeln | SELECT … WHERE … |
Woran erkennt man den Stil beim Blick auf fremden Code?
| Merkmal | Deutet auf |
|---|---|
for / while mit Zählvariable | imperativ |
Eine Variable wird mehrfach überschrieben (summe += x) | imperativ |
append, insert, sort(), del – die Daten selbst werden verändert | imperativ |
List Comprehension, map, filter, sum, sorted() | deklarativ |
| Es wird ein neuer Wert erzeugt statt ein bestehender geändert | deklarativ |
| Der Code liest sich wie ein Satz über das Ergebnis | deklarativ |
Ein guter Test: Kann man den Code laut vorlesen und es klingt wie eine Beschreibung des Ergebnisses – oder wie eine Anleitung?
Achtung: deklarativ heisst nicht automatisch seiteneffektfrei
Seiteneffektfreiheit ist keine Eigenschaft der deklarativen Programmierung an sich, sondern der puren funktionalen Programmierung – also einer Teilmenge davon.
SQL ist deklarativ, aber ein UPDATE kunden SET status = 'aktiv'; verändert sehr wohl den Zustand der Datenbank. Und HTML ist deklarativ, kennt aber gar keine Funktionen.
Richtig ist: Deklarativ beschreibt das Was statt das Wie. Pur funktional verlangt zusätzlich, dass eine Funktion nichts ausserhalb ihrer selbst verändert. Mehr dazu in LU01c und in LU02 (Pure Functions).
Ü1 – Umschreiben. Formulieren Sie diesen imperativen Code deklarativ um:
zahlen = [3, 8, 1, 9, 4, 7] gross = [] for z in zahlen: if z > 5: gross.append(z * 10)
Ü2 – Zuordnen. Imperativ oder deklarativ? sum(zahlen) · liste.sort() · sorted(liste) · while i < 10: · max(noten)
Ü3 – Übersetzen. Beschreiben Sie das Rezept „Spiegelei braten“ einmal imperativ (Schritt für Schritt) und einmal deklarativ (nur das Ergebnis). Was geht bei der deklarativen Fassung verloren, was gewinnt man?
Lösungshinweise
Ü1: gross = [z * 10 for z in zahlen if z > 5]
Ü2: sum deklarativ · liste.sort() imperativ (verändert die Liste, gibt None zurück) · sorted(liste) deklarativ (liefert eine neue Liste, Original bleibt unverändert) · while imperativ · max deklarativ. Das Paar sort() / sorted() ist besonders lehrreich: gleiche Aufgabe, unterschiedliches Paradigma.
Ü3: Verloren geht die Kontrolle über Details (Pfanne, Hitze, Garzeit). Gewonnen werden Kürze und die Freiheit des Ausführenden, den besten Weg zu wählen.
Weiterführende Aufgaben: LU01.A01 und LU01.A03 lösen dasselbe Sortierproblem in beiden Stilen.
Während die imperative Programmierung den Programmierern die vollständige Kontrolle über den Prozess gibt, bietet die deklarative Programmierung eine elegante und oft intuitivere Methode zur Lösung von Problemen. Die Wahl zwischen diesen Stilen hängt oft von der Art des Problems, den Anforderungen an die Effizienz und die Vorlieben des Programmierers ab. In der modernen Softwareentwicklung werden oft Elemente beider Stile kombiniert, um eine effiziente und wartbare Lösung zu schaffen.