Inhalt I Praktische Informatik 3: Einführung in die Funktionale Programmierung Vorlesung vom 03.11.2010: Funktionen und Datentypen Auswertungsstrategien I I Definition von Funktionen I Christoph Lüth & Dennis Walter I Universität Bremen Wintersemester 2010/11 I I Aufzählungen I Produkte Basisdatentypen: Wahrheitswerte, numerische Typen, alphanumerische Typen 2 [36] 1 [36] Fahrplan I Syntaktische Feinheiten Definition von Datentypen I Rev. 1167 Striktheit Auswertungsstrategien Teil I: Funktionale Programmierung im Kleinen i n c :: I n t → I n t i n c x = x+ 1 d o u b l e :: I n t → I n t double x = 2∗x I Einführung I Funktionen und Datentypen I Reduktion von inc (double (inc 3)) I Rekursive Datentypen I I Typvariablen und Polymorphie I Funktionen höherer Ordnung I Typinferenz Von außen nach innen (outermost-first): inc (double (inc 3)) double (inc 3)+ 1 2*(inc 3)+ 1 2*(3+ 1)+ 1 2*4+1 9 I Von innen nach außen (innermost-first): inc (double (inc 3)) inc (double (3+1)) inc (2*(3+ 1)) (2*(3+ 1))+ 1 2*4+1 9 I Teil II: Funktionale Programmierung im Großen I Teil III: Funktionale Programmierung im richtigen Leben 4 [36] 3 [36] Auswertungsstrategien und Konfluenz Auswirkung der Auswertungsstrategie I Outermost-first entspricht call-by-need, verzögerte Auswertung. Funktionale Programme sind für jede Auswertungsstrategie konfluent. I Innermost-first entspricht call-by-value, strikte Auswertung Theorem (Normalform) I Beispiel: Theorem (Konfluenz) Terminierende funktionale Programme werten unter jeder Auswertungsstragie jeden Ausdruck zum gleichen Wert aus (der Normalform). I Auswertungsstrategie für nicht-terminierende Programme relevant. I Nicht-Termination nötig (Turing-Mächtigkeit) div :: Int → Int → Int Ganzzahlige Division, undefiniert für div n 0 mult :: I n t → I n t → I n t mult n m = i f n == 0 then 0 e l s e ( mult ( n− 1 ) m)+ m I Auswertung von mult 0 (div 1 0) 5 [36] Striktheit Wie definiere ich eine Funktion? Generelle Form: Definition (Striktheit) Funktion f ist strikt 6 [36] I ⇐⇒ Signatur: Ergebnis ist undefiniert sobald ein Argument undefiniert ist I Semantische Eigenschaft (nicht operational) I Standard ML, Java, C etc. sind strikt (nach Sprachdefinition) I Haskell ist nicht-strikt (nach Sprachdefinition) max :: I Int → Int → Int Definition max x y = i f x < y then y e l s e x I I I Kopf, mit Parametern I Rumpf (evtl. länger, mehrere Zeilen) I Typisches Muster: Fallunterscheidung, dann rekursiver Aufruf I Was gehört zum Rumpf (Geltungsberereich)? Meisten Implementationen nutzen verzögerte Auswertung Fallunterscheidung ist immer nicht-strikt 7 [36] 8 [36] Haskell-Syntax: Charakteristika I I Geltungsbereich der Definition von f: alles, was gegenüber f eingerückt ist. I Beispiel: f x = h i e r f a e n g t s an und h i e r g e h t s w e i t e r immer w e i t e r g y z = und h i e r f a e n g t was n e u e s an Keine Klammern Abseitsregel: Gültigkeitsbereich durch Einrückung I I Wichtigstes Zeichen: Funktionsapplikation: f a I I Funktionsdefinition: f x1 x2 . . . xn = E Leichtgewichtig I I Haskell-Syntax I: Die Abseitsregel Keine Klammern I Gilt auch verschachtelt. I Kommentare sind passiv Auch in anderen Sprachen (Python, Ruby) 9 [36] Haskell-Syntax II: Kommentare I I Haskell-Syntax III: Bedingte Definitionen I Pro Zeile: Ab −− bis Ende der Zeile f x y = irgendwas 10 [36] f x y = i f B1 then P e l s e i f B2 then Q e l s e . . . −− und hier der Kommentar! . . . bedingte Gleichungen: Über mehrere Zeilen: Anfang {-, Ende -} f x y | B1 = . . . | B2 = . . . {− H i e r f ä n g t d e r Kommentar an e r s t r e c k t s i c h über mehrere Z e i l e n bis hier f x y = irgendwas I Statt verschachtelter Fallunterscheidungen . . . −} I Auswertung der Bedingungen von oben nach unten I Wenn keine Bedingung wahr ist: Laufzeitfehler! Deshalb: Kann geschachtelt werden. | otherwise =... 11 [36] Haskell-Syntax IV: Lokale Definitionen I Datentypen als Modellierungskonstrukt Programme manipulieren ein Modell (der Umwelt) Lokale Definitionen mit where oder let: f x y | g = P y | otherwise = Q where y =M f x = N x f x y = let y = M f x = N x in i f g then P y else Q I f, y, . . . werden gleichzeitig definiert (Rekursion!) I Namen f, y und Parameter (x) überlagern andere I Es gilt die Abseitsregel I 12 [36] I Funktionale Sicht: I Imperative Sicht: I Objektorientierte Sicht: Deshalb: Auf gleiche Einrückung der lokalen Definition achten! 13 [36] Datentypen, Funktionen und Beweise I Datentypen konstruieren Werte I Funktionen definieren Berechnungen I Berechnungen haben Eigenschaften I Dualität: 14 [36] Typkonstruktoren Datentypkonstruktor ←→ Definitionskonstrukt ←→ Beweiskonstrukt 15 [36] I Aufzählungen I Produkt I Rekursion I Funktionsraum 16 [36] Aufzählungen Aufzählung und Fallunterscheidung in Haskell I I Aufzählungen: Menge von disjunkten Konstanten data Days = Mon | Tue | Wed | Thu | F r i | S a t | Sun Days = {Mon, Tue, Wed, Thu, Fri, Sat, Sun} I I Mon 6= Tue, Mon 6= Wed, Tue 6= Thu, Wed 6= Sun . . . I I Genau sieben unterschiedliche Konstanten I Funktion mit Wertebereich Days muss sieben Fälle unterscheiden I Beispiel: weekend : Days → Bool mit weekend(d) = Definition Implizite Deklaration der Konstruktoren Mon :: Days als Konstanten Großschreibung der Konstruktoren Fallunterscheidung: weekend :: Days → Bool weekend d = case d o f S a t → True Sun → True Mon → F a l s e Tue → F a l s e Wed → F a l s e Thu → F a l s e Fri → False true d = Sat ∨ d = Sun false d = Mon ∨ d = Tue ∨ d = Wed ∨ d = Thu ∨ d = Fri weekend d = case d o f S a t → True Sun → True _ → False 17 [36] Fallunterscheidung in der Funktionsdefinition I Der einfachste Aufzählungstyp I Abkürzende Schreibweise (syntaktischer Zucker): f c1 == e1 ... f cn == en 18 [36] Einfachste Aufzählung: Wahrheitswerte Bool = {True, False} f x == case x of c1 → e1 , ... cn → en −→ I I I Damit: Definition von Funktionen: I weekend :: Days → Bool weekend S a t = True weekend Sun = True weekend _ = False Genau zwei unterschiedliche Werte Wertetabellen sind explizite Fallunterscheidungen ∧ true false true true false true true false false false false false ∧ ∧ ∧ ∧ true false true false = = = = true false false false 19 [36] Wahrheitswerte: Bool I 20 [36] Beispiel: Ausschließende Disjunktion Vordefiniert als I data Bool = True | F a l s e I I exOr :: Bool → Bool → Bool exOr x y = ( x | | y ) && ( n o t ( x && y ) ) Vordefinierte Funktionen: not && || :: :: :: Bool → Bool Bool → Bool → Bool Bool → Bool → Bool −− Negation −− Konjunktion −− Disjunktion I Konjunktion definiert als I &&, || sind rechts nicht strikt I I 1 ==0 && div 1 0 ==0 Alternative 1: explizite Wertetabelle: exOr exOr exOr exOr a && b = case a o f True → b False → False I Mathematische Definiton: False True False True False False True True = = = = False True True False Alternative 2: Fallunterscheidung auf ersten Argument exOr True y = n o t y exOr F a l s e y = y False if then else als syntaktischer Zucker: I if b then p else q −→ case b of True → p False → q Was ist am besten? I Effizienz, Lesbarkeit, Striktheit 21 [36] Produkte I I I 22 [36] Produkte in Haskell Konstruktoren können Argumente haben Beispiel: Ein Datum besteht aus Tag, Monat, Jahr Mathematisch: Produkt (Tupel) I data Date = Date I n t Month I n t data Month = Jan | Feb | Mar | Apr | May | Jun | J u l | Aug | Sep | Oct | Nov | Dec Date = {Date (n, m, y) | n ∈ N, m ∈ Month, y ∈ N} Month = {Jan, Feb, Mar, . . .} I I Konstruktorargumente sind gebundene Variablen I year (D(n, m, y )) = y Über Fallunterscheidung Zugriff auf Argumente der Konstruktoren: day :: Date → I n t y e a r :: Date → I n t day d = case d o f Date t m y → t y e a r ( Date d m y ) = y day (D(n, m, y )) = n I Beispielwerte: today = Date 5 Nov 2008 b l o o m s d a y = Date 16 Jun 1904 Funktionsdefinition: I Konstruktoren mit Argumenten Bei der Auswertung wird gebundene Variable durch konkretes Argument ersetzt 23 [36] 24 [36] Beispiel: Tag im Jahr I Fallunterscheidung und Produkte Tag im Jahr: Tag im laufenden Monat plus Summe der Anzahl der Tage der vorherigen Monate I y e a r D a y :: Date → I n t y e a r D a y ( Date d m y ) = d + sumPrevMonths m where sumPrevMonths :: Month → I n t sumPrevMonths Jan = 0 sumPrevMonths m = day sI nM onth ( p r e v m) y + sumPrevMonths ( p r e v m) I Dreieck, gegeben durch Kantenlänge I Kreis, gegeben durch Radius I Rechteck, gegeben durch zwei Kantenlängen O = {Tri(a) | a ∈ R} ∪ {Circle(r ) | r ∈ R} ∪ {Rect(a, b) | a, b ∈ R} data Obj = T r i Double | C i r c l e Double | R e c t Double Double Tage im Monat benötigt Jahr als Argument (Schaltjahr!) I da ysI nMonth :: Month → I n t → I n t p r e v :: Month → Month I Beispiel: geometrische Objekte I Berechung des Umfangs: c i r c :: Obj → Double c i r c ( T r i a ) = 3∗ a c i r c ( C i r c l e r )= 2∗ p i ∗ r c i r c ( R e c t a b )= 2∗ ( a+ b ) Schaltjahr: Gregorianischer Kalender l e a p y e a r :: I n t → Bool l e a p y e a r y = i f mod y 100 == 0 then mod y 400 == 0 e l s e mod y 4 == 0 26 [36] 25 [36] Der Allgemeine Fall: Algebraische Datentypen Beweis von Eigenschaften Definition eines algebraischen Datentypen T: data T = C1 t1,1 . . . t1,k1 ... | Cn tn,1 . . . tn,kn I I Konstruktoren C1 , . . . , Cn sind disjunkt: Ci x1 . . . xn = Cj y1 . . . ym −→ i = j I Konstruktoren sind injektiv: C x1 . . . xn = C y1 . . . yn −→ xi = yi I Konstruktoren erzeugen den Datentyp: ∀x ∈ T . x = Ci y1 . . . ym Eigenschaften von Programmen: Prädikate I Haskell-Ausdrücke vom Typ Bool I Allquantifizierte Aussagen: wenn P(x ) Prädikat, dann ist ∀x .P(x ) auch ein Prädikat I Sonderfall Gleichungen s == t I Müssen nicht ausführbar sein Diese Eigenschaften machen Fallunterscheidung möglich. 27 [36] Wie beweisen? 28 [36] Ein ganz einfaches Beispiel I Gleichungsumformung (equational reasoning) I Fallunterscheidungen I Induktion I Wichtig: formale Notation addTwice :: I n t → I n t → I n t addTwice x y = 2 ∗ ( x+ y ) Lemma: addTwice x (y + z) = addTwice (x + y ) z addTwice x (y + z) = 2 ∗ (x + (y + z)) — Def. addTwice = 2 ∗ ((x + y ) + z) — Assoziativität von + = addTwice (x + y ) z — Def. addTwice 30 [36] 29 [36] Fallunterscheidung Das Rechnen mit Zahlen max , min :: I n t → I n t → I n t max x y = i f x < y then y e l s e x min x y = i f x < y then x e l s e y Lemma: max x y − min x max x y − min x • Fall: x < y = y − min x y = y −x = |x − y | • Fall: x ≥ y = x − min x y = x −y = |x − y | = |x − y | Beschränkte Genauigkeit, beliebige Genauigkeit, ←→ konstanter Aufwand wachsender Aufwand y = |x − y | y Haskell bietet die Auswahl: — Def. max — Def. min — Wenn x < y , dann y − x = |x − y | I Int - ganze Zahlen als Maschinenworte (≥ 31 Bit) I Integer - beliebig große ganze Zahlen — Def. max — Def. min — Wenn x ≥ y , dann x − y = |x − y | I Rational - beliebig genaue rationale Zahlen I Float, Double - Fließkommazahlen (reelle Zahlen) 31 [36] 32 [36] Ganze Zahlen: Int und Integer I Fließkommazahlen: Double I Nützliche Funktionen (überladen, auch für Integer): +, ∗ , ^ , − abs div , quot mod , rem :: :: :: :: Int → Int → Int → Int → Doppeltgenaue Fließkommazahlen (IEEE 754 und 854) I Int → Int I n t −− Betrag Int → Int Int → Int I Logarithmen, Wurzel, Exponentation, π und e, trigonometrische Funktionen Konversion in ganze Zahlen: I fromIntegral :: Int, Integer-> Double I fromInteger :: Integer-> Double Es gilt (div x y)*y + mod x y == x I Vergleich durch ==, /=, <=, <, . . . I round, truncate :: Double-> Int, Integer I Achtung: Unäres Minus I Überladungen mit Typannotation auflösen: I Unterschied zum Infix-Operator - I Im Zweifelsfall klammern: abs (-34) round ( f r o m I n t 10) I :: Int Rundungsfehler! 33 [36] Alphanumerische Basisdatentypen: Char 34 [36] Zusammenfassung I I I I Nützliche Funktionen: ord chr :: :: Striktheit I Notation für einzelne Zeichen: ’a’,. . . I I Char → I n t I n t → Char I :: :: :: :: Char → Char → Char → Char → Char Char Bool Bool I I I I Beweis durch Gleichungsumformung Programmeigenschaften als Prädikate Fallunterscheidung als Beweiskonstrukt Wahrheitswerte Bool Numerische Basisdatentypen: I I Aufzählungen — Fallunterscheidung Produkte Funktionsdefinition und Beweis dual I toLower toUpper isDigit isAlpha Haskell ist spezifiziert als nicht-strikt Datentypen und Funktionsdefinition dual Int, Integer, Rational und Double Zeichenketten: String 35 [36] I Alphanumerische Basisdatentypen: Char I Nächste Vorlesung: Rekursive Datentypen 36 [36]