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