Deklarative (= fortgeschrittene) Programmierung Teile der Lehrveranstaltung: I I Vorlesung Hausaufgaben: I I I Übungssserien autotool Praktika Inhalt I Terme, Haskell-Ausdrücke I Termersetzungssysteme I algebraische Datentypen Pattern Matching I Typen, Polymorphie I Typinferenz, Unifikation I Typklassen zur Steuerung der Polymorphie I Rekursive Datenypen I Lambda-Kalkül I Funktionen höherer Ordnung I Rekursionsschemata fold, map, filter, ... I Bedarfsauswertung I unendliche Datenstrukturen Terme, Haskell-Ausdrücke I mehrsortige (funktionale) Signatur Σ Haskell-Beispiel ( ÜA 1.3 ) : square :: Int -> Int mini :: Int -> Int -> Int sum3 :: Int -> Int -> Int -> Int eq2of3 :: Int -> Int -> Int -> Bool I Terme über Σ Haskell-Beispiel: t = eq2of3 (sum3 3 2 (square 2)) (mini (sum3 3 2 2) (square 2)) (square 3) I Positionen in Termen z.B. [0, 2, 0] ∈ Pos(t), [0, 2, 1] 6∈ Pos(t) I Teilterme an Positionen t[0,2,0] = 2, t[0,2] = square 2 ÜA: alle Positionen in t Unifikation I Substitutionen t[p := s] I unifizierbare Terme I allgemeinster Unifikator zweier Terme ÜA 4.1: allgemeinste Unifikatoren von R(a, x) , R(y , y ) f (g(x), z) , f (g(y ), g(z)) f (g(x), y ) , f (y , h(x)) f (x, g(x)) , f (g(y ), y ) f (x, g(y )) , f (g(y ), x) f (y , g(a, z)) , f (b, g(a, b)) f (y , g(x, y )) , f (b, g(a, y )) f (y , g(x, x)) , f (b, g(a, y )) f (y , g(x, y )) , f (h(z), g(a, z)) Termersetzung I Termersetzungsregel = Paar von Termen, z.B. (m(B(x, y , z), m(x)), Noation m(B(x, y , z) → m(x) I Termersetzungssystem: Menge von Termersetzungsregeln, z.B. {m(B(L, y , z) → y , m(B(x, y , z) → m(x)} I Ableitung im Termersetzungssystem, z.B. m(B(B(L, 3, x), 5, x) → m(B(L, 3, x)) → 3 I Redex, Normalform Auswertung von Termen (Bestimmung des Wertes): Ableitung bis zu einer Normalform Haskell-Funktionsdeklarationen sind Termersetzungsregeln. Haskell-Programme sind Termersetzungssysteme (Konstruktorsysteme). Beispiele: head (x : xs) = x second xs = head ( tail xs ) einsen = 1 : einsen Algebraische Datentypen Datentypen: einfach Int, Bool, Char, ... zusammengesetzt durch Mengenoperationen ∪, ×, → Haskell-Beispiele: data Unit = () data Bool = True | False data Pos = A | B | C data Punkt = Punkt {x :: Float, y :: Float} data Shape = Circle {mp :: Punkt, rad :: Float} | Rect {ol, ur :: Punkt} data Maybe a = Nothing | Just a ÜA 3.3: alle Werte vom Typ Maybe (Bool, Maybe ()) Polymorphie Typvariablen a,b,c,... Typinferenzregel: f :: A → B e :: A f e :: B ÜA 4.2: allgemeinster Typ des Haskell-Ausdrucks fst ( head ( f x ) ) für f :: ( [ a ] , b ) -> [ ( b , a ) ] x = ("foo", [ ( 2 , Just _ , True ) ] ) Pattern Matching data T = C1 ... | C2 ... typisches Vorgehen beim Programmieren einer Funktion f :: T -> ... für jeden Konstruktor des Datentyps ein Zweig in der Fallunterscheidung f x = case x of C1 ... -> ... C2 ... -> ... Typklassen data Ordering = LT | EQ | GT class Ord a where compare :: a -> a -> Ordering instance Ord Nat compare Z Z = compare Z _ = compare _ Z = compare (S x) where EQ LT GT (S y) = compare x y data Bool = True | False instance Show Bool where show True = "wahr" show False = "falsch" oder automatisch data Bool = True | False deriving Show ÜA 7.3: Eq-Instanz für Peano-Zahlen Eingeschränkte Polymorphie Typ-Contraints f :: TC a => ... für jeden Typ a, für den eine TC-Instanz definiert wurde (und damit die Methoden dieser Typklasse definiert) Haskell-Beispiele (ÜA 1.3): square :: Num a => a -> a square x = x * x mini :: Ord a => a -> a -> a mini x y = if x < y then x else y sum3 :: Num a => a -> a -> a -> a sum3 x y z = x + y + z eq2of3 :: Eq a => a -> a -> a -> a -> Bool eq2of3 x y z = not ((x == y) && (x == z)) && ((x == x) || (x == z) || (y == z)) Rekursive Datentypen I Peano-Zahlen I Listen I Binärbäume mit Schlüsseln in inneren Knoten I ÜA 6.3: Binärbäume mit Schlüsseln in Blättern I Formeln (ÜA 7.4): Datentyp Formel zur Repräsentation variablenfreier aussagenlogischer Formeln mit den Junktoren (¬, ∨, ∧, →) mit Wahrheitswertkonstanten in den Blättern Strukturelle Induktion für rekursive Datentypen data T = C1 | .. | Cn | Nachweis, dass jedes Element des Typs T die Eigenschaft p erfüllt durch strukturelle Induktion IA: p gilt für alle nullstelligen Konstruktoren C1, ..., Cn IS: für jeden k -stelligen Konstruktor D: IV: p gilt für t1, ..., tk IB: p gilt für D t1 ... tk ÜA 2.3.b: Addition auf Peano-Zahlen ist kommutativ sum = fold 0 (+) sum ( append xs ys ) = sum xs + sum ys ÜA 2.4.d: reverse [x] = [x], reverse (reverse xs) = xs Funktionen höherer Ordnung Beispiel: head :: [ a ] -> a head ( x : _ ) = x als anonyme Funktion (Lambda-Ausdruck) \ ( x : _ ) -> x als Argument einer Funktion höherer Ordnung map :: ( a -> b ) -> [ a ] -> [ b ] map (\ ( x : _ ) -> x ) ["foo" ,"bar"] = "fb" Lambda-Kalkül Syntax: I Abstraktion, Applikation I freie, gebundene Variablen I Redexe I gebundene Umbenennung Semantik: β-Reduktion ÜA 8.2.d: (λxyz.xz(yz))(λxy .x)(λxy .x)c reduzieren Auswertung von ( \ y -> ( \ x -> x * y ) ) 3 4 Rekursionsmuster I fold für I I I I map für I I I Peano-Zahlen Listen Bäume Listen Bäume filter, zip, zipWith für Listen ÜA 10.5: fold für aussagenlogische Formeln Bedarfsauswertung Bedarfsauswertung (lazy evaluation): I Reduktionsstrategie: leftmost outermost I mit Sharing Beispiel: fst ( 1 + 2 ) ( 2 + 3 ) ermöglicht einfachen Umgang mit unendlichen Datenstrukturen (z.B. Streams) einsen :: [ Int ] einsen = 1 : einsen take 2 einsen ÜA 10.1: sum $ map (\ n -> n * n) $ take 3 nats Softwaretechnische Vorteile der deklarativen Programmierung: Beweisbarkeit : Rechnen mit Programmen wie in der Mathematik mit Termen Sicherheit : es gibt keine Nebenwirkungen und Wirkungen sieht man bereits am Typ Wiederverwendbarkeit : durch Funktionen höherer Ordnung (Entwurfsmuster) Effizienz : durch Programmtransformationen im Compiler Parallelisierbarkeit : durch Nebenwirkungsfreiheit