1 Funktionale vs. Imperative Programmierung 1.1 Einführung Programme einer funktionalen Programmiersprache (functional programming language, FPL) bestehen ausschließlich aus Funktionsdefinitionen und Funktionsaufrufen. Die Bausteine der Funktionsdefinitionen sind dabei I der Aufruf weiterer vom Programmierer definierter Funktionen und I der Aufruf elementarer Funktionen (und Operatoren), die durch die FPL selbst definiert werden. Anwendung (application) einer Funktion f auf ein Argument e ist das zentrale Konzept in FPLs und wird daher standardmäßig einfach durch Nebeneinanderschreiben (Juxtaposition) notiert: f e Funktionale Programme werden ausschließlich durch Kompositionen von Funktionsanwendungen konstruiert. © 2003 T. Grust · Funktionale Programmierung: 1. Funktionale vs. Imperative Programmierung 7 Funktionale PL Programmkonstruktion: Applikation und Komposition Operational: Funktionsaufruf, Ersetzung von Ausdrücken Formale Semantik: λ-Kalkül Imperative PL Programmkonstruktion: Sequenzen von Anweisungen Operational: Zustandsänderungen (Seiteneffekte) Formale Semantik: schwierig (denotationale Semantiken) FPLs bieten konsequenterweise folgende Konzepte nicht: I Sequenzoperatoren für Anweisungen (‘;’ in Pascal oder C) (Programme werden durch Funktionskomposition zusammengesetzt, eine explizite Reihung von Anweisungen existiert nicht) I Zustand (FPLs sind zustandslos und bieten daher keine änderbaren Variablen) I Zuweisungen (‘:=’ in Pascal) (Berechnungen in FPLs geschehen allein durch Auswertung von Funktionen, nicht durch Manipulation des Maschinenzustandes bzw. -speichers) © 2003 T. Grust · Funktionale Programmierung: 1. Funktionale vs. Imperative Programmierung 8 Beispiel 1.1 Entwerfe eine Funktion, die testet, ob eine Zahl n eine Primzahl ist oder nicht. O 1 Wenn n prim ist, ist die Menge der Teiler (factors) von n leer. O 2 Die Menge der Teiler von n sind alle Zahlen x zwischen 2 . . . n − 1, die n ohne Rest teilen. Diese Beschreibung der Eigenschaften einer Primzahl läßt sich direkt in ein funktionales Programm (hier: Haskell) übersetzen: -- Ist n eine Primzahl? isPrime :: Integer -> Bool isPrime n = (factors n == []) where factors n = [ x | x <- [2..n-1], n ‘mod‘ x == 0 ] 1 -- O 2 -- O Das Programm liest sich mehr wie die deklarative Spezifikation der Eigenschaften einer Primzahl als eine explizite Vorschrift, den Primzahltest auszuführen. Bspw. ist eine parallele Ausführung von factors nicht ausgeschlossen. © 2003 T. Grust · Funktionale Programmierung: 1. Funktionale vs. Imperative Programmierung 9 Imperative Programmiersprachen sind dagegen eng mit dem zugrundeliegenden von Neumann’schen Maschinenmodell verknüpft, indem sie die Maschinenarchitektur sehr direkt abstrahieren: I der Programmzähler (PC) der CPU arbeitet Anweisung nach Anweisung sequentiell ab (der Programmierer hat seine Anweisungen also explizit aufzureihen und Wiederholungen/Sprünge zu codieren) I der Speicher der Maschine dient zur Zustandsprotokollierung (der Zustand eines Algorithmus muß durch Variablenzuweisung bzw. -auslesen explizit kontrolliert werden) Zusätzlich zur Lösung seines Problemes hat der Programmierer einer imperativen PL die Aufgabe, obige Punkte korrekt zu spezifizieren. Imperative Programme I sind oft länger als ihre FPL-Äquivalente, (Zustandsupdates und -kontrollen sind explizit zu codieren) I sind oft schwieriger zu verstehen, (eigentliche Problemlösung und Kontrolle der von Neumann-Maschine werden vermischt) I sind nur mittels komplexer Methoden auf Korrektheit zu überprüfen. (Bedeutung jedes Programmteils immer von Zustand des gesamten Speichers und Änderungen auf diesem abhängig) © 2003 T. Grust · Funktionale Programmierung: 1. Funktionale vs. Imperative Programmierung 10 Beispiel 1.2 Primzahltest. Formulierung in PASCAL: program isPrime (output); function isPrime (n : integer) : boolean; var m : integer; found_factor : boolean; begin m := 2; found_factor := false; while (m <= n-1) and (not found_factor) do if (n mod m) = 0 then found_factor := true else m := m + 1; isPrime := not found_factor end; { isPrime } begin writeln (isPrime(112)) end. © 2003 T. Grust · Funktionale Programmierung: 1. Funktionale vs. Imperative Programmierung 11 Das Programm kontrolliert die Maschine durch explizite Schleifenanweisungen (while, for), bedingte Anweisungen (if · then · else) und Sequenzierung (‘;’) von Anweisungen. Die Anweisungsfolge ist explizit festgelegt. Das ist das Hauptmerkmal des imperativen Stils. Die Berechnung erfolgt als Seiteneffekt auf den Zustandsvariablen (m, found_factor). Die Variablen dienen gleichzeitig zur Kontrolle der Maschine (m, found_factor) und zur Protokollierung des Ergebnisses des eigentlichen Problems (found_factor). Andere Konzepte imperativer PLs bieten noch weitergehenden direkten Zugriff auf die Maschine: I Arrays und Indexzugriff (A[i]) (modelliert direkt den linearen Speicher der Maschine sowie indizierende Adressierungsmodi der CPU) I Pointer und Dereferenzierung (modellieren 1:1 die indirekten Adressierungsmodi der CPU) I explizite (De-)Allokation von Speicher (malloc, free, new) (der Speicher wird eigenverantwortlich als Resource verwaltet) I Sprunganweisungen (goto) (direkte Manipulation des PC) I ... © 2003 T. Grust · Funktionale Programmierung: 1. Funktionale vs. Imperative Programmierung 12