Programmieren in Haskell Programmiermethodik Programmieren in Haskell 1 Was wir heute machen • Spezifikation • Strukturelle Rekursion • Strukturelle Induktion Programmieren in Haskell 2 Spezifikation sort [8, 3, 5, 3, 6, 1] ⇒ [1, 3, 3, 5, 6, 8] sort "hello world" ⇒ " dehllloorw" sort ["Bein", "Anfall", "Anna"] ⇒ ["Anfall", "Anna", "Bein"] sort [(1, 7), (1, 3), (2, 2)] ⇒ [(1, 3), (1, 7), (2, 2)] Programmieren in Haskell 3 sort :: (Ord a) => [a] -> OrdList a ordered ordered ordered ordered :: (Ord a) [] [a] (a1:a2:as) => [a] -> Bool = True = True = a1 <= a2 && ordered (a2:as) Spezifikation 1: Für alle Listen x :: [τ ] muß gelten: ordered (sort x) = True . (1) Reicht das? Nein: \x -> [] Programmieren in Haskell 4 Multimengen ∅ die leere Multimenge, *a+ die einelementige Multimenge, die genau ein Vorkommen von a enthält, x ] y die Vereinigung der Elemente von x und y; das „+“ im Vereinigungszeichen deutet an, daß sich die Vorkommen in x und y akkumulieren. Programmieren in Haskell ∅]x = x (2) x]∅ = x (3) x]y = y]x (4) (x ] y) ] z = x ] (y ] z) (5) 5 bag :: [a] -> Bag a bag [] = ∅ bag (a:as) = *a+ ] bag as Spezifikation 2: Für alle Listen x :: [τ ] muß gelten: ordered (sort x) = True ∧ bag (sort x) = bag x . Programmieren in Haskell (6) 6 Strukturelle Rekursion auf Listen length :: [a] -> Int length [] = 0 length (a:as) = 1 + length as Programmieren in Haskell -- vordefiniert 7 Rekursionsbasis: [] Rekursionsschritt: (a:as) Schema der strukturellen Rekursion auf Listen: f f [] f (a : as) where s Programmieren in Haskell :: = = = [σ] -> τ e1 e2 f as 8 Sortieren durch Einfügen insertionSort insertionSort [] insertionSort (a : as) where s :: = = = (Ord a) => [a] -> OrdList a e1 e2 insertionSort as insertionSort :: (Ord a) => [a] -> OrdList a insertionSort [] = [] insertionSort (a:as) = insert a (insertionSort as) Programmieren in Haskell 9 Erweitertes Rekursionsschema: g g i [] g i (a : as) where s insert insert a [] insert a (a’ : as) where s Programmieren in Haskell :: = = = :: = = = σ1 -> [σ2 ] -> τ e1 e2 g e3 as (Ord a) => a -> OrdList a -> OrdList a e1 e2 insert e3 as 10 insert a [] insert a (a’ : as) | a <= a’ | otherwise where s = [a] = = = e21 e22 insert e3 as insert :: (Ord a) => a -> [a] -> [a] insert a [] = [a] insert a (a’:as) | a <= a’ = a:a’:as | otherwise = a’:insert a as Programmieren in Haskell 11 isort (8 : 3 : 5 : 3 : 6 : 1 : []) ⇒ ins 8 (isort (3 : 5 : 3 : 6 : 1 : [])) (Def. isort) ⇒ ins 8 (ins 3 (isort (5 : 3 : 6 : 1 : []))) (Def. isort) ⇒ ... ⇒ ins 8 (ins 3 (ins 5 (ins 3 (ins 6 (ins 1 []))))) ⇒ ins 8 (ins 3 (ins 5 (ins 3 (ins 6 (1 : []))))) (Def. ins) ⇒ ins 8 (ins 3 (ins 5 (ins 3 (1 : ins 6 [])))) (Def. ins) ⇒ ins 8 (ins 3 (ins 5 (1 : ins 3 (ins 6 [])))) (Def. ins) ⇒ ... ⇒ 1 : ins 8 (ins 3 (ins 5 (ins 3 (ins 6 [])))) Programmieren in Haskell (Def. isort) (Def. ins) 12 Strukturelle Rekursion auf Bäumen data Tree a = Nil | Leaf a | Br (Tree a) (Tree a) deriving Show Rekursionsbasis (Nil) Das Problem wird für den leeren Baum gelöst. Rekursionsbasis (Leaf a) Das Problem wird für das Blatt Leaf a gelöst. Rekursionsschritt (Br l r) Um das Problem für den Baum Br l r zu lösen, werden rekursiv Lösungen für l und r bestimmt, die zu einer Lösung für Br l r erweitert werden. Programmieren in Haskell 13 Schema der strukturellen Rekursion of Bäumen: f f Nil f (Leaf a) f (Br l r) where sl sr Programmieren in Haskell :: = = = = = Tree σ -> τ e1 e2 e3 f l f r 14 size size size size :: Tree a -> Integer Nil = 1 (Leaf _) = 1 (Br l r) = size l + size r depth depth depth depth :: Tree a -> Integer Nil = 0 (Leaf _) = 0 (Br l r) = max (depth l) (depth r) + 1 Programmieren in Haskell 15 Das allgemeine Rekursionsschema data T a1 . . . am = C1 t11 . . . t1n1 | ... | Cr tr1 . . . trnr Seien li1 ,. . . , lipi mit 1 6 li1 < li2 < · · · < lipi 6 ni die Positionen, an denen der Konstruktor Ci rekursiv ist Programmieren in Haskell 16 Allgemeines Schema der strukturellen Rekursion: f f (C1 x11 . . . x1n1 ) where s11 ... s1p1 ... f (Cr xr1 . . . xrnr ) where sr1 ... srpr Programmieren in Haskell :: = = T σ1 . . . σm -> τ e1 f x1l11 = f x1l1p1 = = er f xrlr1 = f xrlrpr 17 Verstärkung der Rekursion reverse’’ :: [a] -> [a] -- vordefiniert reverse’’ [] = [] reverse’’ (a:as) = reverse’’ as ++ [a] Spezifikation: reel reel x y Programmieren in Haskell :: = [a] -> [a] -> [a] reverse x ++ y 18 Rekursionsbasis (x = []): reel [] y Programmieren in Haskell = reverse [] ++ y (Spezifikation) = [] ++ y (Def. reverse) = y (Def. (++)) 19 Rekursionsschritt (x = a:as): reel (a:as) y Programmieren in Haskell = reverse (a:as) ++ y (Spezifikation) = (reverse as ++ [a]) ++ y (Def. reverse) = reverse as ++ ([a] ++ y) (Ass. (++)) = reverse as ++ (a:y) (Def. (++)) = reel as (a:y) (Spezifikation) 20 reel :: [a] -> [a] -> [a] reel [] y = y reel (a:as) y = reel as (a:y) reverse xs = reverse xs ++ [] = (reel xs []) (Def. (++)) (Spezifikation) reverse’’’ :: [a] -> [a] reverse’’’ as = reel as [] Programmieren in Haskell 21 Strukturelle Induktion auf Listen Induktionsbasis ([]): Wir zeigen die Aussage zunächst für die leere Liste []. Induktionsschritt (a:as): Wir nehmen an, daß die Aussage für die Liste as gilt, und zeigen, daß sie unter dieser Voraussetzung auch für a:as gilt. Beweisregel: Φ([]) (∀a, as) Φ(as) =⇒ Φ(a:as) (∀x) Φ(x) Programmieren in Haskell 22 Spezifikation von insert: Programmieren in Haskell ordered x = True =⇒ ordered (insert a x) = True (7) bag (insert a x) = *a+ ] bag x (8) 23 Φ(x) ⇐⇒ (∀a) bag (insert a x) = *a+ ] bag x (9) Induktionsbasis (x = []): bag (insert a []) Programmieren in Haskell = bag [a] = *a+ ] bag [] (Def. insert) (Def. bag) 24 ⇐⇒ Φ(x) (∀a) bag (insert a x) = *a+ ] bag x Induktionsschritt (x = a’:as): Fall a <= a’ = True: bag (insert a (a’:as)) Programmieren in Haskell = bag (a:a’:as) = *a+ ] bag (a’:as) (Def. insert) (Def. bag) 25 ⇐⇒ Φ(x) (∀a) bag (insert a x) = *a+ ] bag x Induktionsschritt (x = a’:as): Fall a <= a’ = False: bag (insert a (a’:as)) = bag (a’:insert a as) = *a’+ ] bag (insert a as) = = = Programmieren in Haskell *a’+ ] (*a+ ] bag as) *a+ ] (*a’+ ] bag as) *a+ ] bag (a’:as) (Def. insert) (Def. bag) (I.V.) (Ass., Komm. ]) (Def. bag) 26 Φ(x) ⇐⇒ (∀a) ordered x = True =⇒ ordered (insert a x) = True Induktionsbasis (x = []): ordered (insert a []) Programmieren in Haskell = ordered [a] (Def. insert) = True (Def. ordered) 27 Induktionsschritt (x = a’:as): 1. x = a1 :as ∧ a<=a1 = True 2. x = a1 :as ∧ a<=a1 = False ∧ as = [] 3. x = a1 :as ∧ a<=a1 = False ∧ as = a2 :as0 Programmieren in Haskell 28 Φ(x) ⇐⇒ (∀a) ordered x = True =⇒ ordered (insert a x) = True Teilfall a<=a2 = True: ordered (insert a (a1 :as)) Programmieren in Haskell = ordered (a1 :insert a as) (Def. insert) = ordered (a1 :a:as) (Def. insert) = a1 <=a && a<=a2 && ordered as (Def. ordered) = True (Vor.) 29 ⇐⇒ Φ(x) (∀a) ordered x = True =⇒ ordered (insert a x) = True Teilfall a<=a2 = False: ordered (insert a (a1 :as)) = ordered (a1 :insert a as) (Def. insert) = ordered (a1 :a2 :insert a as0 ) (Def. insert) = a1 <=a2 && ordered (a2 :insert a as0 ) (Def. ordered) = ordered (a2 :insert a as0 ) = ordered (insert a as) (Def. insert) = True (Vor. und I.V.) Programmieren in Haskell (Vor. undDef. (&&)) 30 Strukturelle Induktion auf Bäumen Induktionsbasis (Nil): Wir zeigen die Aussage für den leeren Baum Nil. Induktionsbasis (Leaf a): Wir zeigen die Aussage für das Blatt Leaf a. Induktionsschritt (Br l r): Wir nehmen an, daß die Aussage für den Teilbaum l und für den Teilbaum r gilt, und zeigen, daß sie unter dieser Voraussetzung auch für den Baum Br l r gilt. Φ(Nil) (∀a) Φ(Leaf a) (∀l, r) Φ(l) ∧ Φ(r) =⇒ Φ(Br l r) (∀t) Φ(t) Programmieren in Haskell 31 Φ(t) ⇐⇒ Induktionsbasis (t = Nil): size t 6 2^depth t Induktionsbasis (t = Leaf a): size Nil size (Leaf a) = 1 (Def. size) = 1 (Def. size) = 2^0 (Def. (^)) = 2^0 (Def. (^)) = 2^depth Nil (Def. depth) = 2^depth (Leaf a) Programmieren in Haskell (Def. depth) 32 ⇐⇒ Φ(t) size t 6 2^depth t Induktionsschritt (t = Br l r): size (Br l r) Programmieren in Haskell = size l + size r (Def. size) 6 2^depth l + 2^depth r 6 2 * 2^(max (depth l) (depth r)) (Eig. max) = 2^(max (depth l) (depth r) + 1) (Eig. (^)) = 2^depth (Br l r) (I.V.) (Def. depth) 33 Das allgemeine Induktionsschema data T a1 . . . am = C1 t11 . . . t1n1 | . . . |Cr tr1 . . . trnr Seien li1 ,. . . , lipi mit 1 6 li1 < li2 < · · · < lipi 6 ni die Positionen, an denen der Konstruktor Ci rekursiv ist (∀x11 . . . x1n1 ) Φ(x1l11 ) ∧ · · · ∧ Φ(x1l1p1 ) =⇒ Φ(C1 x11 . . . x1n1 ) ... (∀xr1 . . . xrnr ) Φ(xrlr1 ) ∧ · · · ∧ Φ(xrlrpr ) =⇒ Φ(Cr xr1 . . . xrnr ) (∀x) Φ(x) Programmieren in Haskell 34 Spezialfall: Natürliche Induktion data Natural = Zero | Succ Natural Φ(Zero) (∀n) Φ(n) =⇒ Φ(Succ n) (∀n ∈ Nat) Φ(n) Programmieren in Haskell 35