Lambda-Kalkül

Werbung
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
Herunterladen