Informatik · Klasse 13 · aktualisiert
Deklarative Programmierung: Was statt Wie
Was soll herauskommen, und wie kommt man dahin? Du lernst den Unterschied zwischen deklarativ und imperativ und ordnest SQL, Prolog und Haskell ein.
0 von 12 Aufgaben gelöst
Kurz gesagt
Deklarative Programmierung ist ein Programmierparadigma, bei dem die Beschreibung des Problems im Vordergrund steht: das Was. Der Lösungsweg wird dann automatisch ermittelt. Bei der imperativen Programmierung steht dagegen das Wie im Vordergrund.
Im Restaurant bestellst du ein Gericht und sagst damit, was du willst. Wie die Küche kocht, musst du nicht erklären. Deklarative Programmierung funktioniert ähnlich. Imperative Programmierung gleicht eher einem Rezept, das jeden Schritt vorgibt.
Deine Lernziele
Hake ab, was du schon kannst. Löst du alle Aufgaben eines Abschnitts, hakt Mela das Ziel für dich ab.
4 Abschnitte
Bei der imperativen Programmierung besteht ein Programm aus einer Folge von Anweisungen. Sie geben vor, in welcher Reihenfolge der Computer was tun soll. Der Entwickler steuert die Ausführung mit Kontrollstrukturen, zum Beispiel mit einer Sequenz, einer Schleife oder einer bedingten Verzweigung. Rezepte und Checklisten ähneln diesem Stil: Jeder Schritt ist eine Anweisung. Bekannte imperative Sprachen sind C, C++ und Java. Sie sind weiter verbreitet als die deklarativen Sprachen.
Bei der deklarativen Programmierung beschreibst du dagegen das Problem. Du fragst nach dem Was, das berechnet werden soll. Der Lösungsweg wird dann automatisch ermittelt.
Achtung: Dass der Prozessor am Ende Maschinencode Befehl für Befehl ausführt, macht ein Programm nicht imperativ. Das ist bei jedem Paradigma so. Entscheidend ist, ob ein Entwickler Code schreibt, der die Schritte detailliert beschreibt. Außerdem sind die Paradigmen, bezogen auf einzelne Programme, keine Gegner. Viele Programmiersprachen können mehrere Paradigmen gleichzeitig unterstützen.
Definition
Deklarative Programmierung
Ein Programmierparadigma, bei dem die Beschreibung des Problems im Vordergrund steht. Der Lösungsweg wird dann automatisch ermittelt.
Beispiel: Quadratzahlen der ungeraden Zahlen von 3 bis 11
- 1Aufgabe: Gib die Quadratzahlen der ungeraden Zahlen von 3 bis 11 aus.
- 2Imperativ, mit Schleife: for (int i = 3; i < 12; i += 2) gibt in jedem Durchlauf i * i aus.
- 3Deklarativ, mit Listenbeschreibung: mapM_ print [ i^2 | i <- [3,5..11] ]
- 4Die Schleife legt fest, wie i hochgezählt wird. Die Listenbeschreibung sagt, welche Zahlen quadriert werden.
- 5Beide Programme geben 9, 25, 49, 81 und 121 aus.
Merke: Deklarativ fragt nach dem Was. Imperativ legt das Wie fest: Schritte in einer bestimmten Reihenfolge.
Teste dich
Quicksort ist ein Sortierverfahren. Es gibt ihn in Pascal (imperativ) und in Haskell (deklarativ).
In Pascal beschreibt der Programmierer, wie der Algorithmus ablaufen muss. Der Lösungsweg ist vorgegeben: Welche einzelnen Schritte nacheinander ablaufen und wie Variablen zu verändern sind, bis das Ergebnis da ist. Im Code siehst du Zuweisungen mit :=, Schleifen mit repeat und das Vertauschen von zwei Werten über die Hilfsvariable tmp.
In Haskell steht nur, was mit der Eingabe geschieht. Der Berechnungsablauf ist nicht von Interesse. Die Zeile quicksort [] = [] sagt: Eine leere Liste ergibt eine leere Liste. Die zweite Zeile sagt: Nimm das erste Element x, der Rest heißt xs. Das Ergebnis ist die sortierte Liste der Elemente aus xs, die kleiner als x sind, dann x, dann die sortierte Liste der Elemente, die mindestens so groß wie x sind. Die Berechnungen erfolgen durch Wertemanipulation. Die Hauptkontrollstruktur ist die Rekursion: quicksort kommt in seiner eigenen Beschreibung wieder vor.
Beispiel: Quicksort in Pascal und in Haskell
- 1Pascal, Ausschnitt 1: repeat i := i + 1; until a[i] >= x;
- 2Pascal, Ausschnitt 2: tmp := a[j]; a[j] := a[i]; a[i] := tmp;
- 3Pascal, Ausschnitt 3: quicksort(l, j - 1); quicksort(j + 1, r);
- 4Haskell: quicksort [] = []
- 5Haskell: quicksort (x:xs) = quicksort [n | n<-xs, n<x] ++ [x] ++ quicksort [n | n<-xs, n>=x]
- 6Pascal gibt Schritt für Schritt vor, wie sich Variablen ändern. Haskell beschreibt nur, wie die Ergebnisliste aufgebaut ist.
Merke: Pascal gibt den Lösungsweg vor. Haskell beschreibt, was mit der Eingabe geschehen soll, und nutzt dafür Rekursion.
Teste dich
Deklarative Sprachen gibt es in mehreren Arten. Drei sind für dich wichtig.
Abfragesprachen sind Datenbanksprachen zur Suche nach Informationen. Das Ergebnis einer Abfrage ist eine Teilmenge der Daten, man spricht deshalb von einer Filterung. SQL enthält eine Abfragesprache für Datenbanksysteme. Bei SQL-Aufrufen legt der Entwickler nur das Was fest. Das Datenbanksystem macht daraus Maschinencode. SQL zählt im weiteren Sinne zu den deklarativen Sprachen.
Funktionale Sprachen wie Haskell verstehen Programme als Funktionen, die für eine Eingabe eine Ausgabe liefern, die nur von dieser abhängt. Funktionen sind ineinander verschachtelte Funktionsaufrufe, keine Abfolge von Anweisungen. Man kommt ohne Schleifen und Zuweisungen aus, braucht aber Rekursion.
Logische Sprachen wie Prolog bestehen aus Fakten und Regeln. Eine Regel ist eine Wenn-Dann-Aussage. Du stellst eine Frage, indem du ein Ziel vorgibst. Der Interpreter leitet die Lösung allein aus den Fakten und Regeln her. In Prolog nutzt er dafür Tiefensuche mit Unifikation und Backtracking.
Weitere Arten sind zum Beispiel Datenflusssprachen (z. B. Val oder Linda) und Transformationssprachen (z. B. XSLT).
| Art | Beispiel | Kernidee |
|---|---|---|
| Abfragesprache | SQL | Sucht Informationen und filtert die Daten |
| Funktionale Sprache | Haskell | Funktionen bilden Eingaben auf Ausgaben ab |
| Logische Sprache | Prolog | Fakten und Regeln; die Antwort wird hergeleitet |
Beispiel: Großmutter-Frage in natürlicher Sprache
- 1Das Beispiel ist in natürlicher Sprache formuliert, nicht in Prolog-Schreibweise.
- 2Fakten: Lucia ist die Mutter von Minna. Lucia ist die Mutter von Klaus. Minna ist die Mutter von Nadine.
- 3Regel: Falls X die Mutter von Y ist und Y die Mutter von Z ist, dann ist X die Großmutter von Z.
- 4Frage: Wer ist die Großmutter von Nadine?
- 5Folgerung aus Fakten und Regel: Lucia
Teste dich
Deklarative Programme haben einige Vorzüge. Sie sind oft kürzer als vergleichbare imperative Programme. Beim Quicksort siehst du das: Der Haskell-Text ist viel kürzer als die Pascal-Prozedur. Es gibt keine Nebenwirkungen, weil die Programme referentiell transparent sind. Deshalb sind sie partiell auswertbar. So lassen sich zum Beispiel unendliche Datenstrukturen behandeln. Beweise über Programmeigenschaften, etwa ein Korrektheitsbeweis, sind dank der einfacheren mathematischen Basis (unter anderem Lambda-Kalkül) leichter durchführbar, falls überhaupt möglich. Dazu kommt die Architekturunabhängigkeit. Ein Nachteil: Die Akzeptanz ist teilweise gering. Man nennt solche Sprachen gern Akademikersprachen.
Der Haskell-Quicksort ist trotzdem kein echter Quicksort mehr. Quicksort ist ein in-Place-Verfahren: Es verändert die bestehende Liste. Der Haskell-Code erzeugt dagegen eine neue Liste. Er ist nur ein ähnlicher Algorithmus.
Definition
In-Place-Verfahren
Ein Verfahren, das die bestehende Liste manipuliert und nicht eine neue Liste erzeugt. Quicksort ist so ein Verfahren.
Merke: Vorzüge deklarativer Programme: oft kürzer, keine Nebenwirkungen, partiell auswertbar. Der Haskell-Quicksort erzeugt eine neue Liste.
Teste dich
Alles auf einen Blick
Deklarative Programmierung
Was und Wie
Deklarative Programme beschreiben das Was, imperative legen das Wie fest.
Imperativ
Eine Folge von Anweisungen bestimmt die Reihenfolge der Schritte.
Quicksort
Die Pascal-Version gibt den Weg vor, die Haskell-Version beschreibt, was mit der Eingabe geschieht.
Arten
Abfragesprachen wie SQL (im weiteren Sinne deklarativ), funktionale Sprachen wie Haskell, logische Sprachen wie Prolog.
Vorzüge
Deklarative Programme sind oft kürzer und haben keine Nebenwirkungen.
In-Place
Der Haskell-Quicksort erzeugt eine neue Liste, deshalb ist er kein echter Quicksort.
Musteraufgabe · Schritt für Schritt
Den Haskell-Quicksort auf [3,1,2] anwenden (ausgedachtes Beispiel)
- 1Eingabe (ausgedacht): [3,1,2]. Regel: quicksort (x:xs) = quicksort [n | n<-xs, n<x] ++ [x] ++ quicksort [n | n<-xs, n>=x]
- 2Schritt 1: x = 3, xs = [1,2]. Kleiner als 3: [1,2]. Mindestens 3: [].
- 3Also: quicksort [1,2] ++ [3] ++ quicksort []
- 4Schritt 2: quicksort [1,2] hat x = 1, xs = [2]. Kleiner als 1: []. Mindestens 1: [2].
- 5Also: quicksort [1,2] = quicksort [] ++ [1] ++ quicksort [2]
- 6Schritt 3: quicksort [2] = [] ++ [2] ++ [] = [2]. Außerdem gilt quicksort [] = [].
- 7Zusammen: quicksort [1,2] = [] ++ [1] ++ [2] = [1,2]. Dann [1,2] ++ [3] ++ [] = [1,2,3].
- 8Bei jedem Schritt entstehen neue Listen. Das passt dazu, dass der Haskell-Code eine neue Liste erzeugt.
Fehler finden
Prüfe die Aussagen zu deklarativen Sprachen und Quicksort In einer Zeile steckt ein Fehler. Tippe sie an.
Fehler in Zeile 4Der Schluss ist falsch: Eine neue Liste statt der bestehenden bedeutet kein in-Place-Verfahren. Richtig: Der Haskell-Code erzeugt eine neue Liste und ist deshalb kein echter Quicksort mehr.
Lückentext
Wähl in jeder Lücke das passende Wort und prüf dann deine Antworten.
Bei der deklarativen Programmierung steht die Beschreibung des Problems im Vordergrund, also das . Der Haskell-Quicksort erzeugt beim Sortieren eine Liste. Ein Programm aus Fakten und Regeln, aus denen ein Interpreter die Antwort herleitet, gehört zu den Sprachen.
Karteikasten
Erst selbst überlegen, dann umdrehen.
Übung mit Feedback