Funktionale Programmierung ALP I λ−Kalkül Teil 2 SS 2011 Prof. Dr. Margarita Esponda Prof. Dr. Margarita Esponda Funktionale Programmierung Lokale Variablennamen Es gibt in Haskell eine zweite Möglichkeit, lokale Variablen zu definieren mit Hilfe des let-in-Ausdrucks. Allgemeine Form: let Lokale Definitionen in Beispiel: f x y = let Ausdruck a = x+y*2 b = x-y/2 in (a+b*b) f x y = Prof. Dr. Margarita Esponda x + y*2 + (x-y/2)*(x-y/2) Funktionale Programmierung Lokale Variablennamen Beispiele: u = let a = 2 b = a + ( let a = 3 b=c in a+b ) c = a^2 in b*b u n = let a = 2*n b = a + ( let a = 3 b=c in a+b ) c = a^2 in b*b Prof. Dr. Margarita Esponda Funktionale Programmierung Lokale Variablennamen Haskell: Lambda: let x = exp1 in exp2 λ exp1 . exp2 Einfache Regel: Der Geltungsbereich eines Lambda-Ausdrucks erstreckt sich soweit wie möglich nach rechts. λm.λa.λb.aa(bm) => (λm.(λa.(λb.aa(bm)))) Lambda-Ausdrücke sind rechtsassoziativ Prof. Dr. Margarita Esponda Funktionale Programmierung β-Reduktion Die Auswertung bzw. Funktionsapplikation von LambdaAusdrücken wird auch ( β-Reduktion genannt. λx.E1) (E2) → β Redex E1 [E2\x] (Reduzierbarer Ausdruck) Funktionsapplikation ist linksassoziativ Prof. Dr. Margarita Esponda Funktionale Programmierung α-Konversion ( λx.E1) (E2) → α E1 [E2\x] Was passiert, wenn x in E2 vorkommt? Haskell: u = let a = 2 b = a + ( let a = 3 in a+b ) in b*b u = let a = 2 b = a + ( let x = 3 äquivalent zu in x+b ) in b*b Prof. Dr. Margarita Esponda Funktionale Programmierung Bindungsbereich von Variablennamen Eine Lambda-Abstraktion λx .E bindet alle Vorkommen von x innerhalb des Ausdrucks E. Mit anderen Worten: E ist der Geltungsbereich des Variablennamen x. Beispiel: λz . λx . Bindungsbereich von x x (λy . y x ) x z y Bindungsbereich von z Prof. Dr. Margarita Esponda y ist nur hier gebunden Funktionale Programmierung Freie und Gebundene Variablennamen Definition eines Hilfsoperators, der die Elemente von zwei Listen ohne Verdopplungen konkateniert. Definition als Operator (+++) :: [String] -> [String] -> [String] (+++) [] ys = ys (+++) (x:xs) ys | not (elementOf x ys) = x : (+++) xs ys | otherwise Prof. Dr. Margarita Esponda = (+++) xs ys Funktionale Programmierung Freie und Gebundene Variablennamen Wir können die Menge FV der ungebundenen Variablen mit Hilfe der folgenden Funktion berechnen: free :: Expr -> [String] free (Var x) = [x] free ( App e1 e2) = (free e1) +++ (free e2) free (Lambda x e) = remove x (free e) Prof. Dr. Margarita Esponda Funktionale Programmierung α-Konversion Ein Lambda-Ausdruck E wird als geschlossen bezeichnet, wenn keine freie Variablen in E beinhaltet sind. D.h. FV(E) = λx.E = ∅ In E wird x mit y ersetzt λy.(E[y\x]) wenn y ∉ FV(E) Wir können Variablen immer umbenennen, wenn die neuen Namen nicht als freie Variablennamen innerhalb des Lambda-Ausdrucks vorkommen. Beispiel: ( Prof. Dr. Margarita Esponda λx. y x) x → α ( λa. y a) x → β yx Funktionale Programmierung α-Konversion Formale Definition der Substitution von Variablennamen: x [e\x] = e y [e\x] = y wenn y ≠ x (e1 e2) [e\x] = (e1[e\x] e2[e\x]) (λx.e1)[e\x] = (λx.e1) (λy.e1)[e\x] = (λy. e1[e\x]) wenn y≠x und y∉FV(e) Prof. Dr. Margarita Esponda Funktionale Programmierung Vorgänger-Funktion Um die Vorgänger-Funktion zu berechnen, erzeugen wir zuerst Zahlenpaare der Form (n, n-1), und dann wählen wir das zweite Element des Tupels: (λt. t x y) stellt das Tupel (x,y) dar Das erste Element des Tupels ist: (λt. t x y) T => T x y => x Das zweite Element des Tupels ist: (λt. t x y) F => F x y => y Prof. Dr. Margarita Esponda Funktionale Programmierung Vorgänger-Funktion Die Funktion H erzeugt bei Eingabe des Tupels (n, n-1) das Tupel (n+1, n) H ≡ (λpz.z ( S(pT) )( pT )) Wenn p = (n, n-1), dann ist pT gleich dem ersten Element des Tupels. Prof. Dr. Margarita Esponda Funktionale Programmierung Vorgänger-Funktion H angewendet an p = (2,1) H (λt. t 2 1) => (λpz.z( S(pT) )( pT )) => (λz.z( S((λt. t 2 1) T) )((λt. t 2 1) T )) => (λz.z( S(T 2 1) )(T 2 1 )) => (λz.z ( S(2) )(2) ) => (λz.z (3) (2) ) Prof. Dr. Margarita Esponda Funktionale Programmierung Vorgänger-Funktion Der Vorgänger der Zahl n wird dann berechnet, indem die Funktion H n-mal auf das Tupel (λt.t00) angewendet wird, und dann nur das zweite Element des Ergebnis-Tupels gewählt wird. P ≡ (λn.nH(λz.z00)F) Die Berechnung des Vorgängers von 0 ergibt 0. Prof. Dr. Margarita Esponda Funktionale Programmierung Vorgänger-Funktion Beispiel: Vorgänger von 1 P 1 => (λn.nH(λz.z00)F) 1 => (1H(λz.z00)F) => (λz.z10) F => (F 10) => 0 Prof. Dr. Margarita Esponda Funktionale Programmierung Vergleich mit 0 Folgende Funktion ist wahr, wenn eine Zahl gleich Null ist, sonst ist sie falsch: Z Beispiel: Z0 ≡ λ x . x F ¬F ≡ ( λ x . x F ¬F ) 0 ⇒ 0 F ¬F ⇒ ¬F Prof. Dr. Margarita Esponda ⇒ T Funktionale Programmierung Vergleichsfunktionen Wenn die Vorgänger-Funktion x-mal angewendet auf y zu Null führt, dann ist x >= y (>=) G Vorgänger-Funktion ≡ (λxy.Z(xPy)) Test ob gleich Null Prof. Dr. Margarita Esponda Funktionale Programmierung Gleichheit Die Gleichheit-Funktion kann dann definiert werden, indem getestet wird, dass x>=y und y>=x ist. E => ( λxy. ∧ (Z(xPy)) (Z(yPx)) ) x>=y Prof. Dr. Margarita Esponda y>=x Funktionale Programmierung Gleichheit und Ungleichheit Beispiel: E 1 0 => (λxy. ∧ (Z(xPy))(Z(yPx))) 1 0 => ∧ (Z(1P0)) (Z(0P1))) ∧ (Z(0)) (Z(1))) => ∧ (T) (F) => => (F) Prof. Dr. Margarita Esponda Funktionale Programmierung Normal-Form Ein Lambda-Ausdruck befindet sich in normaler Form, wenn nicht weiter reduziert werden kann. Beispiele: λx . x λx . λz . y Nicht alle Lambda-Ausdrücke haben eine Normal-Form Beispiele: Wenn A = λ x . x x dann A A => (λx.x x) (λx .x x) => (λx.x x) (λx .x x) Prof. Dr. Margarita Esponda Funktionale Programmierung Rekursive Funktionen Rekursive Funktionen werden mit Hilfe der Funktion Y definiert, die die Eigenschaft hat, sich selber zu reproduzieren. Y ≡ (λf.(λx.f(xx))(λx.f(xx))) Angewendet auf eine Funktion R, ergibt das: ≡ (λf.(λx.f(xx))(λx.f(xx))) R YR => (λx.R(xx)) (λx.R(xx)) => R( (λx.R(xx)) (λx.R(xx)) ) Prof. Dr. Margarita Esponda das bedeutet: YR = R(YR) Funktionale Programmierung Rekursive Funktionen Beispiel: Wir möchten die Summe der ersten n natürlichen Zahlen rekursiv berechnen. Haskell: sum 0 = 0 sum n = n + sum (n-1) Lambda-Kalkül: R Die Nachfolger-Funktion wird n-mal angewendet ≡ (λ r n . Z n 0 ( n S ( r ( P n )))) Test n gegen 0 Prof. Dr. Margarita Esponda rekursiver Aufruf des Vorgängers von n Funktionale Programmierung Rekursive Funktionen Wir müssen den Y-Operator verwenden, um die Funktion R rekursiv zu machen. R ≡ (λ r n . Z n 0 ( n S ( r ( P n )))) Beispiel für die Summe der Zahlen von 0 bis 3 ist: YR3 = R(YR)3 = Z 3 0 (3S((YR)(P3))) ... 3S(YR2) ... 3 S (2 S (1 S 0)) ... 6 Prof. Dr. Margarita Esponda Funktionale Programmierung Anonyme Funktionen Anonym-Funktionen können in Haskell mit Hilfe von Lambda-Ausdrücken programmiert werden Beispiel: inc = \ x -> x+1 add = \ x y - > x+y g list = map (\xs -> zip xs [1..]) list für das schnelle Implementieren einfacher Hilfsfunktionen Prof. Dr. Margarita Esponda Funktionale Programmierung Parser ... instance Show Expr where show = show_expr show_expr :: Expr -> String show_expr (Var x) = x show_expr (App x y) = "(" ++ (show_expr x) ++ (show_expr y) ++")" show_expr (Lambda x y) = "(/" ++ x ++ "." ++ (show_expr y) ++")" Prof. Dr. Margarita Esponda Funktionale Programmierung Parser parse Nil [] parse e [] = error "empty expression" =e parse Nil (a:r) | letter a parse e (a:r) | letter a = parse (Var [a]) r = parse (App e (Var [a])) r parse Nil ('/':a:'.':r) | letter a = Lambda [a] (parse Nil r) parse e ('/':a:'.':r) | letter a = App e (Lambda [a] (parse Nil r)) parse Nil ('(':r) parse e ('(':r) parse e _ Prof. Dr. Margarita Esponda = parse (parse Nil a) b where (a,b) = extract [] r 0 = parse (App e (parse Nil a)) b where (a,b) = extract [] r 0 = error ("parsing error"++(show_expr e)) Funktionale Programmierung Parser letter x = element x ['a'..'z'] extract a [] _ = error "unbalanced parentheses" extract a (')':b) 0 = (a,b) extract a (')':b) n = extract (a++")") b (n-1) extract a ('(':b) n = extract (a++"(") b (n+1) extract a (c:b) element x [] n = extract (a++[c]) b n = False element x (y:rest) = x==y || element x rest Prof. Dr. Margarita Esponda Funktionale Programmierung Literatur A Tutorial Introduction to the Lambda Calculus. Raúl Rojas. http://www.inf.fu-berlin.de/lehre/WS03/alpi/lambda.pdf A Brief and Informal Introduction to the Lambda Calculus. Paul Hudak. Spring 2008. http://plucky.cs.yale.edu/cs201/lambda.pdf Prof. Dr. Margarita Esponda