Programowanie funkcyjne Wykład 13. Siła wyrazu rachunku lambda Zdzisław Spławski Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 1
Wstęp Wartości logiczne Liczby naturalne jako liczebniki Churcha Kombinatory punktu stałego Reguły delta Reprezentowalność funkcji rekurencyjnych w λ-rachunku Kombinator punktu stałego w OCamlu Semantyka prostego języka imperatywnego Zadania kontrolne Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 2
Wstęp Język funkcyjny można potraktować jak rachunek lambda (beztypowy lub z typami) z wybraną semantyką redukcyjną, dodanymi stałymi (z odpowiednimi regułami redukcji) i dużą ilością lukru syntaktyczego. Te rozszerzenia rachunku lambda stosuje się w celu ułatwienia pisania programów, polepszenia ich czytelności i zwiększenia efektywności. Logicznie nie są one koniecznie. Jako przykłady zostaną zdefiniowane wartości logiczne i liczby naturalne z odpowiednimi funkcjami jako termy rachunku lambda. Zostaną też podane najważniejsze twierdzenia świadczące o sile wyrazu rachunku lambda. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 3
Wartości logiczne Specyfikacja algebraiczna: if true M N = M if false M N = N Odpowiednie termy można zdefiniować na przykład tak (Church): true λxy.x false λxy.y if λbuv.buv ( η λb.b) Udowodnimy, że spełnione jest pierwsze równanie specyfikacji. if true M N (λbuv.b u v) true M N β (λuv.true u v)m N β (λv.true M v) N β true M N (λxy.x) M N β (λy.m)n β M Analogicznie wygląda dowód drugiego równania. W celu zwiększenia czytelności czasem zamiast if B M N będziemy pisali if BthenM elsen. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 4
Liczby naturalne jako liczebniki Churcha Specyfikacja algebraiczna: { Iter 0 M N = N Iter (suc n) M N = M(Iter n M N) Odpowiednie termy można zdefiniować na różne sposoby, np. liczebniki Churcha są zdefiniowane następująco: 0 λfx.x 1 λfx.fx 2 λfx.f(fx) itd. Możemy to uogólnić i zauważyć, że liczebnik reprezentujący liczbę n będzie miał postać λfx.f n x, czyli jest iteratorem. Wobec tego definiujemy: suc λnfx.f(nfx) Iter λnfa.nfa ( η λn.n) lub suc λnfx.nf(fx) Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 5
Kombinatory punktu stałego Równania stałopunktowe w matematyce. Rozważmy równania algebraiczne, np. x = 5/x czy x = 6 x Problem rozwiązania tych równań można też sformułować jako problem znalezienia punktów stałych funkcji: f 1 λx.5/x i f 2 λx.6 x Punktem stałym funkcji jest wartość należąca do dziedziny funkcji, odwzorowywana przez funkcję na siebie. Poszukujemy więc takich wartości w 1 i w 2, że f 1 w 1 = w 1 i f 2 w 2 = w 2 Oczywiście w 1 = 5 i w 2 = 3. Punkt stały może być jeden, może ich być wiele (nawet nieskończenie wiele), może też nie być żadnego. Istnienie i liczba punktów stałych funkcji istotnie zależy od jej dziedziny. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 6
Kombinatory punktu stałego Równania stałopunktowe w programowaniu. Podobny problem pojawia się przy definicjach funkcji rekurencyjnych, np. lub inaczej: s(n) = if n = 0 then 1 else n s(n 1) s = λn.if n = 0 then 1 else n s(n 1) Z matematycznego punktu widzenia jest to równanie (wyższego rzędu) z jedną niewiadomą s. Symbol = oznacza tu β (lub βη) konwersję. Problem rozwiązania tego równania, tj. znalezienia lambda termu, spełniającego równanie, sprowadza się do znalezienia punktu stałego funkcjonału: F λfn.if n = 0 then 1 else n f(n 1) Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 7
Kombinatory punktu stałego Kombinator punktu stałego Definicja. Kombinatorem punktu stałego nazywamy każdy term M taki, że F.MF = F (MF ). Twierdzenie o punkcie stałym. Dowód. (i) F. X.X = F X (ii) Istnieje kombinator punktu stałego Y λf.(λx.f(xx))(λx.f(xx)) taki, że YF = F (YF ). (i) Niech W λx.f (xx) i X W W. Wówczas X W W (λx.f (xx))w F (W W ) F X (ii) YF (λf.(λx.f(xx))(λx.f(xx)))f β (λx.f (xx))(λx.f (xx)) β F ((λx.f (xx))(λx.f (xx))) = β F ((λf.(λx.f(xx))(λx.f(xx)))f ) F (YF ) Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 8
Kombinatory punktu stałego Kombinator Turinga Czasem potrzebujemy kombinatora punktu stałego M z nieco mocniejszą własnością: F.MF β F (MF ). Turing zaproponował następujący kombinator: Θ AA gdzie A λxy.y(xxy). ΘF (λxy.y(xxy))af β (λy.y(aay))f β F (AAF ) F (ΘF ) Zauważ, że dla kombinatora Y można udowodnić tylko YF = β F (YF ). Istnieje nieskończenie wiele kombinatorów punktu stałego. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 9
Kombinatory punktu stałego Przykład użycia kombinatora punktu stałego: silnia. Teraz możemy zdefiniować lambda term dla silni jako: s YF (lub ΘF ) gdzie F λfn.if n = 0 then 1 else n f(n 1) Uwaga. Przy ewaluacji tego termu należy stosować redukcję normalną. s n (Y F ) n = β F (Y F ) n (λfn.if n = 0 then 1 else n f(n 1))(Y F ) n β if n = 0 then 1 else n (Y F )(n 1) if n = 0 then 1 else n s(n 1) Oczywiście, możliwa jest inna definicja silni za pomocą iteratora, co odpowiadałoby rekursji ogonowej. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 10
Kombinatory punktu stałego Powyższy przykład możemy uogólnić. Twierdzenie. Niech C C[f, x] będzie termem ze zmiennymi wolnymi f i x. Wówczas: (i) F. N.F N = C[F, N] (ii) F. N.F N β C[F, N] Dowód. W obu przypadkach możemy wziąć F Θ(λf x.c[f, x]). Zauważ, że dla przypadku (i) wystarczy wziąć Y. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 11
Reguły delta Reguły delta Definicja. (i) Niech δ będzie pewną stałą. Wówczas Λδ jest zbiorem λ-termów zbudowanych ze zmiennych i stałej δ za pomocą aplikacji i abstrakcji w zwykły sposób. (ii) Analogicznie definiuje się Λ δ, gdzie δ oznacza ciąg stałych. (iii) Niech M oznacza ciąg zamkniętych λ-termów w postaci normalnej. δ-redukcja ma postać δ M δ f( M). Dla zadanej funkcji f δ-redukcja nie jest pojedynczą regułą, lecz schematem reguł. Tak wzbogacony system nazywamy rachunkiem λδ. Relacje kontrakcji i redukcji są oznaczane odpowiednio przez βδ i βδ. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 12
Reguły delta Reguły delta Twierdzenie. Niech f będzie funkcją na zamkniętych λ-termach w postaci normalnej. Wówczas relacja redukcji βδ spełnia twierdzenie Churcha-Rossera. Pojęcie redeksu, postaci normalnej i strategii redukcji w sposób naturalny uogólnia się na rachunek λδ. Prawdziwe jest też twierdzenie o standardyzacji. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 13
Reguły delta Przykład: test na zero W wielu funkcjach konieczne jest sprawdzenie, czy liczba jest równa zeru. Specyfikacja takiego predykatu wygląda następujaco: { iszero 0 = true iszero (suc n) = false Spełnia ona wymagania twierdzenia z poprzedniej strony ( przy założeniu, że n oznacza term zamknięty), więc moglibyśmy dodać do λ-termów stałą iszero oraz dwie reguły redukcji, nie troszcząc się o znalezienie termu, spełniającego powyższą specyfikację. My jednak podamy ten term. iszero λn.iter n (λb.false) true Łatwo pokazać, że spełnia on powyższą specyfikację. Analogicznie należy rozumieć następny przykład. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 14
Reguły delta Przykład: poprzednik { pred 0 = 0 pred (suc n) = n Ideę obliczania poprzednika wyjaśnia poniższy program. } y := 0; 0, 0 z := 0; while y n do ( z = pred y ) begin } z := y; n-krotna iteracja operacji q q y, z suc y, y y := suc y; end ( z = pred n ) Teraz ten algorytm możemy zapisać w postaci lambda-termu. pred λn.snd (Iter n (λp.pair(suc(fst p))(fst p)) (pair 0 0)) Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 15
Reprezentowalność funkcji rekurencyjnych w λ-rachunku Definicja. Funkcja częściowo rekurencyjna f : N k N jest definiowalna w beztypowym rachunku lambda, jeśli istnieje zamknięty term F, spełniający dla dowolnych n 1... n k N następujące warunki: (i) Jeśli f(n 1... n k ) = m, to F n 1... n k = β m; (ii) Jeśli wartość f(n 1... n k ) jest nieokreślona, to F n 1... n k nie ma postaci normalnej. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 16
Reprezentowalność funkcji rekurencyjnych w λ-rachunku Lemat. Funkcje bazowe (początkowe) Z, S, U i n są λ-definiowalne. Dowód. Weźmy następujące kombinatory jako termy definiujące. Z λn.0 suc λnfx.f(nfx) U i n λx 1... x n.x i Lemat. Funkcje λ-definiowalne są zamknięte ze względu na operację składania funkcji. Dowód. Niech funkcje g, h 1,..., h r będą λ-definiowalne odpowiednio za pomocą termów G, H 1,..., H r. Wówczas funkcja f( x) = g(h 1 ( x),..., h r ( x)) jest λ-definiowalna za pomocą termu F λ x.g(h 1 x)... (H r x) Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 17
Reprezentowalność funkcji rekurencyjnych w λ-rachunku Lemat. Funkcje λ-definiowalne są zamknięte ze względu na rekursję { prostą. { Rec g h 0 x = g x f(0, x) = g( x), Rec g h (suc n) x = h n (Rec g h n x) x f(s(n), x) = h(n, f(n, x), x), Ideę obliczania rekursora wyjaśnia poniższy program (porównaj z programem dla poprzednika). } y := 0; 0, g( x) z := g( x); while y n do ( z = f(y, x) ) begin } n-krotna iteracja z := h(y, z, x); q y, z suc y, h(y, z, x) operacji q y := suc y; end ( z = f(n, x) ) Teraz ten algorytm możemy zapisać w postaci λ-termu. Rec λghn x.snd (Iter n (λp.pair (suc (fst p)) (h (fst p) (snd p) x)) (pair 0 (g x)) Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 18
Reprezentowalność funkcji rekurencyjnych w λ-rachunku Lemat. Funkcje λ-definiowalne są zamknięte ze względu na operację minimum. Dowód. Niech funkcja g będzie λ-definiowalna za pomocą termu G. Wówczas funkcja f( x) = (µm.g( x, m) = 0) jest λ-definiowalna za pomocą termu mi λ x.szukaj 0 x ( η szukaj 0) gdzie szukaj Θ λfm x.if iszero (G x m) then m else f (suc m) x. Ponieważ Θ F F (Θ F ), to szukaj m x if iszero (G x m) then m else szukaj (suc m) x Wobec tego szukaj 0 x zaczynając od zera poszukuje najmniejszej liczby m takiej, że (G x m) = 0, co jest zgodne z definicją operacji minimum. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 19
Reprezentowalność funkcji rekurencyjnych w λ-rachunku Twierdzenie. Wszystkie funkcje częściowo rekurencyjne są λ-definiowalne. Teza Churcha [Churcha-Turinga]. Każda funkcja obliczalna w nieformalnym sensie jest λ-definiowalna (rekurencyjna). Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 20
Kombinator punktu stałego w OCamlu Kombinator punktu stałego w OCamlu. I Bezpośrednia definicja kombinatora Y λf.(λx.f(xx))(λx.f(xx)) w języku OCaml spowoduje błąd typu. Możemy jednak zdefiniować typ danych: type a self = Fold of ( a self -> a);; let unfold (Fold t) = t;; a następnie funkcjonał fixl : ( a -> a) -> a let fixl f = let w = fun x -> f ( unfold x x) in w (Fold w);; który działa poprawnie dla dla języków z wartościowaniem leniwym. W OCamlu, który stosuje wartościowanie gorliwie, wartościowanie fixl nie kończy się i nie uda się nam zdefiniować np. nieskończonej listy jedynek (sprawdź to!): let ones = fixl (function x -> 1::x);; Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 21
Kombinator punktu stałego w OCamlu Kombinator punktu stałego w OCamlu. II Można jednak nieco zmodyfikować powyższy funkcjonał i otrzymać funkcjonał: let fix f = let w = fun x y-> f (unfold x x) y in w (Fold w);; val fix : (( a -> b) -> a -> b) -> a -> b = <fun> który znajduje punkty stałe funkcji (ale już nie list!). Przekonaj się o tym, definiując funkcję obliczającą silnię zadanej liczby bez jawnego użycia rekursji. Funkcjonał fix : (( a -> b) -> a -> b) -> a -> b można też zdefiniować bez użycia typu a self, np. let fix f = let rec fixf x = f fixf x in fixf;; Można go teraz użyć do zdefiniowania silni bez użycia rekursji: let fact = fix (fun f n -> if n=0 then 1 else n*f(n-1));; Przekonaj się, że jest to rzeczywiście funkcja silni. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 22
Semantyka prostego języka imperatywnego Składnia i semantyka prostego języka imperatywnego. I Składnia abstrakcyjna V Zmienna N Liczba B ::= true false B&&B B B not B E < E E = E E ::= N V E + E E E E E E C ::= skip C; C V := E if B then C else C fi while B do C od Semantykę denotacyjną języka zadaje się definiując funkcje semantyczne, które opisują, jak semantyka wyrażenia może być otrzymana z semantyki składowych tego wyrażenia (semantyka kompozycyjna). Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 23
Semantyka prostego języka imperatywnego Składnia i semantyka prostego języka imperatywnego. II Funkcja semantyczna dla instrukcji C jest funkcją częściową S C [C ], zmieniającą wektor stanu programu S (pamięć operacyjną). Dziedzinę S reprezentujemy za pomocą zbioru funkcji σ : Zmienna Z. Załóżmy dla uproszczenia, że funkcje semantyczne dla wyrażeń logicznych B i arytmetycznych E są znane. Tutaj zdefiniujemy w rachunku lambda funkcję semantyczną S C [C ] : S S dla instrukcji. Na wykładzie 3 zaprogramowaliśmy ją w OCamlu. Semantyka denotacyjna S B [B ] : S {true, false} S E [E ] : S Z S C [skip]σ = σ S C [C 1 ; C 2 ]σ S C [C 2 ](S C [C 1 ]σ) S C [V := E ]σ σ[v S E [E ]σ] { SC [C S C [if B then C 1 else C 2 fi]σ 1 ]σ gdy S E [B ]σ = true S C [C 2 ]σ gdy S E [B ]σ = false Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 24
Semantyka prostego języka imperatywnego Składnia i semantyka prostego języka imperatywnego. III Wykonując jednokrotnie pętlę while otrzymujemy poniższe równanie dla funkcji semantycznej S C [while B do C od]: { SC[[while B do C od]](s C[[C]]σ) gdy S B[[B]]σ = true S C[[while B do C od]]σ σ gdy S B[[B]]σ = false Z analogicznym problemem mieliśmy do czynienia w przypadku funkcji silnia. Należy znaleźć najmniejszy punkt stały funkcjonału: { f(sc [C ]σ) gdy S F λf.λσ. B [B ]σ = true σ gdy S B [B ]σ = false czyli, używając kombinatora punktu stałego Θ (lub Y): S C [while B do C od] ΘF Istnienie najmniejszego punktu stałego gwarantuje teoria dziedzin. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 25
Zadania kontrolne 1. Wykorzystując term if zdefiniuj pozostałe spójniki logiczne. 2. Udowodnij, że termy 0, suc, Iter spełniają specyfikację algebraiczną ze strony 5. 3. Zdefiniuj lambda wyrażenia pair, fst i snd z następującymi regułami redukcji: fst (pair M N) M snd (pair M N) N 4. Niech Y λf.(λx.f(xx))(λx.f(xx)), S λxyz.xz(yz), I λx.x. Udowodnij, że wszystkie kombinatory zdefiniowane następująco: { Y 0 Y Y n+1 Y n (SI) są kombinatorami punktu stałego. 5. Pokaż, że Y 1 β Θ. 6. Zdefiniuj kombinator Θ w OCamlu. Zdzisław Spławski: Programowanie funkcyjne, Wykład 13. Siła wyrazu rachunku lambda 26