Semantyka i weryfikacja programów/Ćwiczenia 1: Różnice pomiędzy wersjami
Nie podano opisu zmian |
m Zastępowanie tekstu – „.↵</math>” na „</math>” |
||
(Nie pokazano 12 wersji utworzonych przez 5 użytkowników) | |||
Linia 1: | Linia 1: | ||
== Zawartość == | == Zawartość == | ||
Linia 6: | Linia 4: | ||
== | == Semantyka operacyjna wyrażeń == | ||
{{cwiczenie|1|cw1| | {{cwiczenie|1|cw1| | ||
Rozważmy bardzo prosty język wyrażeń, którego składnia opisana jest | Rozważmy bardzo prosty język wyrażeń, którego składnia opisana jest następującą gramatyką: | ||
następującą gramatyką: | |||
<math> | <math> | ||
Linia 31: | Linia 27: | ||
</math> | </math> | ||
Wynikiem wyrażenienia warunkowego <math>\mathbf{if}\, e_1 \,\mathbf{then}\, e_2 \,\mathbf{else}\, e_3</math> | Wynikiem wyrażenienia warunkowego <math>\mathbf{if}\, e_1 \,\mathbf{then}\, e_2 \,\mathbf{else}\, e_3</math> jest wartość wyrażenia <math>e_2</math>, o ile wyrażenie <math>e_1</math> oblicza się do wartości różnej od zera; w przeciwnym przypadku wynikiem jest wartość wyrażenia <math>e_3</math>. | ||
jest wartość wyrażenia <math>e_2</math>, o ile wyrażenie | |||
<math>e_1</math> oblicza się do wartości różnej od zera; w przeciwnym | |||
przypadku wynikiem jest wartość wyrażenia <math>e_3</math>. | |||
Zaproponuj semantykę operacyjną (małe kroki) dla tego języka. | Zaproponuj semantykę operacyjną (małe kroki) dla tego języka. | ||
}} | }} | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> | |||
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span> | |||
<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"> | |||
Zacznijmy od ustalenia notacji i dziedzin syntaktycznych. | Zacznijmy od ustalenia notacji i dziedzin syntaktycznych. | ||
Niech <math>\mathbf{Num}</math> oznacza zbiór stałych liczbowych, | Niech <math>\mathbf{Num}</math> oznacza zbiór stałych liczbowych, <math>n \in \mathbf{Num} = \{ 0, 1, \ldots \}</math>. | ||
<math>n \in \mathbf{Num} = \{ 0, 1, \ldots \}</math>. | Podobnie, niech <math>\mathbf{Var}</math> oznacza zbiór identyfikatorów, które mogą być nazwami zmiennych; <math>x \in \mathbf{Var}</math>. | ||
Podobnie, niech <math>\mathbf{Var}</math> oznacza zbiór identyfikatorów, które | Wreszcie, niech <math>\mathbf{Exp}</math> oznacza zbiór wyrażeń; <math>e \in \mathbf{Exp}</math>. | ||
mogą być nazwami zmiennych; <math>x \in \mathbf{Var}</math>. | Dla ułatwienia zapisywania reguł zakładamy, że stałe liczbowe są wyrażeniami, czyli <math>\mathbf{Num} \subseteq \mathbf{Exp}</math>. | ||
Wreszcie, niech <math>\mathbf{Exp}</math> oznacza zbiór wyrażeń; | |||
<math>e \in \mathbf{Exp}</math>. | |||
Dla ułatwienia zapisywania reguł zakładamy, | |||
liczbowe są wyrażeniami, czyli <math>\mathbf{Num} \subseteq \mathbf{Exp}</math>. | |||
Będziemy potrzebować zbioru | Będziemy potrzebować zbioru "stanów", opisujących wartości przypisane zmiennym. | ||
przypisane zmiennym. | Najprostszym rozwiązaniem jest przyjąć, że stan to funkcja z <math>\mathbf{Var}</math> do <math>\mathbf{Num}</math>. | ||
Najprostszym rozwiązaniem jest | Oznaczmy przez <math>\mathbf{State}</math> zbiór wszystkich takich funkcji; stany oznaczać będziemy przez <math>s, s_1, s', \ldots \in \mathbf{State}</math>. | ||
z <math>\mathbf{Var}</math> do <math>\mathbf{Num}</math>. | |||
Oznaczmy przez <math>\mathbf{State}</math> zbiór wszystkich takich funkcji; | |||
stany | |||
W naszej semantyce będziemy potrzebowac tranzycji dwóch postaci. | W naszej semantyce będziemy potrzebowac tranzycji dwóch postaci. | ||
Linia 65: | Linia 50: | ||
<math> | <math> | ||
e, s \,\Longrightarrow | e, s \,\Longrightarrow, e', s | ||
</math> | </math> | ||
oznaczające mały krok w trakcie obliczania wyrażenia <math>e</math> | oznaczające mały krok w trakcie obliczania wyrażenia <math>e</math> w stanie <math>s</math>, w wyniku którego <math>e</math> wyewoluowało do <math>e'</math>. | ||
w stanie <math>s</math>, w wyniku którego <math>e</math> wyewoluowało do | Stan nie ulega zmianie podczas obliczania wyrażenia (nie ma tzw. ''efektów ubocznych''), więc to samo <math>s</math> figuruje po lewej i prawej stronie strzałki. | ||
<math>e'</math>. Stan nie ulega | |||
(nie ma tzw. ''efektów ubocznych''), | |||
więc to samo <math>s</math> figuruje po lewej i prawej stronie strzałki. | |||
Po drugie, tranzycje postaci | Po drugie, tranzycje postaci | ||
<math> | <math> | ||
e, s \,\Longrightarrow | e, s \,\Longrightarrow, n | ||
</math> | </math> | ||
będą oznaczaczać, że wyrażenie <math>e</math> jest już policzone, | będą oznaczaczać, że wyrażenie <math>e</math> jest już policzone, a jego wartością jest <math>n</math>. | ||
a jego wartością jest <math>n</math>. | |||
Zatem przyjmijmy, że zbiór konfiguracji to | Zatem przyjmijmy, że zbiór konfiguracji to | ||
Linia 92: | Linia 74: | ||
{{ | {{ | ||
uwaga||uwaga1| | uwaga||uwaga1| | ||
Tak naprawdę | Tak naprawdę, druga postać tranzycji nie jest niezbędna, gdyż moglibyśmy umówić się, że konfiguracje końcowe to <math>\mathbf{Num} \times \mathbf{State}</math>. | ||
niezbędna, gdyż moglibyśmy umówić się, że konfiguracje końcowe | |||
to <math>\mathbf{Num} \times \mathbf{State}</math>. | |||
}} | }} | ||
Najprostsze są tranzycje prowadzące do konfiguracji końcowej: | Najprostsze są tranzycje prowadzące do konfiguracji końcowej: | ||
<math> | <math> | ||
n, s \,\Longrightarrow | n, s \,\Longrightarrow, n | ||
</math> | </math> | ||
Symbol <math>n</math> po lewej stronie to wyrażenie składające się | Symbol <math>n</math> po lewej stronie to wyrażenie składające się ze stałej liczbowej, podczas gdy <math>n</math> po prawej stronie reprezentuje liczbę będącą wartością wyrażenia. | ||
ze stałej liczbowej, podczas gdy <math>n</math> po prawej stronie reprezentuje liczbę | |||
będącą wartością wyrażenia. | |||
Zmienna oblicza się do swojej wartości w bieżącym stanie: | Zmienna oblicza się do swojej wartości w bieżącym stanie: | ||
<math> | <math> | ||
x, s \,\Longrightarrow | x, s \,\Longrightarrow, n, s \quad \mbox{ o ile } s(x) = n</math> | ||
</math> | |||
Teraz zajmiemy się dodawaniem <math>e_1 + e_2</math>. Ponieważ semantyka jest w stylu małych | Teraz zajmiemy się dodawaniem <math>e_1 + e_2</math>. Ponieważ semantyka jest w stylu małych kroków, musimy zdecydować się, czy najpierw obliczyć pierwszy (lewy) składnik <math>e_1</math>, czy drugi? | ||
kroków, musimy zdecydować się czy najpierw obliczyć pierwszy (lewy) składnik | Jeśli wybierzemy lewy (strategia "lewostronna"), otrzymamy regułę: | ||
<math>e_1</math> czy drugi? | |||
Jeśli wybierzemy lewy (strategia | |||
<math> | <math> | ||
e_1 + e_2, s \,\Longrightarrow | e_1 + e_2, s \,\Longrightarrow, e'_1 + e_2, s | ||
\quad \mbox{ o ile } \quad | \quad \mbox{ o ile } \quad | ||
e_1, s \,\Longrightarrow | e_1, s \,\Longrightarrow, e'_1, s</math> | ||
</math> | |||
Reguły tej postaci będziemy zapisywać tak: | Reguły tej postaci będziemy zapisywać tak: | ||
<math> | <math> | ||
\frac{e_1, s \,\Longrightarrow | \frac{e_1, s \,\Longrightarrow, e'_1, s} | ||
{e_1 + e_2, s \,\Longrightarrow | {e_1 + e_2, s \,\Longrightarrow, e'_1 + e_2, s} | ||
</math> | </math> | ||
Linia 150: | Linia 109: | ||
<math> | <math> | ||
\frac{e_2, s \,\Longrightarrow | \frac{e_2, s \,\Longrightarrow, e'_2, s} | ||
{n + e_2, s \,\Longrightarrow | {n + e_2, s \,\Longrightarrow, n + e'_2, s}</math> | ||
</math> | |||
A na końcu dodajemy: | A na końcu dodajemy: | ||
<math> | <math> | ||
n_1 + n_2, s \,\Longrightarrow | n_1 + n_2, s \,\Longrightarrow, n, s \quad \mbox{ o ile } n = n_1 + n_2</math> | ||
</math> | |||
Zwróćmy tutaj uwagę na pewną subtelność, dotyczącą dwóch wystąpień | Zwróćmy tutaj uwagę na pewną subtelność, dotyczącą dwóch wystąpień symbolu <math>+</math>: pierwsze wystąpienie oznacza jedną z konstrukcji składniowych języka, a drugie oznacza operację dodawania w zbiorze <math>\mathbf{Num}</math>. | ||
symbolu <math>+</math>: pierwsze wystąpienie oznacza jedną z konstrukcji składniowych | Pozwalamy sobie na taką kolizję oznaczeń, gdyż nie powinna ona prowadzić do niejednoznaczności. Pamiętajmy, że składnia języka jest składnią abstrakcyjną, więc zamiast <math>e_1 + e_2</math> moglibyśmy równie dobrze pisać np. <math>{\mathrm{add}}(e_1, e_2)</math>, a wtedy reguła wyglądałaby tak: | ||
języka, a drugie oznacza operację dodawania w zbiorze <math>\mathbf{Num}</math>. | |||
Pozwalamy sobie na taką kolizję oznaczeń, gdyż nie powinna ona | |||
prowadzić do niejednoznaczności. Pamiętajmy, że składnia języka jest | |||
składnią abstrakcyjną, więc zamiast <math>e_1 + e_2</math> moglibyśmy równie | |||
dobrze pisać np. <math>{\mathrm{add}}(e_1, e_2)</math> a wtedy reguła | |||
wyglądałaby tak: | |||
<math> | <math> | ||
\mathrm{add}(n_1, n_2), s \,\Longrightarrow | \mathrm{add}(n_1, n_2), s \,\Longrightarrow, n, s \quad \mbox{ o ile } n = n_1 + n_2</math> | ||
</math> | |||
Inną możliwą strategią obliczania <math>e_1 + e_2</math> jest strategia | Inną możliwą strategią obliczania <math>e_1 + e_2</math> jest strategia "prawostronna", którą otrzymujemy zastępując pierwsze dwie z trzech powyższych reguł przez: | ||
powyższych reguł przez: | |||
<math> | <math> | ||
\frac{e_2, s \,\Longrightarrow | \frac{e_2, s \,\Longrightarrow, e'_2, s} | ||
{e_1 + e_2, s \,\Longrightarrow | {e_1 + e_2, s \,\Longrightarrow, e_1 + e'_2} | ||
\quad \quad | \quad \quad | ||
\frac{e_1, s \,\Longrightarrow | \frac{e_1, s \,\Longrightarrow, e'_1, s} | ||
{e_1 + n, s \,\Longrightarrow | {e_1 + n, s \,\Longrightarrow, e'_1 + n, s}</math> | ||
</math> | |||
Ponadto, jeśli przyjmiemy regułę pierwszą (dla <math>e_1</math>), trzecią | Ponadto, jeśli przyjmiemy regułę pierwszą (dla <math>e_1</math>), trzecią i czwartą (dla <math>e_2</math>), otrzymamy strategię "równoległą", polegającą na obliczaniu jednocześnie <math>e_1</math> i <math>e_2</math>: | ||
i czwartą (dla <math>e_2</math>), otrzymamy strategię | |||
<math>e_2</math>: | |||
<math> | <math> | ||
\frac{e_1, s \,\Longrightarrow | \frac{e_1, s \,\Longrightarrow, e'_1, s} | ||
{e_1 + e_2, s \,\Longrightarrow | {e_1 + e_2, s \,\Longrightarrow, e'_1 + e_2, s} | ||
\quad \quad | \quad \quad | ||
\frac{e_2, s \,\Longrightarrow | \frac{e_2, s \,\Longrightarrow, e'_2, s} | ||
{e_1 + e_2, s \,\Longrightarrow | {e_1 + e_2, s \,\Longrightarrow, e_1 + e'_2} | ||
\quad \quad | \quad \quad | ||
n_1 + n_2, s \,\Longrightarrow | n_1 + n_2, s \,\Longrightarrow, n, s \quad \mbox{ o ile } n = n_1 + n_2</math> | ||
</math> | |||
Bardziej precyzyjnie mówiąc, małe kroki obliczające | Bardziej precyzyjnie mówiąc, małe kroki obliczające obydwa podwyrażenia przeplatają się, i to w dowolny sposób. | ||
obydwa podwyrażenia przeplatają się, i to w dowolny sposób. | Ta dowolność prowadzi do ''niedeterminizmu'', czyli do sytuacji, gdy kolejna (następna) konfiguracja nie jest wyznaczona jednoznacznie. | ||
Ta dowolność prowadzi do ''niedeterminizmu'', czyli do sytuacji, gdy | |||
kolejna (następna) konfiguracja nie jest wyznaczona jednoznacznie. | |||
Jest tak, gdyż możemy mieć do wyboru dwie różne tranzycje | Jest tak, gdyż możemy mieć do wyboru dwie różne tranzycje | ||
<math> | <math> | ||
e_1 + e_2, s \,\Longrightarrow | e_1 + e_2, s \,\Longrightarrow, e'_1 + e_2, s | ||
\quad \quad \quad | \quad \quad \quad | ||
e_1 + e_2, s \,\Longrightarrow | e_1 + e_2, s \,\Longrightarrow, e_1 + e'_2, s</math> | ||
</math> | |||
Zauważmy natomiast, że kolejność przeplatania się małych kroków obliczających | Zauważmy natomiast, że kolejność przeplatania się małych kroków obliczających <math>e_1</math> i <math>e_2</math> nie wpływa w tym przypadku na końcową wartość całego wyrażenia. | ||
<math>e_1</math> i <math>e_2</math> nie wpływa w tym przypadku na końcową wartość | |||
całego wyrażenia. | |||
Na koniec reguły dla wyrażenia warunkowego. | Na koniec reguły dla wyrażenia warunkowego. | ||
<math> | <math> | ||
\frac{e_1, s \,\Longrightarrow | \frac{e_1, s \,\Longrightarrow, e'_1, s} | ||
{\mathbf{if}\, e_1 \,\mathbf{then}\, e_2 \,\mathbf{else}\, e_3, s \,\Longrightarrow | {\mathbf{if}\, e_1 \,\mathbf{then}\, e_2 \,\mathbf{else}\, e_3, s \,\Longrightarrow, \mathbf{if}\, e'_1 \,\mathbf{then}\, e_2 \,\mathbf{else}\, e_3, s} | ||
</math> | </math> | ||
<math> | <math> | ||
\mathbf{if}\, n \,\mathbf{then}\, e_2 \,\mathbf{else}\, e_3, s \,\Longrightarrow | \mathbf{if}\, n \,\mathbf{then}\, e_2 \,\mathbf{else}\, e_3, s \,\Longrightarrow, e_2, s \quad \mbox{ o ile } n \neq 0 | ||
</math> | </math> | ||
<math> | <math> | ||
\mathbf{if}\, n \,\mathbf{then}\, e_2 \,\mathbf{else}\, e_3, s \,\Longrightarrow | \mathbf{if}\, n \,\mathbf{then}\, e_2 \,\mathbf{else}\, e_3, s \,\Longrightarrow, e_3, s \quad \mbox{ o ile } n = 0 | ||
</math> | </math> | ||
</div></div> | </div></div> | ||
{{cwiczenie|2|cw2| | {{cwiczenie|2|cw2| | ||
}} | |||
Rozszerzmy język wyrażeń z poprzedniego zadania o jedną konstrukcję | Rozszerzmy język wyrażeń z poprzedniego zadania o jedną konstrukcję | ||
Linia 245: | Linia 184: | ||
</math> | </math> | ||
Wyrażenie <math>\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2</math> zawiera w sobie deklarację | Wyrażenie <math>\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2</math> zawiera w sobie deklarację <math>x = e_1</math>, która stanowi mechanizm wiązania identyfikatorów w naszym języku. | ||
<math>x = e_1</math>, która stanowi mechanizm wiązania | Deklaracja <math>x = e_1</math> wprowadza nową zmienną <math>x</math> oraz przypisuje jej wartość. | ||
identyfikatorów w naszym języku. | Wartość wyrażenia <math>\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2</math> obliczamy następująco: najpierw oblicza się wartość <math>e_1</math>, podstawia ją <font color=red>za</font> zmienną <math>x</math>, a następnie oblicza wyrażenie <math>e_2</math>. | ||
Deklaracja <math>x = e_1</math> wprowadza nową zmienną <math>x</math> | Zakresem zmiennej <math>x</math> jest wyrażenie <math>e_2</math>, czyli wewnątrz <math>e_2</math> można odwoływać się (wielokrotnie) do zmiennej <math>x</math>; | ||
oraz przypisuje jej wartość. | Ogólniej, odwołania do zmiennej w wyrażeniu odnoszą się do "najbliższej" (najbardziej zagnieżdzonej) deklaracji tej zmiennej. | ||
Wartość wyrażenia <math>\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2</math> obliczamy następująco: | Taki mechanizm wiązania identyfikatorów nazywamy ''wiązaniem statycznym''. | ||
najpierw oblicza się wartość <math>e_1</math>, podstawia ją | Przyjmujemy zwykłe (statyczne) reguły przesłaniania zmiennych, np. jeśli w <math>e_2</math> występuje podwyrażenie <math>\mathbf{let}\, x = e \,\mathbf{in}\, e'</math>, to | ||
<math>x</math>, a następnie oblicza wyrażenie <math>e_2</math>. | deklaracja <math>x = e</math> "przesłania" deklarację <math>x = e_1</math> w wyrażeniu <math>e'</math>. | ||
Zakresem zmiennej <math>x</math> jest wyrażenie <math>e_2</math>, czyli | |||
wewnątrz <math>e_2</math> można odwoływać się (wielokrotnie) do zmiennej <math>x</math>; | Zakładamy, że na początku wartości wszystkich zmiennych są ''nieokreślone'', czyli zmienne są niezainicjowane, a odwołanie do niezainicjowanej zmiennej jest uważane za niepoprawne. | ||
Ogólniej, odwołania do zmiennej w wyrażeniu odnoszą się do | |||
(najbardziej zagnieżdzonej) deklaracji tej zmiennej. | |||
Taki mechanizm wiązania identyfikatorów nazywamy ''wiązaniem | |||
statycznym''. | |||
Przyjmujemy zwykłe reguły przesłaniania zmiennych. | |||
{{przyklad||| | |||
<math> | <math> | ||
Linia 275: | Linia 203: | ||
\quad \quad \mapsto \quad \quad \mbox{wynik} = 24 | \quad \quad \mapsto \quad \quad \mbox{wynik} = 24 | ||
</math> | </math> | ||
<math> | <math> | ||
\mathbf{let}\, y = 5 \,\mathbf{in}\, \mathbf{let}\, x = (\, \mathbf{let}\, y = 3 \,\mathbf{in}\, y+y \,) \,\mathbf{in}\, x+y | \mathbf{let}\, y = 5 \,\mathbf{in}\, \mathbf{let}\, x = (\, \mathbf{let}\, y = 3 \,\mathbf{in}\, y+y \,) \,\mathbf{in}\, x+y | ||
\quad \quad \mapsto \quad \quad \mbox{wynik} = 11 | \quad \quad \mapsto \quad \quad \mbox{wynik} = 11 | ||
</math> | </math> | ||
<math> | <math> | ||
\mathbf{let}\, z = 5 \,\mathbf{in}\, x+z | \mathbf{let}\, z = 5 \,\mathbf{in}\, x+z \quad \quad \mapsto \quad \quad \mbox{ brak wyniku, odwołanie do niezainicjowanej zmiennej } x | ||
\quad \quad \mapsto \quad \quad \mbox{brak wyniku | |||
zmiennej} | |||
</math> | </math> | ||
<math> | <math> | ||
\mathbf{let}\, x = 1 \,\mathbf{in}\, \mathbf{let}\, x = x+x \,\mathbf{in}\, x+x | \mathbf{let}\, x = 1 \,\mathbf{in}\, \mathbf{let}\, x = x+x \,\mathbf{in}\, x+x | ||
\quad \quad \mapsto \quad \quad \mbox{wynik} = 4 | \quad \quad \mapsto \quad \quad \mbox{wynik} = 4 | ||
</math> | </math> | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> | |||
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span> | |||
<div class="mw-collapsible-content" style="display:none"> | |||
Podobnie jak poprzednio, stan powinien opisywać wartości przypisane zmiennym. | |||
Tym razem jednak uwzględnimy niezainicjowane zmienne, czyli zmienne bez żadnej wartości. | |||
Podobnie jak poprzednio, | |||
stan powinien opisywać wartości przypisane zmiennym. | |||
Tym razem jednak | |||
uwzględnimy niezainicjowane zmienne, czyli zmienne bez żadnej wartości. | |||
Przyjmijmy zatem, że stan to skończona funkcja częściowa z <math>\mathbf{Var}</math> do <math>\mathbf{Num}</math>. | Przyjmijmy zatem, że stan to skończona funkcja częściowa z <math>\mathbf{Var}</math> do <math>\mathbf{Num}</math>. | ||
Oznaczmy symbolem <math>\mathbf{State}</math> zbiór wszystkich takich funkcji: | Oznaczmy symbolem <math>\mathbf{State}</math> zbiór wszystkich takich funkcji: | ||
Linia 304: | Linia 229: | ||
\mathbf{State} = \mathbf{Var} \to_{\mathrm{fin}} \mathbf{Num} | \mathbf{State} = \mathbf{Var} \to_{\mathrm{fin}} \mathbf{Num} | ||
</math>. | </math>. | ||
Naturalnym stanem początkowym jest stan | Naturalnym stanem początkowym jest stan "pusty", tzn. pusta funkcja częściowa, który będziemy oznaczać symbolem <math>\emptyset</math>. | ||
pusta funkcja częściowa, który będziemy oznaczać symbolem <math>\emptyset</math>. | Wartość wyrażenia <math>e</math> w stanie początkowym wynosi <math>n</math>, o ile zachodzi: | ||
Wartość wyrażenia <math>e</math> w stanie początkowym wynosi <math>n</math> | |||
o ile zachodzi: | |||
<math> | <math> | ||
e, \emptyset \,\Longrightarrow^{*}\, n | e, \emptyset \,\Longrightarrow^{*}\, n</math> | ||
</math> | |||
Będziemy potrzebowac tranzycji dwóch postaci, podobnie jak poprzednio, | Będziemy potrzebowac tranzycji dwóch postaci, podobnie jak poprzednio, ale pierwsza postać będzie nieco ogólniejsza: | ||
ale pierwsza postać będzie | |||
<math> | <math> | ||
e, s \,\Longrightarrow | e, s \,\Longrightarrow, e', s'</math> | ||
</math> | |||
Tranzycja ta oznacza mały krok w trakcie obliczania wyrażenia <math>e</math> | Tranzycja ta oznacza mały krok w trakcie obliczania wyrażenia <math>e</math> w stanie <math>s</math>, w wyniku którego <math>e</math> wyewoluowało do <math>e'</math>, a nowym stanem jest <math>s'</math>. | ||
w stanie <math>s</math>, w wyniku którego <math>e</math> wyewoluowało do | |||
<math>e'</math> a nowym stanem jest <math>s'</math>. | |||
Stan może się teraz zmienić na skutek deklaracji zmiennych. | Stan może się teraz zmienić na skutek deklaracji zmiennych. | ||
Spróbujmy | Spróbujmy rozszerzyć semantykę z poprzedniego zadania. | ||
Ponieważ stan jest funkcją częściową, musimy zmienić niektóre reguły, np. | Ponieważ stan jest funkcją częściową, musimy zmienić niektóre reguły, np. | ||
<math> | <math> | ||
x, s \,\Longrightarrow | x, s \,\Longrightarrow, n, s \quad \mbox{ o ile } s(x) \mbox{ jest określone i } s(x) = n | ||
</math> | </math> | ||
Następnie dodajemy reguły dla wyrażenia | Następnie dodajemy reguły dla wyrażenia <math>\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2</math>. | ||
<math>\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2</math>. | Gdy <math>e_1</math> jest już obliczone, wystarczy reguła: | ||
Gdy <math>e_1</math> jest już | |||
<math> | <math> | ||
\mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow | \mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow, e_2, s[x \mapsto n]</math> | ||
</math> | |||
Notacja <math>s[x \mapsto n]</math> oznacza stan <math>s</math>, który zmodyfikowano | Notacja <math>s[x \mapsto n]</math> oznacza stan <math>s</math>, który zmodyfikowano przypisując zmiennej <math>x</math> wartość <math>n</math>, niezależnie od tego, czy <math>s(x)</math> było określone, czy nie, i pozostawiając niezmienione wartości dla pozostałych zmiennych. | ||
przypisując zmiennej <math>x</math> wartość <math>n</math>, | Formalnie | ||
niezależnie od tego, czy <math>s(x)</math> było określone, czy nie, | |||
i pozostawiając niezmienione wartości dla pozostałych zmiennych. | |||
<math> | <math> | ||
Linia 354: | Linia 267: | ||
</math> | </math> | ||
W szczególności | W szczególności dla <math>y \neq x</math>, <math>s[x \mapsto n](y)</math> jest określone wtedy i tylko wtedy, gdy <math>s(y)</math> jest określone. | ||
dla <math>y \neq x</math>, <math>s[x \mapsto n](y)</math> jest określone wtedy i tylko | |||
wtedy, gdy <math>s(y)</math> jest określone. | |||
Natomiast aby obliczyc <math>e_1</math> potrzebujemy reguły: | Natomiast aby obliczyc <math>e_1</math>, potrzebujemy reguły: | ||
<math> | <math> | ||
\frac{e_1, s \,\Longrightarrow | \frac{e_1, s \,\Longrightarrow, e'_1, s'} | ||
{\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2, s \,\Longrightarrow | {\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2, s \,\Longrightarrow, \mathbf{let}\, x = e'_1 \,\mathbf{in}\, e_2, s'} | ||
</math> | </math> | ||
Zwróćmy uwagę, że stan <math>s'</math> może być różny od <math>s</math>, | Zwróćmy uwagę, że stan <math>s'</math> może być różny od <math>s</math>, np. dlatego, że wewnątrz <math>e_1</math> znajduje się podwyrażenie <math>\mathbf{let}\, y = \ldots</math>. | ||
np. dlatego, że wewnątrz <math>e_1</math> znajduje się podwyrażenie | |||
<math>\mathbf{let}\, y = \ldots</math>. | |||
'''Pytanie:''' czy taka semantyka jest poprawna? | '''Pytanie:''' czy taka semantyka jest poprawna? | ||
Linia 378: | Linia 287: | ||
</math> | </math> | ||
Według naszych intencji to wyrażenie nie ma wartości, gdyż ostatnie | Według naszych intencji to wyrażenie nie ma wartości, gdyż ostatnie odwołanie do <math>z</math> jest błędne. | ||
odwołanie do <math>z</math> jest błędne. | |||
Natomiast według powyższych reguł mamy | Natomiast według powyższych reguł mamy | ||
<math> | <math> | ||
\mathbf{let}\, x = (\mathbf{let}\, z = 4 \,\mathbf{in}\, z+z+z) \,\mathbf{in}\, z, \emptyset \,\Longrightarrow | \mathbf{let}\, x = (\mathbf{let}\, z = 4 \,\mathbf{in}\, z+z+z) \,\mathbf{in}\, z, \emptyset \,\Longrightarrow, | ||
\mathbf{let}\, x = z+z+z \,\mathbf{in}\, z, \emptyset[z \mapsto 4] \,\Longrightarrow | \mathbf{let}\, x = z+z+z \,\mathbf{in}\, z, \emptyset[z \mapsto 4] \,\Longrightarrow, \quad \ldots \quad \,\Longrightarrow, | ||
\mathbf{let}\, x = 12 \,\mathbf{in}\, z, \emptyset[z \mapsto 4] \,\Longrightarrow | \mathbf{let}\, x = 12 \,\mathbf{in}\, z, \emptyset[z \mapsto 4] \,\Longrightarrow, | ||
12, \emptyset[z \mapsto 4] \,\Longrightarrow | 12, \emptyset[z \mapsto 4] \,\Longrightarrow, | ||
12 ! | 12 ! | ||
</math> | </math> | ||
Nasz błąd polega na tym, że po zakończeniu obliczania podwyrażenia | Nasz błąd polega na tym, że po zakończeniu obliczania podwyrażenia <math>\mathbf{let}\, z = 4 \,\mathbf{in}\, z+z+z</math> "zapominamy" przywrócić zmiennej <math>z</math> poprzednią wartość (a właściwie brak wartości w przykładzie powyżej). | ||
<math>\mathbf{let}\, z = 4 \,\mathbf{in}\, z+z+z</math> | |||
poprzednią wartość (a właściwie brak wartości w przykładzie powyżej). | |||
Przedyskutujmy kilka wariantów. | Przedyskutujmy kilka wariantów. | ||
<br> | <br> | ||
Linia 400: | Linia 305: | ||
<br> | <br> | ||
Wygodne i eleganckie rozwiązanie tego problemu jest możliwe, jeśli rozszerzymy składnię naszego języka. | |||
Wygodne i eleganckie rozwiązanie tego problemu jest możliwe, jeśli | |||
rozszerzymy składnię naszego języka. | |||
Intuicyjnie, reguła | Intuicyjnie, reguła | ||
<math> | <math> | ||
\mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow | \mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow, e_2, s[x \mapsto n]</math> | ||
</math> | |||
powinna zostać zastąpiona przez | powinna zostać zastąpiona przez | ||
<math> | <math> | ||
\mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow\, e_2 \,\mathbf{then}\, \mbox{ | \mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow\, e_2 \,\mathbf{then}\, \mbox{przywróć wartość zmiennej x}, s[x \mapsto n]</math> | ||
</math> | |||
czyli potrzebujemy konstrukcji składniowej, która polega na obliczeniu | czyli potrzebujemy konstrukcji składniowej, która polega na obliczeniu wyrażenia <math>e_2</math>, a następnie na przypisaniu zmiennej <math>x</math> danej wartości. | ||
wyrażenia | |||
<math>e_2</math> a następnie na przypisaniu zmiennej <math>x</math> danej wartości. | |||
Rozszerzmy zatem składnię następujaco: | Rozszerzmy zatem składnię następujaco: | ||
Linia 423: | Linia 322: | ||
e \, ::= \,\, | e \, ::= \,\, | ||
\ldots \,\,|\,\, | \ldots \,\,|\,\, | ||
e \,\mathbf{then}\, x := n | e \,\mathbf{then}\, x := n</math> | ||
</math> | |||
Wyrażenie <math>e \,\mathbf{then}\, x:= n</math> jest w pewnym sensie dualne | Wyrażenie <math>e \,\mathbf{then}\, x:= n</math> jest w pewnym sensie dualne do <math>\mathbf{let}\, x = n \,\mathbf{in}\, e</math>, gdyż jedyna (choć niewątpliwie istotna) różnica między nimi to kolejność obliczenia <math>e</math> i przypisania wartości na zmienną <math>x</math>. | ||
do <math>\mathbf{let}\, x = n \,\mathbf{in}\, e</math>, gdyż jedyna ( | |||
między nimi to kolejność obliczenia <math>e</math> i przypisania wartości | |||
na zmienną <math>x</math>. | |||
Oto nowa reguła | Oto nowa reguła | ||
<math> | <math> | ||
\mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow | \mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow, e_2 \,\mathbf{then}\, x := n', s[x \mapsto n] \quad | ||
\mbox{ o ile } s(x) = n' | \mbox{ o ile } s(x) = n'</math> | ||
</math> | |||
Pewna trudność pojawia się w sytuacji, gdy <math>s(x)</math> jest | Pewna trudność pojawia się w sytuacji, gdy <math>s(x)</math> jest nieokreślone, czyli gdy zmienna <math>x</math> jest niezainicjowana -- reguła powyższa nie obejmuje wogóle takiej sytuacji. | ||
nieokreślone, czyli gdy zmienna <math>x</math> jest niezainicjowana -- reguła | Najprostszym sposobem rozwiązania tej trudności jest rozszerzenie konstrukcji <math>e \,\mathbf{then}\, x := n</math>: | ||
powyższa nie obejmuje wogóle takiej sytuacji. | |||
Najprostszym sposobem rozwiązania tej trudności jest rozszerzenie | |||
konstrukcji <math>e \,\mathbf{then}\, x := n</math>: | |||
<math> | <math> | ||
Linia 454: | Linia 345: | ||
<math> | <math> | ||
\mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow | \mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow, e_2 \,\mathbf{then}\, x := \bot, s[x \mapsto n] \quad | ||
\mbox{ o ile } s(x) \, \mbox{ jest nieokreślone} | \mbox{ o ile } s(x) \, \mbox{ jest nieokreślone}</math> | ||
</math> | |||
Rozwiązanie to jest odrobinę nieeleganckie, gdyż prawie identyczne reguły | Rozwiązanie to jest odrobinę nieeleganckie, gdyż prawie identyczne reguły musimy napisać dwukrotnie. | ||
musimy napisać dwukrotnie. | Widać to np. w poniższych regułach, "scalających" semantykę dla <math>e \,\mathbf{then}\, x := n</math> z semantyką pozostałych wyrażeń: | ||
Widać to np. w poniższych regułach, | |||
z semantyką pozostałych wyrażeń: | |||
<math> | <math> | ||
\frac{e, s \,\Longrightarrow | \frac{e, s \,\Longrightarrow, e', s'} | ||
{e \,\mathbf{then}\, x := n, s \,\Longrightarrow | {e \,\mathbf{then}\, x := n, s \,\Longrightarrow, e' \,\mathbf{then}\, x:= n, s'} | ||
</math> | </math> | ||
<math> | <math> | ||
n' \,\mathbf{then}\, x := n, s \,\Longrightarrow | n' \,\mathbf{then}\, x := n, s \,\Longrightarrow, n', s[x \mapsto n] | ||
</math> | </math> | ||
<math> | <math> | ||
n' \,\mathbf{then}\, x := \bot, s \,\Longrightarrow | n' \,\mathbf{then}\, x := \bot, s \,\Longrightarrow, n', s' \quad \mbox{ o ile } s(x) | ||
\mbox{ jest określone i } s' = s \setminus \{ (x, s(x)) \} | \mbox{ jest określone i } s' = s \setminus \{ (x, s(x)) \} | ||
</math> | </math> | ||
<br> | <br> | ||
Linia 482: | Linia 369: | ||
<br> | <br> | ||
Zanim przejdziemy do kolejnego wariantu, zastanówmy się, czy istnieje inny sposób rozwiązania trudności związanej z <math>n = \bot</math>, który pozwalałby uniknąć wprowadzania dodatkowej konstrukcji | |||
Zanim przejdziemy do kolejnego wariantu, zastanówmy się czy | |||
istnieje inny sposób rozwiązania trudności związanej z <math>n = | |||
\bot</math>, który pozwalałby uniknąć wprowadzania dodatkowej konstrukcji | |||
<math>e \,\mathbf{then}\, x := \bot</math>. | <math>e \,\mathbf{then}\, x := \bot</math>. | ||
Pomysł może polegać na rozszerzeniu | Pomysł może polegać na rozszerzeniu zbioru <math>\mathbf{Num}</math> o dodatkowy element <math>\bot</math>: | ||
zbioru <math>\mathbf{Num}</math> o dodatkowy element <math>\bot</math>: | |||
<math> | <math> | ||
Linia 495: | Linia 378: | ||
Wtedy nie musimy pisać dwóch bardzo podobnych wariantów reguł. | Wtedy nie musimy pisać dwóch bardzo podobnych wariantów reguł. | ||
Dodatkowo, w tym rozwiązaniu warto poczynić umowę, że | Dodatkowo, w tym rozwiązaniu warto poczynić umowę, że <math>s(x) = \bot</math> reprezentuje brak wartości zmiennej <math>x</math>. | ||
<math>s(x) = \bot</math> reprezentuje brak wartości zmiennej <math>x</math>. | Wtedy stany są funkcjami całkowitymi z <math>\mathbf{Var}</math> w <math>\mathbf{Num}</math>, przyjmującymi wartość różną od <math>\bot</math> tylko dla skończenie wielu elementów. | ||
Wtedy stany są funkcjami całkowitymi z <math>\mathbf{Var}</math> w <math>\mathbf{Num}</math> | Pewnym mankamentem jest to, że teraz <math>n = \bot</math> może pojawiać się w wyrażeniach podobnie jak stałe. | ||
przyjmującymi wartość różną od <math>\bot</math> tylko dla skończenie | Tym niemniej nie musimy adaptować reguł dla stałych tak, aby radziły one sobie z <math>n = \bot</math>, ponieważ wyrażenia zawierające <math>\bot</math> możemy również uważać za roszerzenie składni. | ||
wielu elementów. | |||
Pewnym mankamentem jest to, że teraz | |||
<math>n = \bot</math> może pojawiać | |||
Tym niemniej nie musimy adaptować reguł dla stałych tak, aby radziły | |||
one sobie z <math>n = \bot</math>, ponieważ wyrażenia zawierające | |||
<math>\bot</math> możemy również uważać za roszerzenie składni. | |||
Jeśli jednak dopuścimy symbol <math>\bot</math> w wyrażeniach, to możemy | Jeśli jednak dopuścimy symbol <math>\bot</math> w wyrażeniach, to możemy elegancko wybrnąć z sytuacji, rozszerzając operacje arytmetyczne na zbiór <math>\mathbf{Num} \cup \{ \bot \}</math> tak, aby zachowywały one nieokreśloność: | ||
elegancko wybrnąć z sytuacji, rozszerzając operacje arytmetyczne | |||
na zbiór <math>\mathbf{Num} \cup \{ \bot \}</math> tak, aby zachowywały one | |||
nieokreśloność: | |||
<math> | <math> | ||
n + \bot = \bot + n = \bot | n + \bot = \bot + n = \bot</math> | ||
</math> | |||
Trzeba jednak w takim razie zadbać o to, aby wyrażenie <math>\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2</math> obliczało się normalnie tylko wtedy, gdy wartość wyrażenia <math>e_1</math> jest różna od <math>\bot</math>. | |||
, | |||
<br> | <br> | ||
'''Wariant 3''' | '''Wariant 3''' | ||
Linia 528: | Linia 398: | ||
<math> | <math> | ||
e, s \,\Longrightarrow | e, s \,\Longrightarrow, e', s' | ||
</math> | </math> | ||
pozwalającej na zmianę stanu podczas obliczania wyrażenia. | pozwalającej na zmianę stanu podczas obliczania wyrażenia. | ||
Czy faktycznie był to dobry pomysł? Czy moglibyśmy poradzić sobie | Czy faktycznie był to dobry pomysł? Czy moglibyśmy poradzić sobie przy pomocy tranzycji postaci | ||
przy pomocy tranzycji postaci | |||
<math> | <math> | ||
e, s \,\Longrightarrow | e, s \,\Longrightarrow, e', s ? | ||
</math> | </math> | ||
Spróbujmy! Oto nowa wersja jednej z reguł dla <math>\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2</math> | Spróbujmy! Oto nowa wersja jednej z reguł dla <math>\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2</math>, dotycząca kroku wewnątrz <math>e_1</math>: | ||
dotycząca kroku wewnątrz <math>e_1</math>: | |||
<math> | <math> | ||
\frac{e_1, s \,\Longrightarrow | \frac{e_1, s \,\Longrightarrow, e'_1, s} | ||
{\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2, s \,\Longrightarrow | {\mathbf{let}\, x = e_1 \,\mathbf{in}\, e_2, s \,\Longrightarrow, \mathbf{let}\, x = e'_1 \,\mathbf{in}\, e_2, s} | ||
</math> | </math> | ||
Dotychczas nie ma problemu: podwyrażenie <math>e_1</math> jest | Dotychczas nie ma problemu: podwyrażenie <math>e_1</math> jest prawidłowo obliczane w stanie <math>s</math>. Trudność pojawi się, gdy | ||
prawidłowo obliczane w stanie <math>s</math>. Trudność pojawi się, gdy | |||
zakończymy obliczanie <math>e_1</math> i przejdziemy do <math>e_2</math>. | zakończymy obliczanie <math>e_1</math> i przejdziemy do <math>e_2</math>. | ||
Oto możliwa reguła: | Oto możliwa reguła: | ||
<math> | <math> | ||
\frac{e, s[x \mapsto n] \,\Longrightarrow | \frac{e, s[x \mapsto n] \,\Longrightarrow, e', s[x \mapsto n] } | ||
{\mathbf{let}\, x = n \,\mathbf{in}\, e, s \,\Longrightarrow | {\mathbf{let}\, x = n \,\mathbf{in}\, e, s \,\Longrightarrow, \mathbf{let}\, x = n \,\mathbf{in}\, e', s}</math> | ||
</math> | |||
Okazuje się, że wszystko jest w porządku. Wyrażenie <math>e</math> | Okazuje się, że wszystko jest w porządku. Wyrażenie <math>e</math> obliczamy w prawidłowym stanie, tzn. z wartością <math>n</math> przypisaną zmiennej <math>x</math>. | ||
obliczamy w prawidłowym stanie, tzn. z wartością <math>n</math> | Mały krok w <math>e</math> daje przyczynek do małego kroku w całym wyrażeniu, a przy tym stan pozostaje niezmieniony. | ||
przypisaną zmiennej <math>x</math>. | Przy tym wogóle nie potrzebujemy przywracać poprzedniej wartości zmiennej <math>x</math>, ponieważ <math>x</math> zyskuje nową wartość "tylko" na potrzeby obliczania podwyrażenia <math>e</math>! | ||
Mały krok w <math>e</math> daje przyczynek do małego kroku w całym | Można na to również spojrzeć inaczej: informacja o nowej wartości <math>n</math> dla zmiennej <math>x</math> nie jest jawnie dodawana do stanu <math>s</math>, ale jest przechowywana w składni wyrażenia <math>\mathbf{let}\, x = n \,\mathbf{in}\, \ldots</math> jako deklaracja <math>x = n</math>. | ||
wyrażeniu, a przy tym stan pozostaje niezmieniony. | Na końcu musimy oczywiście pozbyć się tej deklaracji za pomocą następującej tranzycji: | ||
Przy tym wogóle nie potrzebujemy przywracać poprzedniej wartości | |||
zmiennej <math>x</math>, ponieważ <math>x</math> zyskuje nową wartość | |||
Można na to również spojrzeć inaczej: informacja o nowej wartości | |||
<math>n</math> dla zmiennej <math>x</math> nie jest jawnie dodawana do stanu | |||
<math>s</math>, ale jest przechowywana w składni wyrażenia | |||
<math>\mathbf{let}\, x = n \,\mathbf{in}\, \ldots</math> jako deklaracja <math>x = n</math>. | |||
Na końcu musimy oczywiście pozbyć się tej deklaracji za pomocą | |||
następującej tranzycji: | |||
<math> | <math> | ||
\mathbf{let}\, x = n \,\mathbf{in}\, n', s \,\Longrightarrow | \mathbf{let}\, x = n \,\mathbf{in}\, n', s \,\Longrightarrow, n', s | ||
</math> | </math> | ||
Podsumujmy. Okazuje się, że rozwiązanie nie było wcale łatwe, | Podsumujmy. Okazuje się, że rozwiązanie nie było wcale łatwe, nawet dla tak prościutkiego języka. W przyszłości przekonamy się, że łatwiej jest poradzić sobie z zagadnieniem wiązania identyfikatorów w semantyce naturalnej (duże kroki). | ||
nawet dla tak prościutkiego języka. W przyszłości przekonamy się, | W wariancie 1 i 2 wprowadziliśmy do języka dodatkowe elementy, tak by łatwiej było pisać reguły. W przyszłości będziemy czasem stosować takie podejście. | ||
że łatwiej jest poradzić sobie z zagadnieniem wiązania identyfikatorów | |||
w semantyce naturalnej (duże kroki). | |||
W wariancie 1 i 2 wprowadziliśmy do języka dodatkowe elementy, tak | |||
by łatwiej było pisać reguły. W przyszłości będziemy czasem stosować | |||
takie podejście. | |||
Niekiedy jednak rozszerzanie języka będzie zabronione. | Niekiedy jednak rozszerzanie języka będzie zabronione. | ||
</div></div> | </div></div> | ||
== Zadania domowe == | == Zadania domowe == | ||
{{cwiczenie|1|cw1.dom| | |||
Zapisz wariant 2 semantyki z poprzedniego zadania. | Zapisz wariant 2 semantyki z poprzedniego zadania. | ||
}} | |||
{{cwiczenie|2|cw2.dom| | |||
Dotychczas wystąpienie błędu podczas obliczania wyrażenia, | Dotychczas wystąpienie błędu podczas obliczania wyrażenia, np. odwołanie do niezainicjowanej zmiennej, powodowało, że wyrażenie nie posiadało wartości (nie było ciągu tranzycji prowadzących do konfiguracji końcowej). | ||
np. odwołanie do niezainicjowanej zmiennej, powodowało, że | Zmodyfikuj którąś z semantyk z poprzednich zadań tak, aby błąd był komunikowany jako jedna z konfiguracji końcowych. | ||
wyrażenie nie posiadało wartości (nie było ciągu tranzycji | To znaczy: jeśli obliczenie wyrażenia <math>e</math> w stanie <math>s</math> jest niemożliwe bo wystąpił błąd, to | ||
prowadzących do konfiguracji końcowej). Zmodyfikuj którąś z semantyk | |||
z poprzednich zadań tak, aby błąd był komunikowany | |||
jako jedna z konfiguracji końcowych. To znaczy: jeśli obliczenie | |||
wyrażenia <math>e</math> w stanie <math>s</math> jest niemożliwe bo wystąpił | |||
błąd, to | |||
<math> | <math> | ||
e, s \,\Longrightarrow^{*}\, \mathtt{Blad} | e, s \,\Longrightarrow^{*}\, \mathtt{Blad}</math> | ||
</math> | }} | ||
{{cwiczenie|3|cw3.dom| | |||
Rozważ rozszerzenie języka wyrażeń o wyrażenia boolowskie: | Rozważ rozszerzenie języka wyrażeń o wyrażenia boolowskie: | ||
Linia 646: | Linia 492: | ||
Zaproponuj semantykę małych kroków dla tego języka. | Zaproponuj semantykę małych kroków dla tego języka. | ||
Rozważ różne strategie obliczania wyrażeń boolowskich, oraz podejście leniwe. | Rozważ różne strategie obliczania wyrażeń boolowskich, oraz podejście leniwe. | ||
Na przykład w strategii lewostronnej dla <math>b_1 \land b_2</math>, | Na przykład w strategii lewostronnej dla <math>b_1 \land b_2</math>, gdy <math>b_1</math> zostało obliczone do <math>\mathbf{false}</math>, w podejściu leniwym nie ma wogóle potrzeby obliczania <math>b_2</math>. | ||
gdy <math>b_1</math> zostało obliczone do <math>\mathbf{false}</math>, nie ma wogóle potrzeby | }} | ||
obliczania <math>b_2</math>. |
Aktualna wersja na dzień 21:29, 11 wrz 2023
Zawartość
Tematem tych zajęć jest semantyka operacyjna wyrażeń (małe kroki).
Semantyka operacyjna wyrażeń
Ćwiczenie 1
Rozważmy bardzo prosty język wyrażeń, którego składnia opisana jest następującą gramatyką:
Wynikiem wyrażenienia warunkowego jest wartość wyrażenia , o ile wyrażenie oblicza się do wartości różnej od zera; w przeciwnym przypadku wynikiem jest wartość wyrażenia .
Zaproponuj semantykę operacyjną (małe kroki) dla tego języka.
Rozwiązanie
Ćwiczenie 2
Rozszerzmy język wyrażeń z poprzedniego zadania o jedną konstrukcję
Wyrażenie zawiera w sobie deklarację , która stanowi mechanizm wiązania identyfikatorów w naszym języku. Deklaracja wprowadza nową zmienną oraz przypisuje jej wartość. Wartość wyrażenia obliczamy następująco: najpierw oblicza się wartość , podstawia ją za zmienną , a następnie oblicza wyrażenie . Zakresem zmiennej jest wyrażenie , czyli wewnątrz można odwoływać się (wielokrotnie) do zmiennej ; Ogólniej, odwołania do zmiennej w wyrażeniu odnoszą się do "najbliższej" (najbardziej zagnieżdzonej) deklaracji tej zmiennej. Taki mechanizm wiązania identyfikatorów nazywamy wiązaniem statycznym. Przyjmujemy zwykłe (statyczne) reguły przesłaniania zmiennych, np. jeśli w występuje podwyrażenie , to deklaracja "przesłania" deklarację w wyrażeniu .
Zakładamy, że na początku wartości wszystkich zmiennych są nieokreślone, czyli zmienne są niezainicjowane, a odwołanie do niezainicjowanej zmiennej jest uważane za niepoprawne.
{{przyklad|||
Rozwiązanie
Zadania domowe
Ćwiczenie 1
Zapisz wariant 2 semantyki z poprzedniego zadania.
Ćwiczenie 2
Dotychczas wystąpienie błędu podczas obliczania wyrażenia, np. odwołanie do niezainicjowanej zmiennej, powodowało, że wyrażenie nie posiadało wartości (nie było ciągu tranzycji prowadzących do konfiguracji końcowej). Zmodyfikuj którąś z semantyk z poprzednich zadań tak, aby błąd był komunikowany jako jedna z konfiguracji końcowych. To znaczy: jeśli obliczenie wyrażenia w stanie jest niemożliwe bo wystąpił błąd, to
Ćwiczenie 3
Rozważ rozszerzenie języka wyrażeń o wyrażenia boolowskie:
Zaproponuj semantykę małych kroków dla tego języka. Rozważ różne strategie obliczania wyrażeń boolowskich, oraz podejście leniwe. Na przykład w strategii lewostronnej dla , gdy zostało obliczone do , w podejściu leniwym nie ma wogóle potrzeby obliczania .