Fahrplan Praktische Informatik 3: Einführung in die Funktionale Programmierung Vorlesung vom 05.01.2011: Signaturen und Eigenschaften I Teil I: Funktionale Programmierung im Kleinen I Teil II: Funktionale Programmierung im Großen I Abstrakte Datentypen I Signaturen und Eigenschaften I Aktionen und Zustände Christoph Lüth & Dennis Walter Universität Bremen Wintersemester 2010/11 I Rev. 1312 Teil III: Funktionale Programmierung im richtigen Leben 2 [26] 1 [26] Abstrakte Datentypen Signaturen I Letzte Vorlesung: Abstrakte Datentypen Definition (Signatur) I Typ plus Operationen Die Signatur eines abstrakten Datentyps besteht aus den Typen, und der Signatur der darüber definierten Funktionen. I I In Haskell: Module Heute: Signaturen und Eigenschaften I Keine direkte Repräsentation in Haskell I Signatur: Typ eines Moduls 3 [26] Zur Erinnerung: Endliche Abbildungen 4 [26] Endliche Abbildung: Signatur I I Endliche Abbildung (FiniteMap) I Typen: die Abbildung S, Adressen a, Werte b I Operationen (Auszug) I leere Abbildung: S I Abbildung an einer Stelle schreiben: S → a → b → S I Abbildung an einer Stelle lesen: S → a * b (partiell) Adressen und Werte sind Parameter type Map α β I Leere Abbildung: empty :: Map α β I An eine Stelle einen Wert schreiben: insert I :: Map α β → α → β → Map α β An einer Stelle einen Wert lesen: lookup :: Map α β → α → Maybe β 5 [26] Signatur und Eigenschaften 6 [26] Beschreibung von Eigenschaften Definition (Axiome) I Axiome sind Prädikate über den Operationen der Signatur Signatur genug, um ADT typkorrekt zu benutzen I I I Elementare Prädikate P : Insbesondere Anwendbarkeit und Reihenfolge Signatur nicht genug, um Bedeutung (Semantik) zu beschreiben: I Was wird gelesen? I Wie verhält sich die Abbildung? I 7 [26] I Gleichheit s ==t I Ordnung s < t I Selbstdefinierte Prädikate Zusammengesetzte Prädikate I Negation not p I Konjunktion p && q I Disjunktion p | | q I Implikation p =⇒ q 8 [26] Beobachtbare und Abstrakte Typen Axiome für Map I I I Beobachtbare Typen: interne Struktur bekannt I l o o k u p empty a == N o t h i n g I Vordefinierte Typen (Zahlen, Zeichen), algebraische Datentypen (Listen) I Viele Eigenschaften und Prädikate bekannt I Lesen an vorher geschriebener Stelle liefert geschriebenen Wert: I Lesen an anderer Stelle liefert alten Wert: l o o k u p ( i n s e r t m a b ) a == J u s t b Abstrakte Typen: interne Struktur unbekannt I Lesen aus leerer Abbildung undefiniert: a1 /= a2 =⇒ l o o k u p ( i n s e r t m a1 b ) a2 == l o o k u p m a2 Wenig Eigenschaft bekannt, Gleichheit nur wenn definiert I Beispiel Map: Schreiben an dieselbe Stelle überschreibt alten Wert: i n s e r t (m a b1 ) a b2 == i n s e r t m a b2 I beobachtbar: Adressen und Werte I abstrakt: Speicher I Schreiben über verschiedene Stellen kommutiert: a1 /= a2 =⇒ i n s e r t ( i n s e r t m a1 b1 ) a2 b2 == i n s e r t ( i n s e r t m a2 b2 ) a1 b1 9 [26] Axiome als Interface I I I empty :: nach außen das Verhalten nach innen die Implementation Signatur + Axiome = Spezifikation Nutzer Spezifikation Implementation St α empty :: Qu α Wert ein/auslesen: Wert ein/auslesen: push top pop enq :: α→ Qu α→ Qu α f i r s t :: Qu α→ α deq :: Qu α→ Qu α :: α→ St α→ St α :: St α→ α :: St α→ St α Test auf Leer: isEmpty I Implementation kann getestet werden I Axiome können (sollten?) bewiesen werden Queues Typ: Qu α Initialwert: Stacks Typ: St α Initialwert: für alle Werte der freien Variablen zu True auswerten Axiome spezifizieren: I I Signatur und Semantik Axiome müssen gelten I 10 [26] :: Test auf Leer: St α→ Bool Last in first out (LIFO). isEmpty :: Qu α→ Bool First in first out (FIFO) Gleiche Signatur, unterscheidliche Semantik. 11 [26] Eigenschaften von Stack 12 [26] Eigenschaften von Queue First in first out (FIFO): Last in first out (LIFO): f i r s t ( enq a empty ) == a t o p ( push a s ) == a n o t ( i s E m p t y q ) =⇒ f i r s t ( enq a q ) == f i r s t q pop ( push a s ) == s deq ( enq a empty ) == empty i s E m p t y empty n o t ( i s E m p t y q ) =⇒ deq ( enq a q ) = enq a ( deq q ) n o t ( i s E m p t y ( push a s ) ) i s E m p t y ( empty ) push a s /= empty n o t ( i s E m p t y ( enq a q ) ) enq a q /= empty 13 [26] Implementation von Stack: Liste Implementation von Queue Sehr einfach: ein Stack ist eine Liste data S t a c k a= S t a c k [ a ] 14 [26] I Mit einer Liste? d e r i v i n g ( Show , Eq ) I empty = Stack Problem: am Ende anfügen oder abnehmen ist teuer. [] I Deshalb zwei Listen: push a ( S t a c k s ) = S t a c k ( a : s ) top ( Stack pop :: []) = e r r o r " S t a c k : t o p on empty s t a c k " Stack a → Stack a 15 [26] I Erste Liste: zu entnehmende Elemente I Zweite Liste: hinzugefügte Elemente rückwärts I Invariante: erste Liste leer gdw. Queue leer 16 [26] Repräsentation von Queue Operation empty enq 9 enq 4 enq 7 deq enq 5 enq 3 deq deq deq deq deq Resultat 9 4 7 5 3 error Implementation Queue I Repräsentation ([], []) ([9], []) ([9], [4]) ([9], [7, 4]) ([4, 7], []) ([4, 7], [5]) ([4, 7], [3, 5]) ([7], [3, 5]) ([5, 3], []) ([3], []) ([], []) ([], []) 9 4 →9 7 →4 →9 7 →4 5 →7 →4 3 →5 →7 →4 3 →5 →7 3 →5 3 Datentyp: data Qu α = Qu [ α ] [ α ] I Leere Schlange: alles leer empty = Qu [ ] I [] Invariante: erste Liste leer gdw. Queue leer i s E m p t y (Qu x s _) = n u l l x s I Erstes Element steht vorne in erster Liste f i r s t (Qu [ ] _) = e r r o r " Queue : f i r s t o f empty Q" f i r s t (Qu ( x : x s ) _) = x 17 [26] Implementation I 18 [26] Axiome als Eigenschaften Bei enq und deq Invariante prüfen I Axiome können getestet oder bewiesen werden I Tests finden Fehler, Beweis zeigt Korrektheit I Arten von Tests: enq x (Qu x s y s ) = c h e c k x s ( x : y s ) deq (Qu [ ] _ ) = e r r o r " Queue : deq o f empty Q" deq (Qu (_ : x s ) y s ) = c h e c k x s y s I I Prüfung der Invariante nach dem Einfügen und Entnehmen I Unit tests (JUnit, HUnit) I Black Box vs.White Box I Zufallsbasiertes Testen check garantiert Invariante c h e c k :: [ α ] → [ α ] → Qu α c h e c k [ ] y s = Qu ( r e v e r s e y s ) c h e c k x s y s = Qu x s y s [] I Funktionale Programme eignen sich sehr gut zum Testen 19 [26] Zufallsbasiertes Testen in Haskell I Werkzeug: QuickCheck I Zufällige Werte einsetzen, Auswertung auf True prüfen I Polymorphe Variablen nicht testbar I Deshalb Typvariablen instantiieren I I Typ muss genug Element haben (hier Int) I Durch Signatur Typinstanz erzwingen 20 [26] Axiome mit QuickCheck testen I Für das Lesen: prop_read_empty :: I n t → Bool prop_read_empty a = l o o k u p ( empty :: Map I n t I n t ) a == N o t h i n g p r o p _ r e a d _ w r i t e :: Map I n t I n t → I n t → I n t → Bool p r o p _ r e a d _ w r i t e s a v= l o o k u p ( i n s e r t s a v ) a == J u s t v Freie Variablen der Eigenschaft werden Parameter der Testfunktion I Hier: Eigenschaften direkt als Haskell-Prädikate I Es werden N Zufallswerte generiert und getestet (N = 100) 22 [26] 21 [26] Axiome mit QuickCheck testen I Axiome mit QuickCheck testen I Bedingte Eigenschaft in quickCheck: I A =⇒B mit A, B Eigenschaften I Typ ist Property I Es werden solange Zufallswerte generiert, bis N die Vorbedingung erfüllende gefunden und getestet wurden, andere werden ignoriert. Schreiben: p r o p _ w r i t e _ w r i t e :: Map I n t I n t → I n t → I n t → I n t → Bool prop_write_write s a v w = i n s e r t ( i n s e r t s a v ) a w == i n s e r t s a w I p r o p _ w r i t e _ o t h e r :: Map I n t I n t → I n t → I n t → I n t → I n t → P r o p e r t y prop_write_other s a v b w = a /= b =⇒ i n s e r t ( i n s e r t s a v ) b w == i n s e r t ( i n s e r t s b w) a v p r o p _ r e a d _ w r i t e _ o t h e r :: Map I n t I n t → I n t → I n t → I n t → P r o p e r t y p r o p _ r e a d _ w r i t e _ o t h e r s a v b= a /= b =⇒ l o o k u p ( i n s e r t s a v ) b == l o o k u p s b I 23 [26] Schreiben an anderer Stelle: Test benötigt Gleichheit auf Map a b 24 [26] Zufallswerte selbst erzeugen I I Zusammenfassung Problem: Zufällige Werte von selbstdefinierten Datentypen I Signatur: Typ und Operationen eines ADT I Gleichverteiltheit nicht immer erwünscht (e.g. [a]) I Axiome: über Typen formulierte Eigenschaften I Konstruktion nicht immer offensichtlich (e.g. Map) I Spezifikation = Signatur + Axiome In QuickCheck: I I I Typklasse class Arbitrary a für Zufallswerte Eigene Instanziierung kann Verteilung und Konstruktion berücksichtigen E.g. Konstruktion einer Map: I I Interface zwischen Implementierung und Nutzung I Testen zur Erhöhung der Konfidenz und zum Fehlerfinden I Beweisen der Korrektheit QuickCheck: I Zufällige Länge, dann aus sovielen zufälligen Werten Map konstruieren I Freie Variablen der Eigenschaften werden Parameter der Testfunktion I Zufallswerte in Haskell? I =⇒ für bedingte Eigenschaften 25 [26] 26 [26]