Grundprinzipien der funktionalen Programmierung Funktionen haben keine Seiteneffekte Eine Funktion berechnet einen Ausgabewert der nur von den Eingabewerten abhängt: 12 34 inputs + output 46 2 Nicht nur Zahlen, auch Bilder A function to flip a picture in a vertical mirror: input output flipV 3 Mehr Funktionen auf Bildern A function to invert the colours in a picture: invertColour 4 Mehr Funktionen auf Bildern A function to superimpose two pictures: superimpose 5 Mehr Funktionen auf Bildern A function to put one picture above another: above 6 Mehr Funktionen auf Bildern A function to put two pictures side by side: sideBySide 7 Typen Ein Typ ist eine Menge von Werten, wie Zahlen oder Bilder. Auf alle Werte eines Typs sind die gleichen Funktionen anwendbar: Wir können zwei Zahlen addieren, nicht aber eine Zahl und ein Bild oder zwei Bilder. 8 Alles hat einen Typ Werte haben Typen: true : bool 1 : int Funktionen haben Typen: + : int -> int -> int above: picture -> picture -> picture 9 Klammerung von Typen Implizite Klammerung: T1 -> T2 -> T = T1 -> (T2 -> T) + : int -> int -> int + : (int -> int) -> int integrate: (real -> real) -> real -> real -> real integrate: real -> real -> real -> real -> real 10 Auswertung von Ausdrücken durch Termersetzung (7-3)+ 2 Ausdruck (7-3)+ 2 4+2 Wert Auswertung 4+2 6 11 sideBySide (invertColour ) sideBySide Auswertung mit Bildern 12 Auswertungsstrategien f x = 1 g x = g x Eager evaluation / Call-by-value / Eifrige Auswertung: f(g 1) => f(g 1) => ... Prinzip: Zuerst die Parameter, dann die Funktion Lazy evaluation / Call-by-need / Verzögerte Auswertung: f(g 1) => 1 Prinzip: Parameter erst, wenn sie gebraucht werden 13 Auswertungsstrategien Wenn eifrige Auswertung terminiert, dann auch verzögerte Auswertung, und beide berechnen den gleichen Wert. SML: Eager evaluation Haskell: Lazy evaluation 14 Ausdrücke (Terme) Ein Ausdruck ist entweder atomar (z.B. x, true, 42) oder zusammengesetzt: f a1 ... an wobei f, a1,..., an Ausdrücke passenden Typs sind: f : T1 -> ... -> Tn -> T ai : Ti 15 Klammerung von Ausdrücken Implizite Klammerung: f a b = (f a) b Funktionsanwendung ist nach links geklammert sin cos pi = (sin cos) pi sin(cos pi) integrate sin pi = (integrate sin) pi integrate (sin pi) 16 Präfix und Infix Infix: 3+4 ist Abkürzung für + 3 4 Präfix bindet stärker als Infix: f x+1 = (f x) + 1 17 Funktionale Programmierung in SML The Web page ... SML distributions and documentation. Background material: books, FAQs, etc ... 19 Wertedefinitionen in SML Allgemeines Format: val name = expression Beispiele: val null = 0+0; val Horse = above horse horse; 20 Funktionsdefinitionen in SML Einfaches Format: fun name x1 ... xn = expression Beispiel: fun sum n = if n=0 then 0 else n+sum(n-1); 21 Funktionsdefinitionen in SML Mit Pattern Matching: fun name pat ... pat = expression | name pat ... pat = expression ... Beispiel: fun sum 0 = 0 | sum n = n + sum(n-1) Reihenfolge der Gleichungen wichtig! 22 Lokale Definitionen: let let val x = 42 in x+x end => 42+42 => 84 let fun f x = x+1 in f(f 5) end ... => 7 23 Typen in SML Typinferenz SML berechnet automatisch den Typ jedes Ausdrucks val x = 42; > ... : int fun f x = x+1; > ... : int -> int 25 Polymorphismus Der Typ einer Funktion kann Typvariablen enthalten: ’a, ’b, ’name Ein solcher Typ heisst polymorph. fun f x = x; > f > f > ... : ’a -> ’a 5; 5 : int true; true : bool 26 Polymorphismus fun S x y z = (x z) (y z); Was ist der Typ der Funktion? Typinferenz berechnet den allgemeinsten Typ. Polymorphismus ist ein Abstraktionsprinzip. 27 Basistypen •bool •int, real •char, string 28 Booleans Everything is true or false 29 Vordefinierte Boolesche Operatoren if x:bool then y:T else z:T a andalso b a orelse b not a = if a then b else false = if a then true else b = if a then false else true 30 Beispiel Pattern Matching fun | | | xor xor xor xor true true = false false true = true true false = true false false = false; 31 Arithmetik: int und real 42 : int 42.0 : real Funktionen: ~ + - * / Vergleiche: < <= > >= div mod ... Infix syntax: 3 + 4 Präfix syntax: op + (3,4) op + : int * int -> int 32 Überladung Die meisten vordefinierten Operatoren (+, ..., <, ...) sind überladen, dh sowohl für int als auch real definiert. Benutzerdefinierte Funktionen dürfen nicht überladen sein: fun doppel x = x+x; > Fehlermeldung wg Überladung fun doppeli(x:int) = x+x; fun doppelr(x:real) = x+x; 33 Primitive Rekursion auf int Ein Schema oder Schablone: fun f 0 = .... | f n = .... f(n-1) .... Beispiel: 2er Potenzen. fun power2 0 = 1 | power2 n = 2 * power2 (n-1); 34 Wie viele Teile nach n Schnitten? 35 Wie viele Teile nach n Schnitten? 0 Schnitte: 1 Teil. Mit dem n-ten Schnitt erhält man n weitere Teile: fun pieces 0 = 1 | pieces n = pieces(n-1) + n; 36 Nicht alles ist primitiv rekursive fun ggt 0 n = n | ggt m n = ggt (n mod m) m; 37 Characters Letters, digits and special characters. Literal characters are: #"a" … Special characters are: #"\n" newline #"\t" #"\\" #"\" tab backslash double quote 38 Characters und strings "This is a string" : string Standardfunktionen: explode: string -> char list implode: char list -> string chr :: int -> char ord :: char -> int 39 Paare und Tupel (1,2) : int * int (1,2,true) : int * int * bool (1,(2,true)) : int * (int * bool) ((1,2),true) : (int * int) * bool fun fun fun fun fst(x,y) = x; snd(x,y) = y; swap(x,y) = (y,x); pair x = (x,x); 40 Listen T list: Listen von Element des Typs T Leere Liste: [] “Cons”: x :: xs Endliche Liste: [1,2,3] = 1::2::3::[] = 1::(2::(3::[])) 41 Zusammenfassung: Typen Τ ::= bool | int | ... |T*T | T list | (T,...,T)Typkonstruktor | T -> T | (T) | Typvariable 42 Gleichheit op = : ’’a * ’’a -> bool ’’a ist eine Typvariable über Gleichheitstypen. Gleichheitstypen sind (in erster Näherung) alle Typen ohne ->. 1 = 1 : bool not = not : Typfehler 43 Funktionen Partielle Anwendung Sei f: T1 -> T2 -> T und a1:T1. Dann ist f a1 : T2 -> T eine partielle Anwendung. Bsp: Gegeben parser : grammar -> string -> bool Cgram : grammar kann man definieren val Cparser = parser Cgram > Cparser : string -> bool 45 Currying (nach Haskell B. Curry) fun f x y = ... > : T1 -> T2 -> T fun f(x,y) = ... > : T1 * T2 -> T f ist “curried” f ist “gepaart” 46 Funktionen höherer Stufe = Funktionen als Parameter integrate: (real -> real) -> ... sort: (’a -> ’a -> bool) -> ... . . . 47 Funktionen als Werte Lambda Kalkül: λx.e SML: fn x => e : T -> U falls x:T und e:U x ist formaler Parameter e ist Funktionskörper, d.h. Resultat Bsp: fn x => x+1 : int -> int (fn x => x+1) 41 => 42 48 fun, fn und val Parameter kann man von links nach rechts verschieben: fun f x y = e ist äquivalent zu fun f x = fn y => e fun ist val rec: fun g x = e ist äquivalent zu val rec g = fn x => e 49 Programmierung, Spezifikation und Verifikation mit Listen Primitive Rekursion auf list Ein Schema oder Schablone: fun f [] = .... | f (x::xs) = .... f xs .... Beispiel: fun length [] = 1 | length (x::xs) = length xs + 1; 51 Wichtige Funktionen auf Listen fun [] @ ys = ys | (x::xs) @ ys = x :: (xs @ ys); fun concat [] = [] | concat(xs::xss) = xs @ concat xss; Informell: concat [xs1,...,xsn] = xs1 @ ... @ xsn fun rev [] = [] | rev (x::xs) = rev xs @ [x]; 52 map und filter fun map f [] = [] | map f (x::xs) = f x :: map f xs Informell: map f [x1,...,xn] = [f x1,...,f xn] fun filter p [] = [] | filter p (x::xs) = if p x then x :: filter p xs else filter p xs 53 Taking and ... fun take n | take n if n else [] = [] (x:xs) = <= 0 then [] x :: take (n-1) xs; fun takeWhile p [] = [] | takeWhile p (x:xs) = if p x then x :: takeWhile p xs else []; 54 ... dropping Analog: drop : int -> ’a list -> ’a list dropWhile : (’a -> bool) -> ’a list -> ’a list 55 Zipping fun zipWith f (x::xs) (y::ys) = f x y :: zipWith f xs ys | zipWith _ _ _ = []; Anwendung: zipWith ??? [x1,...] [y1,...] = [(x1,y1),...] zipWith ??? [x1,...] [y1,...] = [x1+y1,...] 56