Praktische Informatik 3: Einführung in die Funktionale Programmierung
Vorlesung vom 05.01.2011: Signaturen und Eigenschaften
Christoph Lüth & Dennis Walter
Universität Bremen
Wintersemester 2010/11
Fahrplan
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
I Teil III: Funktionale Programmierung im richtigen Leben
Abstrakte Datentypen
I Letzte Vorlesung:Abstrakte Datentypen
I TypplusOperationen
I In Haskell:Module
I Heute:Signaturen undEigenschaften
Signaturen
Definition (Signatur)
Die Signatur eines abstrakten Datentyps besteht aus den Typen, und der Signatur der darüber definierten Funktionen.
I Keine direkte Repräsentation in Haskell
I Signatur:Typeines Moduls
Zur Erinnerung: Endliche Abbildungen
I Endliche Abbildung(FiniteMap)
I Typen: die AbbildungS, Adressena, Werte b
I Operationen (Auszug)
I leereAbbildung: S
I Abbildung an einer Stelleschreiben:S→a→b→S
I Abbildung an einer Stellelesen:S →a*b (partiell)
Zur Erinnerung: Endliche Abbildungen
I Endliche Abbildung(FiniteMap)
I Typen: die AbbildungS, Adressena, Werte b
I Operationen (Auszug)
I leereAbbildung: S
I Abbildung an einer Stelleschreiben: S→a→b→S
I Abbildung an einer Stellelesen:S →a*b (partiell)
Zur Erinnerung: Endliche Abbildungen
I Endliche Abbildung(FiniteMap)
I Typen: die AbbildungS, Adressena, Werte b
I Operationen (Auszug)
I leereAbbildung: S
I Abbildung an einer Stelleschreiben: S→a→b→S
I Abbildung an einer Stellelesen:S →a*b (partiell)
Zur Erinnerung: Endliche Abbildungen
I Endliche Abbildung(FiniteMap)
I Typen: die AbbildungS, Adressena, Werte b
I Operationen (Auszug)
I leereAbbildung: S
I Abbildung an einer Stelleschreiben: S→a→b→S
I Abbildung an einer Stellelesen:S →a*b (partiell)
Endliche Abbildung: Signatur
I Adressenund Wertesind Parameter type Map α β
I Leere Abbildung:
empty :: Map α β
I An eine Stelle einen Wertschreiben:
i n s e r t :: Map α β→ α → β→ Map α β
I An einer Stelle einen Wertlesen:
l o o k u p :: Map α β→ α → Maybe β
Signatur und Eigenschaften
I Signatur genug, um ADTtypkorrekt zu benutzen
I InsbesondereAnwendbarkeitundReihenfolge
I Signatur nicht genug, umBedeutung (Semantik) zu beschreiben:
I Waswirdgelesen?
I Wieverhältsich die Abbildung?
Beschreibung von Eigenschaften
Definition (Axiome)
Axiome sindPrädikate über denOperationender Signatur
I ElementarePrädikate P :
I Gleichheit s ==t
I Ordnung s <t
I Selbstdefinierte Prädikate
I ZusammengesetztePrädikate
I Negationnot p
I Konjunktionp && q
I Disjunktionp | | q
I Implikationp =⇒q
Beobachtbare und Abstrakte Typen
I BeobachtbareTypen: interne Struktur bekannt
I Vordefinierte Typen (Zahlen,Zeichen), algebraische Datentypen (Listen)
I Viele Eigenschaften und Prädikate bekannt
I AbstrakteTypen: interne Struktur unbekannt
I Wenig Eigenschaft bekannt, Gleichheit nur wenn definiert
I BeispielMap:
I beobachtbar: Adressen und Werte
I abstrakt: Speicher
Axiome für Map
I Lesenaus leerer Abbildung undefiniert:
l o o k u p empty a == N o t h i n g
I Lesenan vorher geschriebener Stelle liefert geschriebenen Wert: l o o k u p ( i n s e r t m a b ) a == J u s t b
I Lesenan anderer Stelle liefert alten Wert:
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
I Schreibenan 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 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
Axiome für Map
I Lesenaus leerer Abbildung undefiniert:
l o o k u p empty a == N o t h i n g
I Lesenan vorher geschriebener Stelle liefert geschriebenen Wert:
l o o k u p ( i n s e r t m a b ) a == J u s t b
I Lesenan anderer Stelle liefert alten Wert:
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
I Schreibenan 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 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
Axiome für Map
I Lesenaus leerer Abbildung undefiniert:
l o o k u p empty a == N o t h i n g
I Lesenan vorher geschriebener Stelle liefert geschriebenen Wert:
l o o k u p ( i n s e r t m a b ) a == J u s t b
I Lesenan anderer Stelle liefert alten Wert:
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
I Schreibenan 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 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
Axiome für Map
I Lesenaus leerer Abbildung undefiniert:
l o o k u p empty a == N o t h i n g
I Lesenan vorher geschriebener Stelle liefert geschriebenen Wert:
l o o k u p ( i n s e r t m a b ) a == J u s t b
I Lesenan anderer Stelle liefert alten Wert:
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
I Schreibenan 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 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
Axiome für Map
I Lesenaus leerer Abbildung undefiniert:
l o o k u p empty a == N o t h i n g
I Lesenan vorher geschriebener Stelle liefert geschriebenen Wert:
l o o k u p ( i n s e r t m a b ) a == J u s t b
I Lesenan anderer Stelle liefert alten Wert:
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
I Schreibenan 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 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
Axiome für Map
I Lesenaus leerer Abbildung undefiniert:
l o o k u p empty a == N o t h i n g
I Lesenan vorher geschriebener Stelle liefert geschriebenen Wert:
l o o k u p ( i n s e r t m a b ) a == J u s t b
I Lesenan anderer Stelle liefert alten Wert:
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
I Schreibenan 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 Schreibenüber verschiedene Stellen kommutiert:
a1 /= a2 =⇒ i n s e r t ( i n s e r t m a1 b1 ) a2 b2 ==
Axiome als Interface
I Axiome müssengelten
I füralleWerte der freien Variablen zuTrue auswerten
I Axiomespezifizieren:
I nach außen dasVerhalten
I nach innen dieImplementation
I Signatur+ Axiome=Spezifikation
Nutzer Spezifikation Implementation
I Implementation kanngetestet werden
I Axiome können (sollten?)bewiesenwerden
Signatur und Semantik
Stacks Typ: St α Initialwert:
empty :: S t α Wert ein/auslesen:
p u s h :: α→ S t α→ S t α t o p :: S t α→ α
pop :: S t α→ S t α Test auf Leer:
i s E m p t y :: S t α→ B o o l Last in first out (LIFO).
Queues Typ: Quα Initialwert:
empty :: Qu α Wert ein/auslesen:
enq :: α→ Qu α→ Qu α f i r s t :: Qu α→ α deq :: Qu α→ Qu α Test auf Leer:
i s E m p t y :: Qu α→ B o o l First in first out (FIFO)
Eigenschaften von Stack
Last in first out (LIFO):
t o p ( p u s h a s ) == a pop ( p u s h a s ) == s i s E m p t y empty
n o t ( i s E m p t y ( p u s h a s ) ) p u s h a s /= empty
Eigenschaften von Queue
First in first out (FIFO):
f i r s t ( enq a empty ) == 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 deq ( enq a empty ) == empty
n o t ( i s E m p t y q ) =⇒ deq ( enq a q ) = enq a ( deq q ) i s E m p t y ( empty )
n o t ( i s E m p t y ( enq a q ) ) enq a q /= empty
Implementation von Stack: Liste
Sehr einfach: ein Stack ist eine Liste
data S t a c k a= S t a c k [ a ] d e r i v i n g ( Show , Eq ) empty = S t a c k [ ]
p u s h a ( S t a c k s ) = S t a c k ( a : s )
t o p ( S t a c k [ ] ) = e r r o r " S t a c k : t o p on empty s t a c k "
pop :: S t a c k a→ S t a c k a
Implementation von Queue
I Mit einerListe?
I Problem: am Ende anfügen oder abnehmen ist teuer.
I DeshalbzweiListen:
I Erste Liste: zuentnehmende Elemente
I Zweite Liste:hinzugefügteElementerückwärts
I Invariante: erste Liste leer gdw. Queue leer
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Repräsentation von Queue
Operation Resultat Queue Repräsentation
empty ([], [])
enq 9 9 ([9], [])
enq 4 4 →9 ([9], [4])
enq 7 7 →4 →9 ([9], [7, 4])
deq 9 7 →4 ([4, 7], [])
enq 5 5 →7 →4 ([4, 7], [5])
enq 3 3 →5 →7 →4 ([4, 7], [3, 5])
deq 4 3 →5 →7 ([7], [3, 5])
deq 7 3 →5 ([5, 3], [])
deq 5 3 ([3], [])
deq 3 ([], [])
deq error ([], [])
Implementation
I 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
Implementation
I Beienq und deqInvariante prüfen
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 Prüfung der Invariantenachdem Einfügen und Entnehmen
I checkgarantiert 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
Axiome als Eigenschaften
I Axiome könnengetestetoderbewiesenwerden
I Tests findenFehler, Beweis zeigtKorrektheit
I Artenvon Tests:
I Unit tests(JUnit, HUnit)
I Black Boxvs.White Box
I ZufallsbasiertesTesten
I Funktionale Programme eignen sichsehr gutzum Testen
Zufallsbasiertes Testen in Haskell
I Werkzeug:QuickCheck
I Zufällige Werteeinsetzen, Auswertung auf Trueprüfen
I Polymorphe Variablen nichttestbar
I Deshalb Typvariableninstantiieren
I Typ muss genug Element haben (hierInt)
I Durch SignaturTypinstanz erzwingen
I Freie Variablender Eigenschaft werdenParameter der Testfunktion
Axiome mit QuickCheck testen
I Für das Lesen:
p r o p _ r e a d _ e m p t y :: I n t→ B o o l p r o p _ r e a d _ e m p t y 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→ B o o l 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
I Hier: Eigenschaften direkt alsHaskell-Prädikate
I Es werdenN Zufallswerte generiert und getestet (N=100)
Axiome mit QuickCheck testen
I BedingteEigenschaft in quickCheck:
I A=⇒BmitA,BEigenschaften
I Typ istProperty
I Es werden solange Zufallswerte generiert, bisN die Vorbedingung erfüllende gefunden und getestet wurden, andere werden ignoriert.
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
Axiome mit QuickCheck testen
I 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→ B o o l p r o p _ w r i t e _ w r i t e 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 Schreibenan anderer Stelle:
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 p r o p _ w r i t e _ o t h e r 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
I Test benötigtGleichheit auf Map a b
Zufallswerte selbst erzeugen
I Problem:ZufälligeWerte vonselbstdefinierten Datentypen
I Gleichverteiltheitnicht immer erwünscht (e.g. [a])
I Konstruktionnicht immer offensichtlich (e.g. Map)
I InQuickCheck:
I Typklasseclass Arbitrary afür Zufallswerte
I EigeneInstanziierungkann Verteilung und Konstruktion berücksichtigen
I E.g.KonstruktioneinerMap:
I Zufällige Länge, dann aus sovielen zufälligen WertenMapkonstruieren
I Zufallswerte in Haskell?
Zusammenfassung
I Signatur: Typ und Operationen eines ADT
I Axiome: über Typen formulierte Eigenschaften
I Spezifikation= Signatur + Axiome
I Interfacezwischen Implementierung und Nutzung
I Testenzur Erhöhung der Konfidenz und zum Fehlerfinden
I Beweisender Korrektheit
I QuickCheck:
I Freie Variablen der Eigenschaften werdenParameterder Testfunktion
I =⇒fürbedingteEigenschaften