3. Funktionales Programmieren 3.1 Grundkonzepte funktionaler Programmierung 3. Funktionales Programmieren Beschreibung von Werten: Beschreibung von Werten: (2) • durch geschachtelte Anwendung von Funktionen: • mittels Konstanten oder Bezeichnern für Werte: 45.67 + 6857 * (-9) floor ( -3.4) * truncate ( -3.4) toEnum ((( fromEnum (last("Urin"++" stinkt ")))+2))::Char 23 "Ich bin eine Zeichenreihe " True x • durch Verwendung des bedingten Ausdrucks (engl. conditional • durch direkte Anwendung von Funktionen: expression): abs ( -28382) "Urin" ++ " stinkt " not True ©Arnd Poetzsch-Heffter 3.1 Grundkonzepte funktionaler Programmierung if <boolAusdruck > then <Ausdruck > else <Ausdruck > TU Kaiserslautern 3. Funktionales Programmieren 193 ©Arnd Poetzsch-Heffter 3.1 Grundkonzepte funktionaler Programmierung TU Kaiserslautern 3. Funktionales Programmieren 3.1 Grundkonzepte funktionaler Programmierung Begriffsklärung: (Ausdruck, expression) Begriffsklärung: (Ausdruck, expression) (2) Ausdrücke sind das Sprachmittel zur Beschreibung von Werten. Ein Ausdruck (engl. expression) in Haskell ist Jeder Ausdruck hat einen Typ: • Der Typ einer Konstanten ergibt sich aus der Signatur. • eine Konstante, • Der Typ eines Bezeichners ergibt sich aus dem Wert, den er bezeichnet. • ein Bezeichner (Variable, Name), • Der Typ einer Funktionsanwendung ist der Ergebnistyp der • die Anwendung einer Funktion auf einen Ausdruck, Funktion. • ein bedingter Ausdruck gebildet • Der Typ eines if-then-else-Ausdrucks ist gleich dem Typ des • oder ist mit Sprachmitteln aufgebaut, die erst später behandelt Ausdruck im then- bzw. else-Zweig. werden. ©Arnd Poetzsch-Heffter 194 TU Kaiserslautern 195 ©Arnd Poetzsch-Heffter TU Kaiserslautern 196 3. Funktionales Programmieren 3.1 Grundkonzepte funktionaler Programmierung 3. Funktionales Programmieren Präzedenzregeln: 3.1 Grundkonzepte funktionaler Programmierung Präzedenzregeln: (2) Präzedenzregeln legen fest, wie Ausdrücke zu strukturieren sind: • Am stärksten binden Funktionsanwendungen in Präfixform. • Regeln für Infix-Operatoren: Wenn Ausdrücke nicht vollständig geklammert sind, ist im Allg. nicht klar, wie ihr Syntaxbaum aussieht. infixl 7 ∗, /, div, mod infixl 6 +, − infix 4 ==, / =, <, >, <=, >= infixr 3 && infixr 2 || Je höher die Präzedenzzahl, desto stärker binden die Operationen. Beispiele: 3 == 5 == True False == True || True False && True || True • Mit “infixl”/“infixr” gelistete Operatoren sind links-/rechtsassoziativ, d.h. sie werden von links/rechts her geklammert. • Mit “infix” gelistete Operatoren müssen geklammert werden. ©Arnd Poetzsch-Heffter TU Kaiserslautern 3. Funktionales Programmieren 197 3.1 Grundkonzepte funktionaler Programmierung 198 3.1 Grundkonzepte funktionaler Programmierung Begriffsklärung: (Vereinbarung, Deklaration, Bindung) In Programmiersprachen dienen Vereinbarungen oder Deklarationen (engl. declaration) dazu, den in einem Programm verwendeten Elementen Bezeichner/Namen zu geben. Bisher haben wir Ausdrücke formuliert, die sich auf die vordefinierten Funktions- und Konstantenbezeichner von Haskell gestützt haben. Syntaktisch gesehen heißt Programmierung: Dadurch entsteht eine Bindung (n, e) zwischen dem Bezeichner n und dem bezeichneten Programmelement e. • neue Typen, Werte und Funktionen zu definieren, • die neu definierten Elemente unter Bezeichnern zugänglich zu An allen Programmstellen, an denen die Bindung sichtbar ist, kann der Bezeichner benutzt werden, um sich auf das Programmelement zu beziehen. machen. TU Kaiserslautern TU Kaiserslautern 3. Funktionales Programmieren Deklaration und Bezeichnerbindung: ©Arnd Poetzsch-Heffter ©Arnd Poetzsch-Heffter 199 ©Arnd Poetzsch-Heffter TU Kaiserslautern 200 3. Funktionales Programmieren 3.1 Grundkonzepte funktionaler Programmierung 3. Funktionales Programmieren Bemerkung: Wertvereinbarungen: • Die verschiedenen Arten an Programmelementen, die in • Wertvereinbarungen haben (u.a.) die Form: Deklarationen vorkommen können, hängen von der Programmiersprache ab. <Bezeichner > • In Haskell sind es im Wesentlichen: 1. Bezeichnervereinbarungen (nur zusammen mit Wert-/Funktionsvereinbarung) 2. Wertvereinbarungen 3. Vereinbarungen (rekursiver) Funktionen 4. Vereinbarungen benutzerdeklarierter Typen 3. Funktionales Programmieren <Ausdruck > ; voranstellen, um den Typ des Bezeichners zu deklarieren: <Bezeichner > :: <Typ > ; <Bezeichner > = <Ausdruck > ; Der Typ des Ausdrucks muss gleich dem vereinbarten Typ sein. • Der rechtsseitige Ausdruck darf nur sichtbare Bezeichner festlegen, sind ebenfalls sprachabhängig und können sehr komplex sein. Wir führen die Sichtbarkeitsregeln schrittweise ein TU Kaiserslautern = • Wertvereinbarungen kann man eine Bezeichnervereinbarung • Die Regeln, die die Sichtbarkeit von Bindungen bzw. Bezeichern ©Arnd Poetzsch-Heffter 3.1 Grundkonzepte funktionaler Programmierung enthalten. 201 ©Arnd Poetzsch-Heffter TU Kaiserslautern 3.1 Grundkonzepte funktionaler Programmierung 3. Funktionales Programmieren Beispiele: (Wertvereinbarungen) 202 3.1 Grundkonzepte funktionaler Programmierung Beispiele: (Wertvereinbarungen) (2) Einzeilige Vereinbarungen im Interpreter ghci: let b = 56 ; b = 56 ; Mehrzeilige Vereinbarungen im Interpreter ghci: a::Int a = 7 :{ let { a::Int a = 7 sieben sieben flag dkv } :} sieben sieben flag dkv :: Float ; = 7.0 ; = floor sieben == truncate (- sieben ) = " Deutscher Komiker Verein e.v." ©Arnd Poetzsch-Heffter TU Kaiserslautern 203 ©Arnd Poetzsch-Heffter ; ; :: Float ; = 7.0 ; = floor sieben == truncate (- sieben ) ; = " Deutscher Komiker Verein e.v." TU Kaiserslautern 204 3. Funktionales Programmieren 3.1 Grundkonzepte funktionaler Programmierung 3. Funktionales Programmieren Funktionsvereinbarungen: 3.1 Grundkonzepte funktionaler Programmierung Beispiele: (Funktionsvereinbarungen) Zwei Probleme: 1. Bisher haben wir keine Ausdrücke, die eine Funktion als Ergebnis liefern (Ausnahme: Funktionsbezeichner) 2. Funktionen können rekursiv sein, d.h. der Funktionsbezeichner kommt im definierenden Ausdruck vor. myDivision :: Integer -> Integer -> Interger myDevision = div fac :: Integer -> Integer -- Argument n muss >= 0 sein fac n = if n==0 then 1 else n * fac (n -1) Lösungen: Zu 1. Erweitere die Menge der Ausdrücke, so dass Ausdrücke Funktionen beschreiben können. Dann kann die obige Wertvereinbarung genutzt werden. plus2 :: Integer -> Integer plus2 = (+) 2 Zu 2. Erlaube selbstbezügliche Deklarationen (Haskells Lösung) oder benutze spezielle Syntax für rekursive Funktionsdeklarationen. Genaueres dazu in Unterabschnitt 3.1.2 (Folien 215ff). ©Arnd Poetzsch-Heffter TU Kaiserslautern 3. Funktionales Programmieren 205 ©Arnd Poetzsch-Heffter 3.1 Grundkonzepte funktionaler Programmierung TU Kaiserslautern 3. Funktionales Programmieren 3.1 Grundkonzepte funktionaler Programmierung Typvereinbarungen: Beispiele: (Typvereinbarungen) Zwei Probleme: type IntPaar = (Int ,Int) ; type CharList = [Char] ; type Telefonbuch = [(( String ,String ,String ,Int) ,[ String ])] ; 1. Bisher haben wir keine Ausdrücke, die Typen als Ergebnis liefern. 2. Typen können rekursiv sein, d.h. der vereinbarte Typbezeichner kommt im definierenden Typausdruck vor. type IntegerNachInteger Lösungen: Zu 1. Führe “Ausdrücke” für Typen ein (z.B. Int -> Int). Genaueres dazu in Unterabschnitt 3.1.4. (Folien 269ff). TU Kaiserslautern Integer -> Integer ; fakultaet :: IntegerNachInteger ; -- Argument muss >= 0 sein fakultaet = fac ; Zu 2. Benutze spezielle Syntax für rekursive Typdeklarationen. ©Arnd Poetzsch-Heffter = 206 207 ©Arnd Poetzsch-Heffter TU Kaiserslautern 208 3. Funktionales Programmieren 3.1 Grundkonzepte funktionaler Programmierung 3. Funktionales Programmieren Begriffsklärung: (Bezeichnerumgebung) 3.1 Grundkonzepte funktionaler Programmierung Bemerkungen: • Programmiersprachen stellen üblicherweise eine Standard-Umgebung bereit mit den vordefinierten Programmelementen (Werten, Funktionen, Typen, etc.). In Haskell ist die Standard-Umgebung durch das Modul Prelude definiert. Eine Bezeichnerumgebung ist eine Abbildung von Bezeichnern auf Werte (einschl. Funktionen) und Typen, ggf. auch auf andersartige Programmelemente. • Eine Bezeichnerumgebung wird häufig als Liste von Bindungen Oft spricht man auch von Namensumgebung oder einfach von Umgebung (engl. environment). modelliert (vgl. Folie 201). • Jede Datenstruktur und jedes Modul definiert eine Bezeichnerumgebung. ©Arnd Poetzsch-Heffter TU Kaiserslautern 3. Funktionales Programmieren 209 ©Arnd Poetzsch-Heffter 3.1 Grundkonzepte funktionaler Programmierung TU Kaiserslautern 3. Funktionales Programmieren Begriffsklärung: (Programm) 210 3.1 Grundkonzepte funktionaler Programmierung Beispiel: (funktionales Programm) import System .IO fac :: Integer -> Integer -- Argument n muss >= 0 sein fac n = if n==0 then 1 else n * fac (n -1) Ein Programm besteht in der Regel aus • einer Menge von Deklarationen und • einer (durch einen besonderen Namen) ausgezeichneten Funktion bzw. Prozedur (oder ähnlichem Konstrukt), die angibt, wie die Auswertung bzw. Ausführung des Programms zu starten ist. ©Arnd Poetzsch-Heffter TU Kaiserslautern 211 main = do { hSetBuffering stdout NoBuffering ; putStr " Eingabe x (x>=0): "; a <- readLn ; putStr " Ergebnis (fac x): "; print (fac a); } ©Arnd Poetzsch-Heffter TU Kaiserslautern 212 3. Funktionales Programmieren 3.1 Grundkonzepte funktionaler Programmierung 3. Funktionales Programmieren Unterabschnitt 3.1.2 3.1 Grundkonzepte funktionaler Programmierung Begriffsklärung: (Funktionsabstraktion Eine Funktion kann man durch einen Ausdruck beschreiben, in dem die Argumente der Funktion durch Bezeichner vertreten sind. Um deutlich zu machen, welche Bezeichner für Argumente stehen, werden diese deklariert. Alle anderen Bezeichner des Ausdrucks müssen anderweitig gebunden werden. Rekursive Funktionen Diesen Schritt von einem Ausdruck zu der Beschreibung einer Funktion nennt man Funktionsabstraktion oder λ-Abstraktion. ©Arnd Poetzsch-Heffter TU Kaiserslautern 3. Funktionales Programmieren 213 ©Arnd Poetzsch-Heffter 3.1 Grundkonzepte funktionaler Programmierung TU Kaiserslautern 3. Funktionales Programmieren Beispiele: Funktionsabstraktion 214 3.1 Grundkonzepte funktionaler Programmierung Beispiele: Funktionsabstraktion (2) 2. Volumenberechnung eines Kegelstumpfes: 1. Quadratfunktion: Ausdruck: Abstraktion: Haskell-Notation: Formel: Sei h die Höhe, rk , rg die Radien; dann ergibt sich das Volumen v zu x ∗x λx.(x ∗ x) \ x -> (x * x) π∗h ∗ (rk 2 + rk ∗ rg + rg 2 ) 3 Haskell-Ausdruck für die rechte Seite: v= Vereinbarung eines Bezeichners für die Funktion: (pi * h) / 3.0 * ( rk **2 + rk*rg + rg **2 ) quadrat = \ x -> (x * x) Abstraktion in Haskell-Syntax und Vereinbarung von v: v = \ h rk rg -> (pi*h)/3.0 * (rk **2+ rk*rg+rg **2) ©Arnd Poetzsch-Heffter TU Kaiserslautern 215 ©Arnd Poetzsch-Heffter TU Kaiserslautern 216