Semantyka i weryfikacja programów/Ćwiczenia 5: Różnice pomiędzy wersjami
Nie podano opisu zmian |
Nie podano opisu zmian |
||
Linia 1: | Linia 1: | ||
== | == Zawartość == | ||
Kontynuujemy | Kontynuujemy ćwiczenie semantyki naturalnej, | ||
dodając pewne konstrukcje do języka Tiny. | |||
W | W szczególności rozszerzymy go o deklaracje zmiennych (bloki). | ||
Po raz pierwszy roszdzielimy | Po raz pierwszy roszdzielimy informację przechowywaną w konfiguracji | ||
na '' | na ''środowisko'' określające wiązanie identyfikatorów i ''stan'' | ||
przechowujący wartości zmiennych. | |||
Będzie to przygotowanie do kolejnych zajęć. | |||
Linia 18: | Linia 18: | ||
Rozszerzamy | Rozszerzamy język Tiny następująco: | ||
<math> | <math> | ||
Linia 28: | Linia 28: | ||
Znaczenie instrukcji <math>\mathbf{for}\, x = e_1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2</math> | Znaczenie instrukcji <math>\mathbf{for}\, x = e_1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2</math> | ||
jest | jest następujące. | ||
Obliczamy | Obliczamy wartości wyrażeń <math>e_1</math> i <math>e_2</math>. Jeśli pierwsza | ||
z nich jest mniejsza lub równa od drugiej, to podstawiamy | z nich jest mniejsza lub równa od drugiej, to podstawiamy pierwszą | ||
wartość (wartość wyrażenia <math>e_1</math>) na zmienną <math>x</math> i uruchamiamy <math>I_1</math>. | |||
Jeśli w <math>I_1</math> nie zostanie napotkana instrukcja <math>\mathbf{fail}</math>, | |||
kończymy instrukcję <math>\mathbf{for}\,</math> i przyracamy wartość zmiennej <math>x</math> | |||
sprzed tej instrukcji. | sprzed tej instrukcji. | ||
Natomiast | Natomiast jeśli w <math>I_1</math> zostanie napotkana instrukcja <math>\mathbf{fail}</math>, | ||
podstawiamy na <math>x</math> | podstawiamy na <math>x</math> kolejną, o jeden większą wartość, | ||
przywracamy | przywracamy wartości wszystkich | ||
pozostałych zmiennych sprzed instrukcji <math>\mathbf{for}\,</math>, i ponownie | |||
wykonujemy <math>I_1</math>. | wykonujemy <math>I_1</math>. | ||
Powtarzamy w ten sposób | Powtarzamy w ten sposób aż <math>I_1</math> zakończy się nie napotkawszy <math>\mathbf{fail}</math>, | ||
albo | albo wartość zmiennej <math>x</math> przekroczy wartość wyrażenia <math>e_2</math> | ||
obliczoną na początku. | |||
W pierwszym przypadku | W pierwszym przypadku | ||
kończymy instrukcję <math>\mathbf{for}\,</math> i przyracamy wartość zmiennej <math>x</math> | |||
sprzed tej instrukcji. | sprzed tej instrukcji. | ||
W drugim przywracamy | W drugim przywracamy wartości wszystkich zmiennych sprzed instrukcji | ||
<math>\mathbf{for}\,</math> i uruchamiamy <math>I_2</math>. | <math>\mathbf{for}\,</math> i uruchamiamy <math>I_2</math>. | ||
}} | }} | ||
Linia 52: | Linia 52: | ||
{{przyklad||| | {{przyklad||| | ||
Oto | Oto przykładowy program: | ||
}} | }} | ||
Linia 62: | Linia 62: | ||
Wartości zmiennych po zakończeniu programu to: <math>x = 0, y = 2, z = 5</math>. | |||
Linia 70: | Linia 70: | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><div class="mw-collapsible-content" style="display:none"> | ||
Będziemy posługiwać się jak zwykle tranzycjami postaci: | |||
<math> | <math> | ||
Linia 77: | Linia 77: | ||
Zacznijmy od najprostszego przypadku, gdy nie ma wogóle potrzeby | Zacznijmy od najprostszego przypadku, gdy nie ma wogóle potrzeby | ||
uruchamiania instrukcji | uruchamiania instrukcji wewnętrznej <math>I_1</math>, ponieważ | ||
wartość wyrażenia <math>e_1</math> jest większa od <math>e_2</math>: | |||
<math> | <math> | ||
Linia 90: | Linia 90: | ||
</math> | </math> | ||
Po prostu uruchamiamy | Po prostu uruchamiamy instrukcję <math>I_2</math> w stanie <math>s</math>, ignorując | ||
<math>I_1</math>. | <math>I_1</math>. | ||
Kolejny nietrudny przypadek to sytuacja, w której | Kolejny nietrudny przypadek to sytuacja, w której | ||
<math>n_1 \leq n_2</math> a wykonanie instrukcji | <math>n_1 \leq n_2</math> a wykonanie instrukcji wewnętrznej <math>I_1</math> | ||
kończy się sukcesem, tzn. nie następuje wywołanie instrukcji | |||
<math>\mathbf{fail}</math>. Oto | <math>\mathbf{fail}</math>. Oto reguła dla tego przypadku: | ||
<math> | <math> | ||
Linia 108: | Linia 108: | ||
</math> | </math> | ||
Tym razem uruchamiamy | Tym razem uruchamiamy instrukcję <math>I_1</math>, podstawiając | ||
pod | pod zmienną <math>x</math> wartość <math>n_1</math>. | ||
Zwróćmy uwagę, że po zakończeniu <math>I_1</math> przywracamy | |||
wartość zmiennej <math>x</math> sprzed jej wykonania. | |||
Pozostał nam trzeci przypadek, gdy <math>n_1 \leq n_2</math> | |||
a wykonanie instrukcji <math>I_1</math> | a wykonanie instrukcji <math>I_1</math> zakończyło się | ||
wykonaniem instrukcji <math>\mathbf{fail}</math> | wykonaniem instrukcji <math>\mathbf{fail}</math> gdzieś wewnątrz <math>I_1</math>. | ||
Aby poprawnie | Aby poprawnie opisać takie zdarzenie, potrzebujemy dodatkowych | ||
tranzycji postaci: | tranzycji postaci: | ||
<math> | <math> | ||
I, s \,\longrightarrow\, \mbox{ | I, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}. | ||
</math> | </math> | ||
Mówiąc formalnie, poszerzamy zbiór konfiguracji o jeden element | |||
<math>\mbox{ | <math>\mbox{było-}\mathbf{fail}</math>. | ||
Zauważmy, że po prawej stronie tranzycji nie ma wogóle stanu. | |||
Nie potrzebujemy go dla opisania semantyki instrukcji <math>\mathbf{for}\,</math>: | Nie potrzebujemy go dla opisania semantyki instrukcji <math>\mathbf{for}\,</math>: | ||
jeśli wystąpi <math>\mathbf{fail}</math>, powtarzamy <math>I_1</math> dla większej o <math>1</math> wartości | |||
zmiennej <math>x</math>, ale | zmiennej <math>x</math>, ale pozostałym zmiennym przywracamy wartość ''sprzed'' | ||
<math>I_1</math>. | <math>I_1</math>. | ||
A | A więc załóżmy, że | ||
<math>I_1, s[x \mapsto n_1] \,\longrightarrow\, \mbox{ | <math>I_1, s[x \mapsto n_1] \,\longrightarrow\, \mbox{było-}\mathbf{fail}.</math> | ||
W takiej sytuacji | W takiej sytuacji powinniśmy przypisać <math>n_1 + 1</math> | ||
na | na zmienną <math>x</math> i spróbować ponownie wykonać | ||
<math>I_1</math> przy | <math>I_1</math> przy wartościach wszystkich pozostałych | ||
zmiennych takich, jak na | zmiennych takich, jak na początku instrukcji <math>\mathbf{for}\,</math>. | ||
I | I powtarzać ten schemat aż do skutku, tzn. aż do mementu, | ||
gdy zaistnieje | gdy zaistnieje któraś z poprzednich dwóch (prostych) sytuacji. | ||
Oto odpowiednia | Oto odpowiednia reguła: | ||
<math> | <math> | ||
Linia 147: | Linia 147: | ||
e_2, s \,\longrightarrow\, n_2 | e_2, s \,\longrightarrow\, n_2 | ||
\quad | \quad | ||
I_1, s[x \mapsto n_1] \,\longrightarrow\, \mbox{ | I_1, s[x \mapsto n_1] \,\longrightarrow\, \mbox{było-}\mathbf{fail} | ||
\quad | \quad | ||
\mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'} | \mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'} | ||
Linia 154: | Linia 154: | ||
</math> | </math> | ||
Zwróćmy uwagę na pewnien niezwykle istotny szczegół: po zakończeniu | |||
wykonania instrukcji <math>\mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2</math> | wykonania instrukcji <math>\mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2</math> | ||
otrzymujemy stan <math>s'</math>, w którym '''nie przywracamy''' | otrzymujemy stan <math>s'</math>, w którym '''nie przywracamy''' wartości | ||
zmiennej <math>x</math>. | zmiennej <math>x</math>. Gdybyśmy tak zrobili, tzn. gdybyśmy zastąpili <math>s' | ||
</math> przez <math>s'[x \mapsto s(x)]</math>, nasze semantyka | </math> przez <math>s'[x \mapsto s(x)]</math>, nasze semantyka byłaby | ||
niepoprawna. Dlaczego? Dlatego, | niepoprawna. Dlaczego? Dlatego, że nie wiemy tak naprawdę, | ||
czy | czy powinniśmy przywracać wartość zmiennej <math>x</math> czy nie. | ||
Jeśli ostatecznie nasza instrukcja <math>\mathbf{for}\,</math> zakończyla | |||
się przez bezbłędne zakończenie instrukcji <math>I_1</math> | |||
(przypadek drugi), to | (przypadek drugi), to powinniśmy to zrobić; | ||
ale | ale jeśli zakończyła się wykonaniem instrukcji <math>I_2</math> | ||
(przypadek pierwszy), | (przypadek pierwszy), | ||
to | to powinniśmy pozostawić wartość zmiennej <math>x</math> taką, | ||
jaka jest ona po | jaka jest ona po zakończeniu <math>I_2</math>. | ||
A zatem w | A zatem w powyższej regule dla przypadku trzeciego | ||
nie przywracamy | nie przywracamy wartości zmiennej <math>x</math>; jeśli było | ||
to konieczne, to | to konieczne, to zostało już wykonane ,,głębiej", | ||
dzięki regule dla przypadku drugiego oraz dzięki temu, | |||
że instrukcja <math>\mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2</math> | |||
wykonywana jest w orginalnym stanie <math>s</math>, | wykonywana jest w orginalnym stanie <math>s</math>, | ||
a nie w stanie, w którym | a nie w stanie, w którym kończy się <math>I_1</math> | ||
(tego ostatniego stanu nawet nie znamy). | (tego ostatniego stanu nawet nie znamy). | ||
Jeszcze jeden drobiazg: zamiast <math>e_1 {+} 1</math> w | Jeszcze jeden drobiazg: zamiast <math>e_1 {+} 1</math> w | ||
<math>\mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'</math> | <math>\mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'</math> | ||
w regule | w regule powyżej, moglibyśmy podstawić policzoną już | ||
wartość, czyli <math>n_1 {+} 1</math>. | |||
Na | Na zakończenie prezentujemy reguły niebędne do tego, | ||
aby | aby wygenerować <math>\mbox{było-}\mathbf{fail}</math>: | ||
<math> | <math> | ||
\mathbf{fail}, s \,\longrightarrow\, \mbox{ | \mathbf{fail}, s \,\longrightarrow\, \mbox{było-}\mathbf{fail} | ||
</math> | </math> | ||
oraz aby | oraz aby wystąpienie <math>\mathbf{fail}</math> umiejscowione gdzieś ,,głęboko" | ||
zostało rozpropagowane do najbliższej otaczającej | |||
instrukcji <math>\mathbf{for}\,</math>. | instrukcji <math>\mathbf{for}\,</math>. | ||
Jeśli pojawił się <math>\mathbf{fail}</math>, powinniśmy zaniechać dalszego | |||
wykonania instrukcji a w przypadku | wykonania instrukcji a w przypadku pętli, powinniśmy | ||
zaniechać dalszego iterowania tej pętli. | |||
Oto odpowiednie | Oto odpowiednie reguły: | ||
<math> | <math> | ||
\frac{I_1, s \,\longrightarrow\, \mbox{ | \frac{I_1, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}} | ||
{I_1;\, I_2, s \,\longrightarrow\, \mbox{ | {I_1;\, I_2, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}} | ||
\quad \quad | \quad \quad | ||
\frac{I_1, s \,\longrightarrow\, s' \quad \quad I_2, s' \,\longrightarrow\, \mbox{ | \frac{I_1, s \,\longrightarrow\, s' \quad \quad I_2, s' \,\longrightarrow\, \mbox{było-}\mathbf{fail}} | ||
{I_1;\, I_2, s \,\longrightarrow\, \mbox{ | {I_1;\, I_2, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}} | ||
</math> | </math> | ||
<math> | <math> | ||
\frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I_1, s \,\longrightarrow\, \mbox{ | \frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I_1, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}} | ||
{\mathbf{if}\, b \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, \mbox{ | {\mathbf{if}\, b \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}} | ||
\quad \quad | \quad \quad | ||
\mbox{ i analogicznie gdy } b, s \,\longrightarrow\, \mathbf{false} | \mbox{ i analogicznie gdy } b, s \,\longrightarrow\, \mathbf{false} | ||
Linia 213: | Linia 213: | ||
<math> | <math> | ||
\frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I, s \,\longrightarrow\, \mbox{ | \frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}} | ||
{\mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, \mbox{ | {\mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}} | ||
\quad \quad | \quad \quad | ||
\frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I, s \,\longrightarrow\, s' \quad \quad | \frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I, s \,\longrightarrow\, s' \quad \quad | ||
\mathbf{while}\, b \,\mathbf{do}\, I, s' \,\longrightarrow\, \mbox{ | \mathbf{while}\, b \,\mathbf{do}\, I, s' \,\longrightarrow\, \mbox{było-}\mathbf{fail}} | ||
{\mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, \mbox{ | {\mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}} | ||
</math> | </math> | ||
Widać podobieństwo do analogicznych reguł dla | |||
pętli <math>\mathbf{loop}\, I</math>, którą zajmowaliśmy się na wcześniejszych | |||
zajęciach. | |||
Zauważmy, że jesli <math>\mathbf{fail}</math> zostało wykonane poza jakąkolwiek | |||
pętlą <math>\mathbf{for}\,</math>, to program ,,zakończy się" w konfiguracji | |||
<math>\mbox{ | <math>\mbox{było-}\mathbf{fail}</math>. Możemy zatem tę właśnie konfigurację | ||
uznać za konfigurację końcową, informującą o porażce | |||
wykonania programu. | wykonania programu. | ||
Linia 240: | Linia 240: | ||
{{cwiczenie|2 (bloki i deklaracje zmiennych)|cw2| | {{cwiczenie|2 (bloki i deklaracje zmiennych)|cw2| | ||
Rozszerz | Rozszerz semantykę języka Tiny o deklarację zmiennej: | ||
<math> | <math> | ||
Linia 248: | Linia 248: | ||
</math> | </math> | ||
Zasięgiem zmiennej <math>x</math> jest instrukcja <math>I</math> | |||
czyli | czyli wnętrze bloku, w którym jest zadeklarowana. | ||
Zakładamy zwykłe (statyczne) reguły widoczności, przesłaniania, itp. | |||
}} | }} | ||
Linia 258: | Linia 258: | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><div class="mw-collapsible-content" style="display:none"> | ||
''' | '''Rozwiązanie 1 (tylko stan)''' | ||
<br> | <br> | ||
Oczywiście powinniśmy odróżniać zmienne zadeklarowane (i zarazem | |||
zainicjowane) od tych niezadeklarowanych. | zainicjowane) od tych niezadeklarowanych. | ||
Zatem przyjmijmy, | Zatem przyjmijmy, że | ||
<math>\mathbf{State} = \mathbf{Var} \to_{\mathrm{fin}} \mathbf{Num}</math>. | <math>\mathbf{State} = \mathbf{Var} \to_{\mathrm{fin}} \mathbf{Num}</math>. | ||
Zachowujemy wszystkie | Zachowujemy wszystkie reguły semantyczne dla języka Tiny | ||
i dodajemy | i dodajemy jedną nową: | ||
<math> | <math> | ||
Linia 275: | Linia 275: | ||
</math> | </math> | ||
mówiącą, że instrukcja wewnętrzna <math>I</math> ewaluowana jest | |||
w stanie, w którym dodatkowo zaznaczono | w stanie, w którym dodatkowo zaznaczono wartość zmiennej <math>x</math> | ||
równą wartości, do której oblicza się <math>e</math>. | |||
Oczywiście takie rozwiązanie jest niepoprawne, gdyż wartość | |||
zmiennej <math>x</math> pozostaje w stanie nawet po | zmiennej <math>x</math> pozostaje w stanie nawet po wyjściu z bloku | ||
(,,wycieka" z bloku). | (,,wycieka" z bloku). | ||
Musimy | Musimy dodać ,,dealokację" zmiennej <math>x</math>: | ||
<math> | <math> | ||
Linia 289: | Linia 289: | ||
</math> | </math> | ||
I znów napotykamy podobnie | I znów napotykamy podobnie trudności jak na wcześniejszych zajęciach: | ||
powyższa reguła nie obejmuje przypadku, gdy <math>s(x)</math> jest | |||
nieokreślone. | |||
I | I choć moglibyśmy naprawić ten mankament podobnie jak kiedyś | ||
(co pozostawiamy Czytelnikowi), istnieje inne, bardzo eleganckie | (co pozostawiamy Czytelnikowi), istnieje inne, bardzo eleganckie | ||
i elastyczne | i elastyczne rozwiązanie tego problemu, które teraz omówimy. | ||
<br> | <br> | ||
''' | '''Rozwiązanie 2 (stan i środowisko)''' | ||
<br> | <br> | ||
Podstawowy | Podstawowy pomysł polega na rozdzielenie informacji przechowywanej | ||
dotychczas w stanie na dwa ,,etapy". | dotychczas w stanie na dwa ,,etapy". | ||
Pierwszy z nich, odwzorowuje identyfikatory zmiennych na ''lokacje'', a drugi | Pierwszy z nich, odwzorowuje identyfikatory zmiennych na ''lokacje'', a drugi | ||
lokacje na | lokacje na wartości. | ||
Mamy | Mamy więc ''środowiska'' <math>E \in \mathbf{Env}</math>, będące funkcjami częściowymi z <math>\mathbf{Var}</math> | ||
do <math>\mathbf{Loc}</math>, zbioru lokacji: | do <math>\mathbf{Loc}</math>, zbioru lokacji: | ||
Linia 312: | Linia 312: | ||
</math> | </math> | ||
oraz stany, | oraz stany, będące teraz funkcjami częściowymi z <math>\mathbf{Loc}</math> do <math>\mathbf{Num}</math>: | ||
<math> | <math> | ||
Linia 318: | Linia 318: | ||
</math> | </math> | ||
Dla | Dla jednoznaczności używamy innej nazwy (<math>\mathbf{Store}</math>) ale będziemy | ||
zwykle | zwykle używać symbolu <math>s, s', s_1, \ldots \in \mathbf{Store}</math> itp. do nazywania stanów. | ||
Intuicyjnie | Intuicyjnie można myśleć o lokacjach w pamięci | ||
operacyjnej maszyny. | operacyjnej maszyny. Środowisko zawiera informację o lokacji, w której | ||
przechowywana jest | przechowywana jest wartość danej zmiennej | ||
a stan opisuje | a stan opisuje właśnie zawartość używanych lokacji. | ||
Zakładamy przy tym, że mamy do dyspozycji nieskończony zbiór | |||
lokacji <math>\mathbf{Loc} = \{ l_0, l_1, \ldots \}</math> i | lokacji <math>\mathbf{Loc} = \{ l_0, l_1, \ldots \}</math> i że | ||
w | w każdym momencie tylko skończenie wiele spośród nich jest | ||
wykorzystywane. | wykorzystywane. | ||
Formalnie | Formalnie mowiąc, dziedzina funkcji częściowej <math>s</math> jest zawsze skończona. | ||
Daje nam to | Daje nam to pewność, że zawsze jest jakaś nieużywana lokacja. | ||
Środowisko początkowe, w którym uruchamiany | |||
będzie program, będzie z reguły puste. | |||
Ponadto obraz funkcji | Ponadto obraz funkcji częściowej <math>E</math> | ||
będzie zawsze zawarty w zbiorze aktualnie używanych lokacji, | |||
czyli zawarty w dziedzinie funkcji | czyli zawarty w dziedzinie funkcji częściowej <math>s</math>, oznaczanej | ||
<math>\mathrm{dom}(s)</math>. | <math>\mathrm{dom}(s)</math>. | ||
Pożytek z tego podziału na dwa etapy będzie taki, że | |||
będziemy umieli łatwo i elastycznie opisywać deklaracje zmiennych, | |||
przesłanianie identyfikatorów, itp. | |||
Tranzycje | Tranzycje będą teraz postaci: | ||
<math> | <math> | ||
Linia 348: | Linia 348: | ||
</math> | </math> | ||
czyli instrukcja <math>I</math> | czyli instrukcja <math>I</math> będzie modyfikować stan ale nie będzie | ||
zmieniać środowiska <math>E</math>. Dla czytelności będziemy zapisywać | |||
nasze | nasze reguły w następujący sposób: | ||
<math> | <math> | ||
Linia 356: | Linia 356: | ||
</math> | </math> | ||
podkreślając w ten sposób, że środowisko nie ulega zmianie. | |||
Ale | Ale należy pamiętać, że konfiguracja, w której ,,uruchamiamy" | ||
instrukcję <math>I</math> składa się naprawdę z pary <math>(E, s)</math>. | |||
Deklarując nową zmienną <math>x</math> dodamy po prostu do <math>E</math> parę | |||
<math>(x, l)</math>, gdzie <math>l</math> jest | <math>(x, l)</math>, gdzie <math>l</math> jest nową, nieużywaną dotychczas lokacją. | ||
Dla wygody zapisu | Dla wygody zapisu załóżmy, że mamy do dyspozycji funkcję | ||
funkcję | |||
<math> | <math> | ||
Linia 369: | Linia 369: | ||
</math> | </math> | ||
która zwraca | która zwraca jakąś nową, nieużywaną lokację. Formalnie wymagamy, by | ||
<math> | <math> | ||
Linia 375: | Linia 375: | ||
</math> | </math> | ||
Dla ilustracji popatrzmy na | Dla ilustracji popatrzmy na przykładową regułę dla deklaracji. | ||
<math> | <math> | ||
Linia 384: | Linia 384: | ||
</math> | </math> | ||
Zauważmy, że stan <math>s'</math> po zakończeniu instrukcji | |||
<math>\mathbf{begin}\, \mathbf{var}\, x=e;\, I \,\mathbf{end}</math> zawiera | <math>\mathbf{begin}\, \mathbf{var}\, x=e;\, I \,\mathbf{end}</math> zawiera informację o zmiennej | ||
lokalnej <math>x</math>, tzn. <math>s(l)</math> jest | lokalnej <math>x</math>, tzn. <math>s(l)</math> jest określone. | ||
Ale lokacja <math>l</math> jest ,, | Ale lokacja <math>l</math> jest ,,nieosiągalna" w środowisku | ||
<math>E</math>, | <math>E</math>, gdyż para <math>x \mapsto l</math> została dodana tylko do | ||
środowiska, w którym ewaluuje się wnętrze bloku, a środowisko | |||
<math>E</math> | <math>E</math> całego bloku nie jest modyfikowane. | ||
Poniżej przedstawiamy reguły dla pozostałych instrukcji. | |||
Przede wszystkim | Przede wszystkim złożenie sekwencyjne: | ||
<math> | <math> | ||
Linia 402: | Linia 402: | ||
</math> | </math> | ||
Reguła ta uzmysławia nam różnicę pomiędzy środowiskiem a stanem: | |||
środowisko pozostaje to samo, gdy przechodzimy od jednej instrukcji do | |||
następnej, a stan oczywiście ewoluuje wraz ze zmieniającymi się | |||
wartościami zmiennych. | |||
Reguły dla przypisania, instrukcji warunkowej i pętli | |||
nie | nie przedstawiają żadnych nowych trudności. | ||
Musimy tylko najpierw | Musimy tylko najpierw ustalić postać reguł dla wyrażeń: | ||
<math> | <math> | ||
Linia 415: | Linia 415: | ||
</math> | </math> | ||
która jest | która jest zupełnie naturalna w naszym podejściu opartym o środowiska | ||
i stany. | i stany. | ||
Reguły mogą wyglądać np. tak: | |||
<math> | <math> | ||
Linia 443: | Linia 443: | ||
</math> | </math> | ||
Reguły dla wyrażeń są oczywiste: | |||
<math> | <math> | ||
Linia 452: | Linia 452: | ||
</math> | </math> | ||
i tak dalej -- pomijamy | i tak dalej -- pomijamy pozostałe reguły. | ||
Linia 459: | Linia 459: | ||
<br> | <br> | ||
Przypomnijmy sobie | Przypomnijmy sobie semantykę bloku | ||
<math> | <math> | ||
Linia 468: | Linia 468: | ||
</math> | </math> | ||
i zastanówmy | i zastanówmy się, jak ,,posprzątać" po zakończeniu wykonania bloku, | ||
tzn. | tzn. zwolnić lokację <math>l</math>, która była używana tylko w tym bloku | ||
i w | i w związku z tym nie będzie już potrzebne. | ||
Oznaczałoby to przywrócenie lokacji <math>l</math> do puli wolnych | |||
( | (nieużywanych) lokacji. | ||
Zmodyfikowana | Zmodyfikowana reguła dla instrukcji bloku powinna wyglądać mniej więcej tak: | ||
<math> | <math> | ||
Linia 484: | Linia 484: | ||
gdzie <math>\bar{s}</math> to stan <math>s''</math> ,,okrojony" do dziedziny stanu <math>s</math>. | gdzie <math>\bar{s}</math> to stan <math>s''</math> ,,okrojony" do dziedziny stanu <math>s</math>. | ||
Powinniśmy po prostu przywrócić nieokreśloność stanu dla lokacji | |||
<math>l</math>. | <math>l</math>. | ||
Natomiast | Natomiast oczywiście nie ma potrzeby dealokownia środowiska! | ||
Oto | Oto rozwiązanie: | ||
<math> | <math> | ||
Linia 504: | Linia 504: | ||
{{cwiczenie|1|cw1.dom| | {{cwiczenie|1|cw1.dom| | ||
Napisz | Napisz semantykę naturalną dla następującego | ||
rozszerzenia | rozszerzenia języka Tiny: | ||
<math> | <math> | ||
Linia 520: | Linia 520: | ||
</math> | </math> | ||
Instrukcja <math>\mathbf{throw}\, x</math> oznacza podniesienie | Instrukcja <math>\mathbf{throw}\, x</math> oznacza podniesienie wyjątku o nazwie <math>x</math>. | ||
Instrukcja <math>I = \,\mathbf{try}\, I_1 \,\mathbf{catch}\, exc\, I_2</math> wykonuje <math>I_1</math>. | Instrukcja <math>I = \,\mathbf{try}\, I_1 \,\mathbf{catch}\, exc\, I_2</math> wykonuje <math>I_1</math>. | ||
Jeśli podczas wykonania <math>I_1</math> zostanie podniesiony wyjątej <math>x | |||
</math>, i <math>exc = x</math> albo <math>exc = \mathbf{any}</math>, to | </math>, i <math>exc = x</math> albo <math>exc = \mathbf{any}</math>, to następuje przerwanie <math> | ||
I_1</math> i sterowanie zostaje przeniesione do <math>I_2</math> | I_1</math> i sterowanie zostaje przeniesione do <math>I_2</math> | ||
( | (następuje ''obsługa wyjątku''). | ||
Jeśli zaś podczas wykonania <math>I_1</math> zostanie podniesiony wyjątej <math>x | |||
</math> oraz <math>exc \neq x</math> i <math>exc \neq \mathbf{any}</math>, to | </math> oraz <math>exc \neq x</math> i <math>exc \neq \mathbf{any}</math>, to obsługa wyjątku | ||
przekazana jest do | przekazana jest do najbliższej instrukcji <math>\,\mathbf{try}\,</math> otaczającej <math>I | ||
</math>. | </math>. | ||
Umawiamy | Umawiamy się, że <math>\,\mathbf{try}\, I_1 \,\mathbf{catch}\, exc\, I_2</math> ''otacza'' <math>I_1</math> | ||
i wszystkie instrukcje | i wszystkie instrukcje wewnątrz <math>I_1</math>, | ||
ale ''nie'' otacza <math>I_2</math>. | ale ''nie'' otacza <math>I_2</math>. | ||
}} | }} | ||
Linia 538: | Linia 538: | ||
{{cwiczenie|2|cw2.dom| | {{cwiczenie|2|cw2.dom| | ||
Zaproponuj | Zaproponuj modyfikację semantyki, w której deklaracja | ||
jest wykonywana ,,równolegle", analogicznie do przypisania | jest wykonywana ,,równolegle", analogicznie do przypisania | ||
równoległego. Przy takiej semantyce kolejność poszczególnych | |||
deklaracji powinna | deklaracji powinna być nieistotna. | ||
}} | }} |
Wersja z 08:10, 10 sie 2006
Zawartość
Kontynuujemy ćwiczenie semantyki naturalnej, dodając pewne konstrukcje do języka Tiny. W szczególności rozszerzymy go o deklaracje zmiennych (bloki). Po raz pierwszy roszdzielimy informację przechowywaną w konfiguracji na środowisko określające wiązanie identyfikatorów i stan przechowujący wartości zmiennych. Będzie to przygotowanie do kolejnych zajęć.
Semantyka naturalna pewnej instrukcji
Ćwiczenie 1
Rozszerzamy język Tiny następująco:
Znaczenie instrukcji jest następujące. Obliczamy wartości wyrażeń i . Jeśli pierwsza z nich jest mniejsza lub równa od drugiej, to podstawiamy pierwszą wartość (wartość wyrażenia ) na zmienną i uruchamiamy . Jeśli w nie zostanie napotkana instrukcja , kończymy instrukcję i przyracamy wartość zmiennej sprzed tej instrukcji. Natomiast jeśli w zostanie napotkana instrukcja , podstawiamy na kolejną, o jeden większą wartość, przywracamy wartości wszystkich pozostałych zmiennych sprzed instrukcji , i ponownie wykonujemy . Powtarzamy w ten sposób aż zakończy się nie napotkawszy , albo wartość zmiennej przekroczy wartość wyrażenia obliczoną na początku. W pierwszym przypadku kończymy instrukcję i przyracamy wartość zmiennej sprzed tej instrukcji. W drugim przywracamy wartości wszystkich zmiennych sprzed instrukcji i uruchamiamy .
Przykład
Oto przykładowy program:
x := 0; y := 1; x := 1 \,\mathbf{to}\, 5 y := y+1; x <= 4 z:= x
Wartości zmiennych po zakończeniu programu to: .
Rozwiązanie
Semantyka naturalna bloków
Ćwiczenie 2 (bloki i deklaracje zmiennych)
Rozszerz semantykę języka Tiny o deklarację zmiennej:
Zasięgiem zmiennej jest instrukcja czyli wnętrze bloku, w którym jest zadeklarowana. Zakładamy zwykłe (statyczne) reguły widoczności, przesłaniania, itp.
Rozwiązanie
Zadania domowe
Ćwiczenie 1
Napisz semantykę naturalną dla następującego rozszerzenia języka Tiny:
Parser nie mógł rozpoznać (nieznana funkcja „\ldotes”): {\displaystyle I \, ::= \,\, \ldotes \,\,|\,\, \mathbf{throw}\, x \,\,|\,\, \,\mathbf{try}\, I_1 \,\mathbf{catch}\, exc\, I_2 }
Instrukcja oznacza podniesienie wyjątku o nazwie . Instrukcja wykonuje . Jeśli podczas wykonania zostanie podniesiony wyjątej , i albo , to następuje przerwanie i sterowanie zostaje przeniesione do (następuje obsługa wyjątku). Jeśli zaś podczas wykonania zostanie podniesiony wyjątej oraz i , to obsługa wyjątku przekazana jest do najbliższej instrukcji otaczającej . Umawiamy się, że otacza i wszystkie instrukcje wewnątrz , ale nie otacza .
Ćwiczenie 2
Zaproponuj modyfikację semantyki, w której deklaracja jest wykonywana ,,równolegle", analogicznie do przypisania równoległego. Przy takiej semantyce kolejność poszczególnych deklaracji powinna być nieistotna.