Semantyka i weryfikacja programów/Ćwiczenia 3: Różnice pomiędzy wersjami
Nie podano opisu zmian |
Nie podano opisu zmian |
||
Linia 1: | Linia 1: | ||
== | == Zawartość == | ||
Kończymy semantykę małych kroków i rozpoczynamy semantykę naturalną (duże kroki). | |||
Uzupełnimy semantykę naturalną języka Tiny o semantykę naturalną | |||
wyrażeń boolowskich i arytmetycznych oraz semantykę błędów wykonania. | |||
Wreszcie dodamy | Wreszcie dodamy nową instrukcję pętli <math>\mathbf{loop}\,</math> pozwalającą na | ||
niestrukturalne przerwanie lub wznowienie iteracji (instrukcje <math>\mathbf{exit}</math> i <math>\mathbf{continue}</math>). | niestrukturalne przerwanie lub wznowienie iteracji (instrukcje <math>\mathbf{exit}</math> i <math>\mathbf{continue}</math>). | ||
== Rozszerzenia semantyki | == Rozszerzenia semantyki języka Tiny == | ||
{{cwiczenie|1|cw1| | {{cwiczenie|1|cw1| | ||
Zdefiniuj znaczenie | Zdefiniuj znaczenie wyrażeń boolowskich i arytmetycznych | ||
w | w języku Tiny, w stylu dużych kroków (semantyka naturalna). | ||
}} | }} | ||
Linia 24: | Linia 24: | ||
<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"> | ||
Przypomnijmy | Przypomnijmy składnię wyrażeń boolowskich i arytmetycznych | ||
jezyka Tiny: | jezyka Tiny: | ||
Linia 56: | Linia 56: | ||
</math> | </math> | ||
Chcemy, aby tranzycje | Chcemy, aby tranzycje wyrażeń wyglądały następująco: | ||
<math> | <math> | ||
Linia 64: | Linia 64: | ||
</math> | </math> | ||
gdzie <math>s \in \mathbf{State}</math>, <math>n \in</math> jest | gdzie <math>s \in \mathbf{State}</math>, <math>n \in</math> jest liczbą całkowitą, | ||
<math>n \in \mathbf{Num}</math>, a <math>l \in \mathbf{Bool} = \{ \mathbf{true}, \mathbf{false} \}</math>. | <math>n \in \mathbf{Num}</math>, a <math>l \in \mathbf{Bool} = \{ \mathbf{true}, \mathbf{false} \}</math>. | ||
Tranzycja taka oznacza, | Tranzycja taka oznacza, że wyrażenie <math>e</math> w stanie <math>s</math> wylicza się do | ||
wartości <math>n</math> oraz analogicznie, wyrażenie logiczne <math>b</math> w | |||
stanie <math>s</math> wylicza | stanie <math>s</math> wylicza się do <math>l</math>. | ||
Zatem zbiór konfiguracji dla semantyki | Zatem zbiór konfiguracji dla semantyki całego języka Tiny to znów | ||
<math> | <math> | ||
Linia 75: | Linia 75: | ||
\mathbf{Num} \, \cup \, \mathbf{Bool} \, \cup \, \mathbf{State} | \mathbf{Num} \, \cup \, \mathbf{Bool} \, \cup \, \mathbf{State} | ||
</math> | </math> | ||
a konfiguracje | a konfiguracje końcowe to <math>\mathbf{State}</math> tak jak poprzednio. | ||
Tranzycje dla instrukcji | Tranzycje dla instrukcji pozostają zasadniczo bez zmian, z tym, | ||
że odwołania do funkcji semantycznych dla wyrażen zastępujemy | |||
przez odpowiednie tranzycje. | przez odpowiednie tranzycje. | ||
Np. dla instrukcji | Np. dla instrukcji pętli będziemy mieć następujące reguły: | ||
<math> | <math> | ||
Linia 92: | Linia 92: | ||
Podobnie dla instrukcji warunkowej. | Podobnie dla instrukcji warunkowej. | ||
Teraz zajmiemy | Teraz zajmiemy się tranzycjami dla wyrażeń. | ||
Zacznijmy od stalych arytmetycznych i boolowskich: | Zacznijmy od stalych arytmetycznych i boolowskich: | ||
Linia 101: | Linia 101: | ||
</math> | </math> | ||
Operatory arytmetyczne definiujemy | Operatory arytmetyczne definiujemy następująco: | ||
<math> | <math> | ||
Linia 110: | Linia 110: | ||
</math> | </math> | ||
Czyli aby | Czyli aby obliczyć sumę <math>e_1 + e_2</math> w stanie | ||
<math>s</math>, trzeba najpierw | <math>s</math>, trzeba najpierw obliczyć <math>e_1</math> i | ||
<math>e_2</math> w tym stanie, a | <math>e_2</math> w tym stanie, a następnie dodać obliczone wartości. | ||
Zauważmy, że nie specyfikujemy kolejności, w jakiej mają się | |||
obliczać obydwa podwyrażenia. | |||
I | I choć tutaj nie ma to żadnego znaczenia, w przyszłości | ||
będzie inaczej, gdy jezyk będzie umożliwiał efekty uboczne | |||
podczas obliczania | podczas obliczania wyrażeń. | ||
Podobne | Podobne reguły można napisać dla pozostałych operacji | ||
arytmnetycznych, oraz dla spójników logicznych: | arytmnetycznych, oraz dla spójników logicznych: | ||
Linia 129: | Linia 129: | ||
</math> | </math> | ||
Oto inny wariant, | Oto inny wariant, reguły lewo-stronne (ponieważ rozpoczynamy od ''lewego'' koniunktu): | ||
<math> | <math> | ||
Linia 142: | Linia 142: | ||
Inny wariant to wariant prawo-stronny ( | Inny wariant to wariant prawo-stronny ( | ||
najpierw <math>b_2</math>, potem <math>b_1</math>). | najpierw <math>b_2</math>, potem <math>b_1</math>). | ||
Wreszcie | Wreszcie rozważmy kombinację obydwu semantyk (reguły ''równoległe''): | ||
<math> | <math> | ||
Linia 152: | Linia 152: | ||
</math> | </math> | ||
Czyli | Czyli jeśli którekolwiek z podwyrażeń daje wynik <math>\mathbf{false}</math>, | ||
to taki wynik zyskuje | to taki wynik zyskuje całe wyrażenie. | ||
Dodatkowo potrzebujemy jeszcze | Dodatkowo potrzebujemy jeszcze reguły: | ||
<math> | <math> | ||
Linia 162: | Linia 162: | ||
</math> | </math> | ||
W naszym prostym | W naszym prostym języku wszystkie cztery warianty | ||
są równoważne. | |||
Reguły dla pozostałych spójników logicznych, dla | |||
negacji oraz dla instrukcji przypisania pozostawiamy jako proste | negacji oraz dla instrukcji przypisania pozostawiamy jako proste | ||
ćwiczenie. | |||
</div></div> | </div></div> | ||
Linia 174: | Linia 174: | ||
{{cwiczenie|2|cw2| | {{cwiczenie|2|cw2| | ||
Rozważ dodatkowo operację dzielenia: | |||
<math> | <math> | ||
Linia 181: | Linia 181: | ||
</math> | </math> | ||
i rozszerz | i rozszerz semantykę z poprzedniego zadania. | ||
Dzielenie przez zero jest | Dzielenie przez zero jest błądem i kończy natychmiast wykonanie | ||
programu. | programu. | ||
Oprócz stanu wynikiem programu powinna byc informacja o | Oprócz stanu wynikiem programu powinna byc informacja o błędzie, | ||
jeśli błąd wystąpił. | |||
}} | }} | ||
Linia 193: | Linia 193: | ||
<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"> | ||
Dopóki nie | Dopóki nie wystąpi błąd dzielenia przez zero, semantyka programu | ||
powinna | powinna być identyczna jak w poprzednim zadaniu. Zatem pozostawiamy | ||
wszystkie | wszystkie reguły z poprzedniego zadania. | ||
Dodatkowo potrzebujemy | Dodatkowo potrzebujemy reguł, które opiszą | ||
* kiedy powstaje | * kiedy powstaje błąd oraz | ||
* jak zachowuje | * jak zachowuje się program po wystąpieniu błędu | ||
Zaczynamy od pierwszego punktu. | Zaczynamy od pierwszego punktu. | ||
W tym celu dodajemy do konfiguracji | W tym celu dodajemy do konfiguracji jedną konfigurację końcową | ||
<math>\mathtt{Blad}</math>. | <math>\mathtt{Blad}</math>. | ||
Reguła opisująca powstanie błędu może wyglądać np. tak | |||
( | (duże i małe kroki): | ||
<math> | <math> | ||
Linia 214: | Linia 214: | ||
</math> | </math> | ||
Pomijamy tutaj | Pomijamy tutaj reguły dla przypadku, gdy <math>e_2</math> | ||
oblicza | oblicza się do wartości różnej od zera. | ||
Ponadto dla | Ponadto dla czytelności przyjęliśmy, że wynikiem tranzycji jest | ||
wyłącznie informacja o błędzie, a stan jest zapominany. | |||
Bez trudu | Bez trudu możnaby wszystkie reguły (zarówno te powyżej jak i te | ||
poniżej) zmodyfikować tak, by wraz z | |||
informacją o błędzie zwracany był też stan, w którym błąd się | |||
pojawił. Np. ostatnia reguła wyglądałaby następująco: | |||
<math> | <math> | ||
Linia 230: | Linia 230: | ||
</math> | </math> | ||
i zamiast | i zamiast pojedyńczej konfiguracji końcowej <math>\mathtt{Blad}</math> potrzebowalibyśmy | ||
oczywiście całego zbioru <math>\{ \mathtt{Blad} \} \times \mathbf{State}</math>. | |||
Przejdźmy teraz do drugiego punktu. Potrzebujemy dodatkowych reguł, | |||
które | które zagwarantują, że błąd, raz pojawiwszy się, propaguje się przez | ||
wszystkie konstrukcje | wszystkie konstrukcje składniowe, a normalne obliczenie wyrażenia | ||
jest wstrzymame. | jest wstrzymame. | ||
Linia 246: | Linia 246: | ||
</math> | </math> | ||
Następnie, błąd w wyrażeniu powinien wstrzymać normalne wykonanie instrukcji: | |||
<math> | <math> | ||
Linia 263: | Linia 263: | ||
</math> | </math> | ||
I wreszcie | I wreszcie błąd powinien propagować się do kolejnych instrukcji: | ||
<math> | <math> | ||
Linia 287: | Linia 287: | ||
</math> | </math> | ||
Zwróćmy szczególnie uwagę na ostatnią regułę dla pętli <math>\mathbf{while}\,</math>: | |||
wyraża ona przypadek, gdy błąd został wygenerowany | |||
nie w pierwszym obiegu | nie w pierwszym obiegu pętli, ale w którymś z kolejnych. | ||
Musieliśmy rozważyc również ten przypadek, ponieważ wybraliśmy | |||
podejście dużych kroków; w podejściu małych kroków nie byłoby | |||
to zapewne konieczne. | to zapewne konieczne. | ||
Linia 300: | Linia 300: | ||
{{cwiczenie|3|cw3| | {{cwiczenie|3|cw3| | ||
Rozszerzmy | Rozszerzmy język Tiny o następującą instrukcję pętli: | ||
<math> | <math> | ||
Linia 309: | Linia 309: | ||
</math> | </math> | ||
<math>\mathbf{loop}\, I</math> to instrukcja | <math>\mathbf{loop}\, I</math> to instrukcja pętli, <math>I</math> stanowi instrukcję wewnętrzną. | ||
Instrukcja <math>\mathbf{exit}</math> wychodzi z | Instrukcja <math>\mathbf{exit}</math> wychodzi z nabliższej otaczającej pętli <math>\mathbf{loop}\,</math> | ||
i kontynuuje wykonanie programu od pierwszej instrukcji za | i kontynuuje wykonanie programu od pierwszej instrukcji za tą pętlą. | ||
Instrukcja <math>\mathbf{continue}</math> powraca na | Instrukcja <math>\mathbf{continue}</math> powraca na początek instrukcji wewnętrznej | ||
najbliższej otaczającej pętli <math>\mathbf{loop}\,</math>. | |||
Pozważ zarówno semantykę naturalną jak i semantykę małych kroków. | |||
}} | }} | ||
Linia 323: | Linia 323: | ||
<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"> | ||
Dodamy do | Dodamy do reguł semantyki naturalnej dla języka Tiny | ||
kilka nowych | kilka nowych reguł opisujących nową konstrukcję języka | ||
i jej ,, | i jej ,,interakcję\'' z pozostałymi konstrukcjami. | ||
Pomysł polega na dodaniu specjalnych konfiguracji zawierających | |||
informację o tym, że została wykonana instrukcja <math>\mathbf{exit}</math> | |||
lub <math>\mathbf{continue}</math>. Oto odpowiednie | lub <math>\mathbf{continue}</math>. Oto odpowiednie reguły: | ||
<math> | <math> | ||
\mathbf{exit}, s \,\longrightarrow\, s, \mbox{ | \mathbf{exit}, s \,\longrightarrow\, s, \mbox{było-}\mathbf{exit} | ||
\quad \quad | \quad \quad | ||
\mathbf{continue}, s \,\longrightarrow\, s, \mbox{ | \mathbf{continue}, s \,\longrightarrow\, s, \mbox{było-}\mathbf{continue}. | ||
</math> | </math> | ||
Czyli instrukcje <math>\mathbf{exit}</math> i <math>\mathbf{continue}</math> nie | Czyli instrukcje <math>\mathbf{exit}</math> i <math>\mathbf{continue}</math> nie modyfikują | ||
stanu <math>s</math>, ale | stanu <math>s</math>, ale zostawiają po sobie ,,ślad". | ||
Zauważmy, że taki ślad zostaje pozostawiony tylko wtedy, jesli | |||
nie | nie było dotychczas innego śladu, to znaczy jeśli <math>\mathbf{exit}</math> | ||
(lub <math>\mathbf{continue}</math>) | (lub <math>\mathbf{continue}</math>) zostało wykonane w zwykłym stanie <math>s</math>. | ||
Oczywiście poszerzamy odpowiednio zbiór konfiguracji o: | |||
<math> | <math> | ||
\mathbf{State} \times \{ \mbox{ | \mathbf{State} \times \{ \mbox{było-}\mathbf{exit}, \mbox{było-}\mathbf{continue} \} | ||
</math> | </math> | ||
'''Pytanie''': jakie powinno | '''Pytanie''': jakie powinno być zachowanie <math>\mathbf{exit}</math> i | ||
<math>\mathbf{continue}</math> w konfiguracji <math>s, \mbox{ | <math>\mathbf{continue}</math> w konfiguracji <math>s, \mbox{było-}\mathbf{exit}</math> | ||
lub <math>s, \mbox{ | lub <math>s, \mbox{było-}\mathbf{continue}</math>? | ||
Czy instrukcje <math>\mathbf{exit}</math> i <math>\mathbf{continue}</math> | Czy instrukcje <math>\mathbf{exit}</math> i <math>\mathbf{continue}</math> | ||
będą faktycznie wykonywane w takich konfiguracjach? | |||
Zapiszmy teraz jak inne instrukcje | Zapiszmy teraz jak inne instrukcje korzystają z dodatkowej informacji | ||
( | (śladu) zawartej w konfiguracjach. | ||
Oczywiście beneficjentem tej informacji jest instrukcji <math>\mathbf{loop}\,</math>: | |||
<math> | <math> | ||
\frac{I, s \,\longrightarrow\, s', \mbox{ | \frac{I, s \,\longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
{\mathbf{loop}\, I, s \,\longrightarrow\, s'} | {\mathbf{loop}\, I, s \,\longrightarrow\, s'} | ||
\quad \quad | \quad \quad | ||
\frac{I, s \,\longrightarrow\, s', \mbox{ | \frac{I, s \,\longrightarrow\, s', \mbox{było-}\mathbf{continue} \quad \quad \mathbf{loop}\, I, s' \,\longrightarrow\, s''} | ||
{\mathbf{loop}\, I, s \,\longrightarrow\, s''} | {\mathbf{loop}\, I, s \,\longrightarrow\, s''} | ||
</math> | </math> | ||
Czyli w | Czyli w zależności od tego, jaki ślad został zarejestrowany | ||
podczas wykonania <math>I</math>, albo | podczas wykonania <math>I</math>, albo kończymy wykonanie pętli <math>\mathbf{loop}\,</math>, | ||
albo rozpoczynamy | albo rozpoczynamy kolejną iterację. | ||
Zauważmy, że stan <math>s'</math> może być różny od <math>s</math>, ponieważ | |||
zanim | zanim wykonała się ktoraś z instrukcji <math>\mathbf{exit}</math> lub | ||
<math>\mathbf{continue}</math>, | <math>\mathbf{continue}</math>, mogły zostać zmienione wartości niektórych zmiennych. | ||
Oczywiście, jeśli instrukcja wewnętrzna <math>I</math> zakończyła się | |||
''normalnie'', kontynuujemy wykonanie | ''normalnie'', kontynuujemy wykonanie pętli podobnie jak w przypadku | ||
wywołania <math>\mathbf{continue}</math>: | |||
<math> | <math> | ||
Linia 382: | Linia 382: | ||
</math> | </math> | ||
'''Pytanie:''' czy potrzebujemy dodatkowo | '''Pytanie:''' czy potrzebujemy dodatkowo reguł postaci: | ||
<math> | <math> | ||
\frac{I, s \,\longrightarrow\, s' \quad \quad \mathbf{loop}\, I, s' \,\longrightarrow\, s'', \mbox{ | \frac{I, s \,\longrightarrow\, s' \quad \quad \mathbf{loop}\, I, s' \,\longrightarrow\, s'', \mbox{było-}\mathbf{exit}} | ||
{\mathbf{loop}\, I, s \,\longrightarrow\, s'', \mbox{ | {\mathbf{loop}\, I, s \,\longrightarrow\, s'', \mbox{było-}\mathbf{exit}} | ||
\quad \quad | \quad \quad | ||
\frac{I, s \,\longrightarrow\, s', \mbox{ | \frac{I, s \,\longrightarrow\, s', \mbox{było-}\mathbf{continue} \quad \quad \mathbf{loop}\, I, s' \,\longrightarrow\, s'', \mbox{było-}\mathbf{exit}} | ||
{\mathbf{loop}\, I, s \,\longrightarrow\, s'', \mbox{ | {\mathbf{loop}\, I, s \,\longrightarrow\, s'', \mbox{było-}\mathbf{exit}} | ||
</math> | </math> | ||
Okazuje | Okazuje się że nie, ponieważ ślad powinien zostać zawsze | ||
,,wymazany" przez | ,,wymazany" przez pętlę <math>\mathbf{loop}\,</math>. | ||
<!-- | <!-- | ||
Poza tym | Poza tym reguły te wykraczałyby poza klasyczna podejście semantyki | ||
naturalnej, w którym zwykle | naturalnej, w którym zwykle bezpośrednio definiujemy | ||
tranzycje postaci <math>I, s \,\longrightarrow\, s'</math>. | tranzycje postaci <math>I, s \,\longrightarrow\, s'</math>. | ||
--> | --> | ||
Teraz musimy | Teraz musimy określić zachowanie wszystkich instrukcji w sytuacji, | ||
gdy | gdy bieżąca konfiguracja zawiera już ślad. Zasadniczo, powinniśmy | ||
zaniechać wykonania instrukcji (w przypadku pętli, powinniśmy | |||
zaniechać dalszego iterowania tej pętli). | |||
Oto odpowiednie | Oto odpowiednie reguły dla <math>\mbox{było-}\mathbf{exit}</math>: | ||
<math> | <math> | ||
\frac{I_1, s \,\longrightarrow\, s', \mbox{ | \frac{I_1, s \,\longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
{I_1;\, I_2, s \,\longrightarrow\, s', \mbox{ | {I_1;\, I_2, s \,\longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
\quad \quad | \quad \quad | ||
\frac{I_1, s \,\longrightarrow\, s' \quad \quad I_2, s' \,\longrightarrow\, s'', \mbox{ | \frac{I_1, s \,\longrightarrow\, s' \quad \quad I_2, s' \,\longrightarrow\, s'', \mbox{było-}\mathbf{exit}} | ||
{I_1;\, I_2, s \,\longrightarrow\, s'', \mbox{ | {I_1;\, I_2, s \,\longrightarrow\, s'', \mbox{było-}\mathbf{exit}} | ||
</math> | </math> | ||
<math> | <math> | ||
\frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I_1, s \,\longrightarrow\, s', \mbox{ | \frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I_1, s \,\longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
{\mathbf{if}\, b \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s', \mbox{ | {\mathbf{if}\, b \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
\quad \quad | \quad \quad | ||
\frac{b, s \,\longrightarrow\, \mathbf{false} \quad \quad I_2, s \,\longrightarrow\, s', \mbox{ | \frac{b, s \,\longrightarrow\, \mathbf{false} \quad \quad I_2, s \,\longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
{\mathbf{if}\, b \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s', \mbox{ | {\mathbf{if}\, b \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
</math> | </math> | ||
<math> | <math> | ||
\frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I, s \,\longrightarrow\, s', \mbox{ | \frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I, s \,\longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
{\mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, s', \mbox{ | {\mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
\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\, s'', \mbox{ | \mathbf{while}\, b \,\mathbf{do}\, I, s' \,\longrightarrow\, s'', \mbox{było-}\mathbf{exit}} | ||
{\mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, s', \mbox{ | {\mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
</math> | </math> | ||
Pominęliśmy zupełnie analogiczne reguły dla <math>\mbox{było-}\mathbf{continue}</math>. | |||
Zauważmy, że dla pętli <math>\mathbf{while}\,</math> nie rozpatrujemy przypadku, | |||
gdy dozór <math>b</math> oblicza | gdy dozór <math>b</math> oblicza się do <math>\mathbf{false}</math>, gdyż w tym przypadku | ||
nie ma | nie ma możliwości wygenerowania śladu. | ||
Zauważmy, że nasze reguły nie pozwalają na dodanie drugiego (kolejnego) śladu! | |||
Linia 443: | Linia 443: | ||
{{rozwiazanie|( | {{rozwiazanie|(małe kroki)|roz3.2| | ||
<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"> | ||
W semantyce naturalnej | W semantyce naturalnej musieliśmy napisać wiele reguł aby | ||
zapewnić pomijanie instrukcji w sytuacji, gdy został juz | |||
zarejestrowany | zarejestrowany jakiś ślad. Było to dość uciążliwe. | ||
Okazuje | Okazuje się, że podejście mało-krokowe oferuje możliwość | ||
bardziej eleganckiego | bardziej eleganckiego rozwiązania. | ||
Punktem startowym | Punktem startowym sę teraz reguły mało-krokowe dla języka Tiny. | ||
Podobnie jak poprzednio rozszerzymy zbiór konfiguracji i podobnie | Podobnie jak poprzednio rozszerzymy zbiór konfiguracji i podobnie | ||
opiszemy, jak powstaje | opiszemy, jak powstaje ślad: | ||
<math> | <math> | ||
\mathbf{exit}, s \,\Longrightarrow\, s, \mbox{ | \mathbf{exit}, s \,\Longrightarrow\, s, \mbox{było-}\mathbf{exit} | ||
\quad \quad | \quad \quad | ||
\mathbf{continue}, s \,\Longrightarrow\, s, \mbox{ | \mathbf{continue}, s \,\Longrightarrow\, s, \mbox{było-}\mathbf{continue}. | ||
</math> | </math> | ||
Ponadto musimy | Ponadto musimy określić sposób, w jaki mały krok | ||
mniejszej instrukcji zostaje zamieniony na | mniejszej instrukcji zostaje zamieniony na mały krok | ||
większej. Np. | |||
<math> | <math> | ||
\frac{I_1, s \,\Longrightarrow\, s', \mbox{ | \frac{I_1, s \,\Longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
{I_1;\, I_2, s \,\Longrightarrow\, s', \mbox{ | {I_1;\, I_2, s \,\Longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
\quad \quad | \quad \quad | ||
\mbox{ i analogicznie dla } \mbox{ | \mbox{ i analogicznie dla } \mbox{było-}\mathbf{continue}. | ||
</math> | </math> | ||
Nie musimy zajmowac | Nie musimy zajmowac się przypadkiem, gdy ślad | ||
powstaje w <math>I_2</math>, bo | powstaje w <math>I_2</math>, bo wybraliśmy podejście mało-krokowe. | ||
Ponadto, nie musimy | Ponadto, nie musimy opisywać instrukcji warunkowej i pętli <math>\mathbf{while}\,</math>, | ||
ponieważ ślad nie może powstać podczas obliczania dozoru! | |||
Wreszcie zobaczmy jak zachowuje | Wreszcie zobaczmy jak zachowuje się pętla <math>\mathbf{loop}\,</math>: | ||
<math> | <math> | ||
\frac{I, s \,\Longrightarrow\, s', \mbox{ | \frac{I, s \,\Longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
{\mathbf{loop}\, I, s \,\Longrightarrow\, s'} | {\mathbf{loop}\, I, s \,\Longrightarrow\, s'} | ||
\quad \quad | \quad \quad | ||
\frac{I, s \,\Longrightarrow\, s', \mbox{ | \frac{I, s \,\Longrightarrow\, s', \mbox{było-}\mathbf{continue}} | ||
{\mathbf{loop}\, I, s \,\Longrightarrow\, \mathbf{loop}\, I, s'} | {\mathbf{loop}\, I, s \,\Longrightarrow\, \mathbf{loop}\, I, s'} | ||
</math> | </math> | ||
Reguły te są prawie identyczne do reguł semantyki naturalnej dla tej | |||
sytuacji! | sytuacji! | ||
Natomiast korzystamy z tego, | Natomiast korzystamy z tego, że w podejściu mało-krokowym zmiana konfiguracji | ||
na <math>s', \mbox{ | na <math>s', \mbox{było-}\mathbf{exit}</math> czy <math>s', \mbox{było-}\mathbf{continue}</math> jest | ||
''natychmiast'' widoczna w instrukcji <math>\mathbf{loop}\, I</math>, nawet | ''natychmiast'' widoczna w instrukcji <math>\mathbf{loop}\, I</math>, nawet jeśli | ||
<math>\mathbf{exit}</math> czy <math>\mathbf{continue}</math> | <math>\mathbf{exit}</math> czy <math>\mathbf{continue}</math> zostało wywołane głęboko wewnątrz | ||
<math>I</math>! | <math>I</math>! | ||
Niestety | Niestety powyższe reguły '''nie są poprawne'''! | ||
Dlaczego? Jak zwykle w semantyce | Dlaczego? Jak zwykle w semantyce małych kroków, wykonując instrukcję | ||
wewnętrzną ''zapominamy'' stopniowo jaka była ona na początku. | |||
I w | I w związku z tym nie potrafimy poprawnie powrócić do wykonywania | ||
pętli <math>\mathbf{loop}\,</math> po wywołaniu <math>\mathbf{continue}</math>. | |||
Prostym sposobem poprawienia naszego | Prostym sposobem poprawienia naszego blędu jest rozszerzenie składni | ||
tak, aby | tak, aby możliwe było jednorazowe rozwinięcie pętli <math>\mathbf{loop}\,</math>: | ||
<math> | <math> | ||
Linia 511: | Linia 511: | ||
</math> | </math> | ||
Teraz | Teraz możemy zapisać dwie powyższe reguły dla <math>\mathbf{loop}\,</math> w poprawnej | ||
wersji, | wersji, pamiętając o tym, że <math>\mbox{było-}\mathbf{exit}</math> i | ||
<math>\mbox{ | <math>\mbox{było-}\mathbf{continue}</math> pojawią się nie w instrukcji wewnętrznej, | ||
ale w jej ''kopii'' umieszczonej przed <math>\,\mathbf{then}\,</math>: | ale w jej ''kopii'' umieszczonej przed <math>\,\mathbf{then}\,</math>: | ||
<math> | <math> | ||
\frac{I', s \,\Longrightarrow\, s', \mbox{ | \frac{I', s \,\Longrightarrow\, s', \mbox{było-}\mathbf{exit}} | ||
{I' \,\mathbf{then}\, \mathbf{loop}\, I, s \,\Longrightarrow\, s'} | {I' \,\mathbf{then}\, \mathbf{loop}\, I, s \,\Longrightarrow\, s'} | ||
\quad \quad | \quad \quad | ||
\frac{I', s \,\Longrightarrow\, s', \mbox{ | \frac{I', s \,\Longrightarrow\, s', \mbox{było-}\mathbf{continue}} | ||
{I; \,\mathbf{then}\, \mathbf{loop}\, I, s \,\Longrightarrow\, \mathbf{loop}\, I, s'} | {I; \,\mathbf{then}\, \mathbf{loop}\, I, s \,\Longrightarrow\, \mathbf{loop}\, I, s'} | ||
\quad \quad | \quad \quad | ||
Linia 527: | Linia 527: | ||
</math> | </math> | ||
Potrzebujemy | Potrzebujemy też dodatkowej reguły dla obliczeń wewnątrz instrukcji | ||
stojącej przed <math>\,\mathbf{then}\,</math> (w szczególności może ona zawierać | |||
zagnieżdzone pętle <math>\mathbf{loop}\,</math> : | |||
<math> | <math> | ||
Linia 536: | Linia 536: | ||
</math> | </math> | ||
Na koniec | Na koniec zauważmy, że stan <math>s'</math> w | ||
pierwszych dwóch z | pierwszych dwóch z powyższych reguł nigdy nie jest różny | ||
od <math>s</math>. Zatem | od <math>s</math>. Zatem równoważnie moglibyśmy zamienić <math>s'</math> na <math>s</math> | ||
w | w powyższych dwóch regułach. Ale wtedy okazuje się , <math>s</math> | ||
w parze z <math>\mbox{ | w parze z <math>\mbox{było-}\mathbf{exit}</math> albo <math>\mbox{było-}\mathbf{continue}</math> jest nadmiarowe | ||
i | i może zostać wyeliminowane. | ||
Zatem ostatecznie nasze | Zatem ostatecznie nasze reguły mogą wyglądać tak: | ||
<math> | <math> | ||
\mathbf{exit}, s \,\Longrightarrow\, \mbox{ | \mathbf{exit}, s \,\Longrightarrow\, \mbox{było-}\mathbf{exit} | ||
\quad \quad | \quad \quad | ||
\mathbf{continue}, s \,\Longrightarrow\, \mbox{ | \mathbf{continue}, s \,\Longrightarrow\, \mbox{było-}\mathbf{continue} | ||
</math> | </math> | ||
<math> | <math> | ||
\frac{I_1, s \,\Longrightarrow\, \mbox{ | \frac{I_1, s \,\Longrightarrow\, \mbox{było-}\mathbf{exit}} | ||
{I_1;\, I_2, s \,\Longrightarrow\, \mbox{ | {I_1;\, I_2, s \,\Longrightarrow\, \mbox{było-}\mathbf{exit}} | ||
\quad \quad | \quad \quad | ||
\frac{I_1, s \,\Longrightarrow\, \mbox{ | \frac{I_1, s \,\Longrightarrow\, \mbox{było-}\mathbf{continue}} | ||
{I_1;\, I_2, s \,\Longrightarrow\, \mbox{ | {I_1;\, I_2, s \,\Longrightarrow\, \mbox{było-}\mathbf{continue}} | ||
</math> | </math> | ||
Linia 564: | Linia 564: | ||
<math> | <math> | ||
\frac{I', s \,\Longrightarrow\, \mbox{ | \frac{I', s \,\Longrightarrow\, \mbox{było-}\mathbf{exit}} | ||
{I' \,\mathbf{then}\, \mathbf{loop}\, I, s \,\Longrightarrow\, s} | {I' \,\mathbf{then}\, \mathbf{loop}\, I, s \,\Longrightarrow\, s} | ||
\quad \quad | \quad \quad | ||
\frac{I', s \,\Longrightarrow\, \mbox{ | \frac{I', s \,\Longrightarrow\, \mbox{było-}\mathbf{continue}} | ||
{I; \,\mathbf{then}\, \mathbf{loop}\, I, s \,\Longrightarrow\, \mathbf{loop}\, I, s} | {I; \,\mathbf{then}\, \mathbf{loop}\, I, s \,\Longrightarrow\, \mathbf{loop}\, I, s} | ||
\quad \quad | \quad \quad | ||
Linia 580: | Linia 580: | ||
a zbiór konfiguracji poszerzamy tylko o dwie nowe konfiguracje | a zbiór konfiguracji poszerzamy tylko o dwie nowe konfiguracje | ||
<math>\{ \mbox{ | <math>\{ \mbox{było-}\mathbf{exit}, \mbox{było-}\mathbf{continue} \}</math>. | ||
</div></div> | </div></div> | ||
Linia 591: | Linia 591: | ||
{{cwiczenie|1|cw1.dom| | {{cwiczenie|1|cw1.dom| | ||
Zaproponuj | Zaproponuj semantykę mało-krokową dla rozszerzeń języka Tiny, które | ||
studiowaliśmy powyżej. | |||
}} | }} | ||
Linia 598: | Linia 598: | ||
{{cwiczenie|2|cw2.dom| | {{cwiczenie|2|cw2.dom| | ||
Napisz | Napisz semantyję naturalną dla nieznacznie rozszerzonej wersji instrukcji <math>\mathbf{loop}\,</math>: | ||
<math> | <math> | ||
Linia 607: | Linia 607: | ||
</math> | </math> | ||
Identyfikator <math>x</math> | Identyfikator <math>x</math> pełni tutaj rolę etykiety związanej z | ||
instrukcją <math>\mathbf{loop}\,</math>, jest też parametrem dwóch pozostałych | |||
instrukcji. <math>\mathbf{exit} x</math> | instrukcji. <math>\mathbf{exit} x</math> kończy teraz najbliższą otaczającą pętlę <math>\mathbf{loop}\,</math> | ||
o etykiecie <math>x</math>. Podobnie <math>\mathbf{continue} x</math> wznawia | o etykiecie <math>x</math>. Podobnie <math>\mathbf{continue} x</math> wznawia najbliższą | ||
otaczającą pętlę o etykiecie <math>x</math>. | |||
{{przyklad||| | {{przyklad||| | ||
Linia 627: | Linia 627: | ||
}} | }} | ||
kończy działanie z wartością zmiennej <math>a = 3</math>. | |||
Za | Za pomocą wcięć określiliśmy, do wnętrza której pętli | ||
<math>\mathbf{loop}\,</math> | <math>\mathbf{loop}\,</math> należy każda z trzech ostatnich instrukcji przypisania. | ||
Niejednoznaczność bierze się oczywiście stąd, że | |||
pracujemy ze | pracujemy ze składnią abstrakcyjną. Składnia konkretna zawierałaby | ||
prawdopodobnie | prawdopodobnie jakąś konstukcję ''zamykającą'' pętlę <math>\mathbf{loop}\,</math>, | ||
np. <math>\mathbf{loop}\, I \,\mathbf{end}\mathbf{loop}\,</math>. | np. <math>\mathbf{loop}\, I \,\mathbf{end}\mathbf{loop}\,</math>. | ||
}} | }} | ||
Linia 639: | Linia 639: | ||
{{cwiczenie|3|cw3.dom| | {{cwiczenie|3|cw3.dom| | ||
Napisz | Napisz semantykę naturalną i mało-krokową dla rozszerzenia | ||
języka Tiny o wyrażenia z efektami ubocznymi: | |||
<math> | <math> | ||
Linia 650: | Linia 650: | ||
</math> | </math> | ||
Obliczenie | Obliczenie wyrażenia <math>\,\mathbf{do}\, I \,\mathbf{then}\, e</math> polega na wykonaniu <math>I</math> | ||
a potem na obliczeniu <math>e</math>. | a potem na obliczeniu <math>e</math>. Wartość wyrażenia <math>x:= e</math> jest taka | ||
jak | jak wartość wyrażenia <math>e</math> a efektem ubocznym jest | ||
podstawienie tej | podstawienie tej wartości na zmienną <math>x</math>. | ||
}} | }} |
Wersja z 08:06, 10 sie 2006
Zawartość
Kończymy semantykę małych kroków i rozpoczynamy semantykę naturalną (duże kroki). Uzupełnimy semantykę naturalną języka Tiny o semantykę naturalną wyrażeń boolowskich i arytmetycznych oraz semantykę błędów wykonania. Wreszcie dodamy nową instrukcję pętli pozwalającą na niestrukturalne przerwanie lub wznowienie iteracji (instrukcje i ).
Rozszerzenia semantyki języka Tiny
Ćwiczenie 1
Zdefiniuj znaczenie wyrażeń boolowskich i arytmetycznych w języku Tiny, w stylu dużych kroków (semantyka naturalna).
Rozwiązanie
Ćwiczenie 2
Rozważ dodatkowo operację dzielenia:
i rozszerz semantykę z poprzedniego zadania. Dzielenie przez zero jest błądem i kończy natychmiast wykonanie programu. Oprócz stanu wynikiem programu powinna byc informacja o błędzie, jeśli błąd wystąpił.
Rozwiązanie
Ćwiczenie 3
Rozszerzmy język Tiny o następującą instrukcję pętli:
to instrukcja pętli, stanowi instrukcję wewnętrzną. Instrukcja wychodzi z nabliższej otaczającej pętli i kontynuuje wykonanie programu od pierwszej instrukcji za tą pętlą. Instrukcja powraca na początek instrukcji wewnętrznej najbliższej otaczającej pętli .
Pozważ zarówno semantykę naturalną jak i semantykę małych kroków.
Rozwiązanie (semantyka naturalna)
Rozwiązanie (małe kroki)
Zadania domowe
Ćwiczenie 1
Zaproponuj semantykę mało-krokową dla rozszerzeń języka Tiny, które studiowaliśmy powyżej.
Ćwiczenie 2
Napisz semantyję naturalną dla nieznacznie rozszerzonej wersji instrukcji :
Identyfikator pełni tutaj rolę etykiety związanej z instrukcją , jest też parametrem dwóch pozostałych instrukcji. kończy teraz najbliższą otaczającą pętlę o etykiecie . Podobnie wznawia najbliższą otaczającą pętlę o etykiecie .
Przykład
kończy działanie z wartością zmiennej . Za pomocą wcięć określiliśmy, do wnętrza której pętli należy każda z trzech ostatnich instrukcji przypisania. Niejednoznaczność bierze się oczywiście stąd, że pracujemy ze składnią abstrakcyjną. Składnia konkretna zawierałaby prawdopodobnie jakąś konstukcję zamykającą pętlę , np. .
Ćwiczenie 3
Napisz semantykę naturalną i mało-krokową dla rozszerzenia języka Tiny o wyrażenia z efektami ubocznymi:
Obliczenie wyrażenia polega na wykonaniu a potem na obliczeniu . Wartość wyrażenia jest taka jak wartość wyrażenia a efektem ubocznym jest podstawienie tej wartości na zmienną .