

















Besser lernen dank der zahlreichen Ressourcen auf Docsity
Heimse Punkte ein, indem du anderen Studierenden hilfst oder erwirb Punkte mit einem Premium-Abo
Prüfungen vorbereiten
Besser lernen dank der zahlreichen Ressourcen auf Docsity
Download-Punkte bekommen.
Heimse Punkte ein, indem du anderen Studierenden hilfst oder erwirb Punkte mit einem Premium-Abo
Community
Finde heraus, welche laut den Docsity-Nutzern die besten Unis deines Landes sind
Kostenlose Leitfäden
Lade unsere Leitfäden mit Lernmethoden, Hilfen zur Angstbewältigung und von Docsity-Tutoren erstellte Tipps zum Verfassen von Haus- und Abschlussarbeiten kostenlos herunter
Funktionale Programmierung. ○ Definition. ○ Historie. ○ Motivation. ○ Die Programmiersprache Scala. ○ Einfache funktionale Programme.
Art: Grafiken und Mindmaps
1 / 25
Diese Seite wird in der Vorschau nicht angezeigt
Lass dir nichts Wichtiges entgehen!
Funktionale Programmierung basiert auf Mathematik. Sie ist deklarativ (nicht imperativ). Innerhalb des Paradigmas ist der Ablauf eines Programms nicht definiert. Funktionale Programme basieren auf Ausdrücken , die ausgewertet (evaluiert) werden. In eingeschränkter Form ist funktionale Programmierung die Programmierung ohne veränderliche Variablen , ohne Zuweisung und ohne Kontrollstrukturen. In allgemeinerer Form ist es Programmierung mit besonderer Betonung von Funktionen. Funktionen sind Werte. Man kann durch Operationen neue Funktionen erzeugen. Man kann Funktionen an Funktionen übergeben. Java (Version <= Java 7) ermöglicht die Implementierung von Funktionen durch Funktionsobjekte. Diese werden in der einfachsten Form durch anonyme Klassen definiert. Der Umgang mit Funktionsobjekten ist extrem schwerfällig. Eine Sprache, die die funktionale Programmierung unterstützt, ermöglicht erheblich einfacheren Umgang mit Funktionen. Scala ist objekt-funktional: Das Verhalten von Objekten wird durch funktionale Methoden bestimmt.
Alonso Church definierte 1936 ein umfassendes funktionales System (Lambda-Kalkül). Marvin Minsky entwickelte 1960 am MIT die Programmiersprache LISP (inspiriert vom Lambda-Kalkül). Ziel: mächtige Programmiersprache für Forschung in der KI. LISP war von vom Komfort der Zeit weit voraus (interaktives Arbeiten, dynamische Datenstrukture mit automatischer Speicherbereinigung). Funktionale Programmierung war wesentlicher Ausgangspunkt von objektorientierter Programmierung (Flavors), von höheren Mechanismen der Nebenläufigkeit (Actors) und auch Programmiersprache der ersten graphisch interaktiven Benutzeroberflächen (Lisp-Machine, Loops). Heute spielt funktionale Programmierung eine zunehmende Rolle bei einigen Mustern der objektorientierten Programmierung und Programmiersprache für verteilte Systeme (Erlang, Haskell, Scala).
Ein Programm besteht in der Auswertung eines funktionalen Ausdrucks: (+ (* 4 5) (/ 20 4)) → (+ 20 5) → 25 Funktionen werden durch Lambda-Ausdrücke definiert und können „angewendet“ werden. (( lambda (x) (* 3 x)) 10) → 30 Funktionen können (für spätere Anwendungen) in Variablen gespeichert und an andere Funktionen übergeben werden. ( define mal3 ( lambda (x) (* 3 x))) // formale Definition ( define (mal3 x) (* 3 x)) // abgekürzte Syntax (mal3 5) → 15 ( define (make-erhoehe betrag) // eine Funktion erzeugt eine Funktion ( lambda (x) (+ x betrag))) ( define erhoeheUm3 (make-erhoehe 3)) ( define liste '(1 2 3 4)) ( define liste+3 (map erhoeheUm3 liste)) → (4 5 6 7) // map = Funktion höherer Ordnung Die LISP-Syntax ist sehr einfach und sehr flexibel – aber für Java/C-Programmierer ziemlich ungewohnt. Deshalb bevorzuge ich Scala – auch um zu zeigen, dass funktionale Programmierung nicht exotisch ist!
Entwickelt ab 2001 am EPFL (Lausanne) von Martin Odersky. Scala enthält auch eine ganze Menge von möglichen Verbesserung von Java: ● (^) Keine statischen Klassenelemente ● (^) Alles ist ein Objekt (auch Zahlen und Funktionen) ● (^) Scala fördert die Verwendung von unveränderlichen Variablen ● (^) Typangaben können meist unterbleiben (Typinferenz) ● (^) Es gibt eine Reihe von syntaktischen Verbesserungen gegenüber Java ● (^) High-Level Pattern-Matching Ausdruck ● (^) Closures (= Funktionsobjekte mit Kenntnis der Umgebung) ● (^) Die Scala-Bibliothek unterstützt die funktionale Programmierung mit Objekten ● (^) Scala erlaubt die prozedurale Programmierung. ● (^) Scala ermöglicht die Verwendung aller Java Klassen. ● (^) Das Ziel ist die Erforschung der objekt-funktionalen Programmierung.
trait Entspricht einem Java-Interface + Variablen + Methoden + Mehrfachvererbung (benötigen wir nicht) abstract class wie Java class wie Java case class Klasse für unveränderliche Objekte mit: Mustererkennung, toString, equals, hashCode, Generator object singuläres Objekt mit globalem Namen und anonymer Klasse case object unveränderliches Objekt mit: toString, equals, hashCode class XYZ(a: Double, b: Double) { // primärer Konstruktur private val c = a + b def getc: Double = c // default = public // keine () nötig }
object IntFunctions { def summe(array: Array[Int]): Int = { var sum = 0 // var sum: Int = 0 (Variable) for (x <- array) // = foreach (es gibt kein C-for!) sum += x sum // Rückgabewert } def indexOf(x: Int, array: Array[Int]): Int = { var index = 0 while (index < array.length && array(index) != x) index += 1 // kein i++, Indizierung: array(index) index } def max(array: Array[Int]): Int = { var m = array(0) for (i <- 1 until array.length) // to = incl./ until excl. if (array(i) > m) m = array(i) // prozedurales if m } }
object IntFunctions { def summe(list: Seq[Int]): Int = if (list.isEmpty) 0 else list.head + summe(list.tail) def indexOfGeneric [T] (x: T, array: Array[T]): Int = { // Typparameter @tailrec // ist garantiert endrekursiv def indexFrom(index: Int): Int = // lokale Funktion if (index >= array.length)
else if (x == array(index)) index else indexFrom(index+1) indexFrom(0) // lokale Berechnung } def quadratSumme(list: Seq[Double]): Double = list.map(x => x * x).sum } == ruft das equals() des referierten Objektes auf (Pointergleichheit mit dem Operator eq).
● (^) val = unveränderliche Variable, var = veränderliche Variable, def = Methode/Funktion ● (^) Methodenparameter sind unveränderlich ● (^) object = singuläres Objekt ● (^) Typparameter in eckigen Klammern, Arrayindizes in runden Klammern ● (^) Lokale Funktionen. Umfassende Variablen gelten innen weiter ● (^) if ist funktional (bedingter Ausdruck) ● (^) match .. case Ausdruck (Pattern-Matching) ● (^) For = for-Ausdruck, foreach-Statement ● (^) == entspricht immer equals (für Referenzvergleich gibt es eq) ● (^) Sonderzeichen sind als Funktions-/Methodennamen erlaubt
● (^) Imperative Programmierung sagt, was der Rechner tun soll. ● (^) Deklarative Programmierung beschreibt Sachverhalte ● (^) Imperative Programme nur verständlich, wenn man den Ablauf nachvollzieht!! ● (^) Deklarative Programme kennen keine Seiteneffekte. ● (^) Imperative Programme erfordern separaten Beweis (Invarianten etc.) ● (^) Die Geschichte der Programmiersprachen ist der Versuch die Nachteile der imperativen Programmierung zu beheben (modulare Programmierung, objektorientierte Programmierung ...) ● (^) Imperative Programmierung ist rechnernah => effizient (auch bei schlechtem Compiler). ● (^) Funktionale Programme müssen durch den Compiler in einen effizienten Ablauf umgesetzt werden.
int fak( int n) { int r; if (n == 0) r = 1; else r = n * fak(n – 1); return r; } fak(3)
1. 2. 3. 4. rekursiver Aufruf n r | n r | n r | n r aktuelle lokale Variablen 3 - Werte der Variablen 2 - 1 - 0 - 1 1 1 1 2 2 3 6 Fazit: das Verständnis imperativer Programme ist schwierig. Rekursion ist imperativ schwer verständlich. Imperative Programme sind fehleranfällig !!! Beachte: Rekursion braucht bei N-Wiederholungen O(n) Speicherplatz
def mal3(x: Double) = 3 * x def plus1(x: Double) = x + 1 plus1(mal3(7)-plus1(2)) = plus1((3*7) – (2+1)) = plus1(21 - 3) = plus1(18) = (18 + 1) = 19 ● (^) Auswertung = Gleichungsumformung ● (^) Funktionsanwendung = Ersetzen des Aufrufs durch den Körper, und Ersetzung der Parameter durch die Argumente ● (^) Grundsätzlich ist die Auswertungsreihenfolge beliebig. (lazy Evaluierung, call by name , Nebenläufigkeit)! Scala ist nicht streng funktional! Es lassen sich Funktionen mit Seiteneffekt schreiben. ( Effekt der Funktion: Bestimmung des Ergebniswertes - Seiteneffekt : jede andere Wirkung )
def factorial(n: Int) = { def fac(n: Int, f: Int): Int = if (n == 0) f else fac(n – 1, n * f) fac(n, 1) } factorial(3) = fac(3, 1) = fac(3 – 1, 3 * 1) = fac(2, 3) = fac(2 – 1, 2 * 3) = fac(1, 6) = fac(1 – 1, 1 * 6) = fac(0, 6) = 6 ● (^) Man kann die Umformung auch kürzer schreiben. ● (^) Sobald die Ersetzung ein Ende hat, ist man fertig!
factorial(4) factorial(4) = fac(4, 1) = (4 * factorial(3)) = fac(3, 4) = (4 * (3 * factorial(2))) = fac(2,12) = (4* (3 * (2 * factorial(1)))) = fac(1,24) = (4 * (3 * (2 * (1 * factorial(0))))) = fac(0,24) = (4 * (3 * (2 * (1 * 1)))) = 24 = (4 * (3 * (2 * 1))) = (4 * (3 * 2)) Variable: n, f = (4 * 6) = 24 ● (^) Beide Algorithmen sind rekursiv fomuliert ● (^) Ein Algorithmus wird durch einen Prozess ausgeführt. Links iterativ, rechts rekursiv. Definition : ein iterativer Prozess kann durch einen festen Satz von Zustandsvariablen beschrieben werden. (Speicherkomplexität = O(1)) ein rekursiver Prozess ist durch eine Menge von aufgeschobenen Operationen charakterisiert. (Speicherkomplexität = O(n)) Beachten Sie den feinen Unterschied zu rekursiver und iterativer Funktionsdefinition!