Semantyka i weryfikacja programów/Ćwiczenia 6: Różnice pomiędzy wersjami
Nie podano opisu zmian |
m Zastępowanie tekstu – „.↵</math>” na „</math>” |
||
(Nie pokazano 10 wersji utworzonych przez 4 użytkowników) | |||
Linia 1: | Linia 1: | ||
== Zawartość == | |||
Ostatnie zajęcia poświęcone semantyce naturalnej. | |||
Rozszerzymy język TINY o deklaracje zmiennych i procedur z jednym parametrem. | |||
Rozważymy statyczną i dynamiczną widoczność (wiązanie) identyfikatorów oraz różne mechanizmy przekazywania parametrów. | |||
Ostatnie | Pracujemy ze środowiskami i stanami. | ||
Rozszerzymy | |||
parametrem. | |||
identyfikatorów oraz | |||
parametrów. | |||
Pracujemy ze | |||
Linia 17: | Linia 12: | ||
{{cwiczenie|1 (procedury)|cw1| | {{cwiczenie|1 (procedury)|cw1| | ||
Rozszerzmy | Rozszerzmy język TINY o deklaracje i wywołania procedur: | ||
<math> | <math> | ||
Linia 33: | Linia 28: | ||
</math> | </math> | ||
Konstrukcja <math>\mathbf{proc}\, x_1(x_2)</math> deklaruje | Konstrukcja <math>\mathbf{proc}\, x_1(x_2)</math> deklaruje procedurę o nazwie <math>x_1</math>, której parametrem formalnym jest <math>x_2</math>. | ||
<math>x_1</math>, której parametrem formalnym jest <math>x_2</math>. | Zakładamy statyczne wiązanie identyfikatorów i przekazywanie parametrów przez zmienną (to znaczy, że w momencie wywołania procedury powinna zostać przekazana lokacja odpowiadająca parametrowi aktualnemu). | ||
Instrukcja <math>\mathbf{call}\, x_1(x_2)</math> wywołuje procedurę <math>x_1</math> z parametrem aktualnym <math>x_2</math>. | |||
parametrów przez | |||
procedury | |||
przekazana lokacja | |||
Instrukcja <math>\mathbf{call}\, x_1(x_2)</math> | |||
z parametrem aktualnym <math>x_2</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"> | |||
Użyjemy środowisk <math>E \in \mathbf{Env}</math> i stanów <math>s \in \mathbf{Store}</math>: | |||
<math> | <math> | ||
\mathbf{Store} = \mathbf{Loc} \to_{\mathrm{fin}} \mathbf{Num} | \mathbf{Store} = \mathbf{Loc} \to_{\mathrm{fin}} \mathbf{Num} | ||
\quad \quad \quad | \quad \quad \quad | ||
\mathbf{Env}= \mathbf{Var} \to_{\mathrm{fin}} (\mathbf{Loc} \cup \mathbf{Proc}) | \mathbf{Env}= \mathbf{Var} \to_{\mathrm{fin}} (\mathbf{Loc} \cup \mathbf{Proc})</math> | ||
</math> | |||
Nowym elementem jest zbiór <math>\mathbf{Proc}</math>, potrzebny nam do | Nowym elementem jest zbiór <math>\mathbf{Proc}</math>, potrzebny nam do przechowywania informacji o procedurach. | ||
przechowywania informacji o procedurach. | Informacja na temat procedury, którą musimy przechowywać, to: | ||
Informacja | |||
* nazwa parametru formalnego, | * nazwa parametru formalnego, | ||
* | * ciało procedury (instrukcja <math>I \in \mathbf{Stmt}</math>) oraz | ||
* '' | * ''środowisko'', w którym procedura została zadeklarowana. | ||
Zauważmy, że nie musimy (i nie powinniśmy) pamiętać stanu z momentu deklaracji, czyli konkretnych wartości zmiennych | |||
z momentu deklaracji, czyli konkretnych | wówczas zadeklarowanych. | ||
wówczas zadeklarowanych. | Pamiętamy tylko środowisko, które wyznacza nam ''wiązanie'' identyfikatorów (nazw zmiennych i procedur) występujących w ciele procedury z lokacjami (lub opisami procedur z <math>\mathbf{Proc}</math>), a więc z konkretnymi zmiennymi (lub procedurami). | ||
które wyznacza nam '' | Widać tutaj jasno, jak elegancki i wygodny jest podział na środowisko i stan. | ||
zmiennych i procedur) | |||
lokacjami (lub opisami procedur z <math>\mathbf{Proc}</math>), a | |||
zmiennymi (lub procedurami). | |||
i stan. | |||
Zatem niech <math>\mathbf{Proc} = \mathbf{Var} \times \mathbf{Stmt} \times \mathbf{Env}</math>. | Zatem niech <math>\mathbf{Proc} = \mathbf{Var} \times \mathbf{Stmt} \times \mathbf{Env}</math>. | ||
Czyli znów zbiór <math>\mathbf{Env}</math> zadany jest przez równanie. | Czyli znów zbiór <math>\mathbf{Env}</math> zadany jest przez równanie. | ||
Nasze tranzycje | Nasze tranzycje będą następujących postaci: | ||
<math> | <math> | ||
Linia 91: | Linia 74: | ||
</math> | </math> | ||
Na | Na uwagę zasługuje ostatnia postać tranzycji: deklaracja, w odróżnieniu od instrukcji, może zmieniać środowisko. | ||
w | Umówmy się, że środowisko <math>E'</math> będzie rozszerzało <math>E</math> o informację o nowych identyfikatorach, tzn. tych zadeklarowanych w <math>d</math>. | ||
Umówmy | |||
o nowych identyfikatorach, tzn. tych zadeklarowanych w <math>d</math>. | |||
Zajmijmy | Zajmijmy się najpierw deklaracjami zmiennych: | ||
<math> | <math> | ||
\frac{E \,\vdash\, e, s \,\longrightarrow\, n} | \frac{E \,\vdash\, e, s \,\longrightarrow\, n} | ||
{E \,\vdash\, \mathbf{var}\, x=e, s \,\longrightarrow\, (E', s')} | {E \,\vdash\, \mathbf{var}\, x=e, s \,\longrightarrow\, (E', s')} | ||
\quad \mbox{ gdzie } E' = E[x mapsto l],\, | \quad \mbox{ gdzie } E' = E[x \mapsto l],\, | ||
s' = s[l \mapsto n],\, | s' = s[l \mapsto n],\, | ||
l = \mathtt{newloc}(s) | l = \mathtt{newloc}(s) | ||
Linia 111: | Linia 92: | ||
E \,\vdash\, \mathbf{proc}\, x_1(x_2);\, I, s \,\longrightarrow\, (E', s) | E \,\vdash\, \mathbf{proc}\, x_1(x_2);\, I, s \,\longrightarrow\, (E', s) | ||
\quad \mbox{ gdzie } | \quad \mbox{ gdzie } | ||
E' = E[x_1 \mapsto \langle x_2, I, E \rangle] | E' = E[x_1 \mapsto \langle x_2, I, E \rangle]</math> | ||
</math> | |||
Zauważmy, że deklaracja procedury nie zmienia stanu, tylko środowisko, w odróżnieniu od deklaracji zmiennej. | |||
w | W środowisku pamiętamy trójkę <math>\langle x_2, I, E \rangle</math> zawierającą nazwę parametru formalnego, ciało procedury oraz ''środowisko deklaracji procedury''. | ||
W | To ostatnie determinuje statyczne wiązanie identyfikatorów: wiązanie identyfikatorów występujących w ciele procedury jest jakby "zamrożone" (ustalone) w momencie deklaracji procedury. | ||
Aby dokończyć semantykę deklaracji potrzebujemy jeszcze jednej reguły: | |||
deklaracji procedury''. | |||
To ostatnie determinuje statyczne | |||
jakby | |||
Aby | |||
<math> | <math> | ||
Linia 131: | Linia 106: | ||
</math> | </math> | ||
która opisuje | która opisuje sekwencyjną "kumulację" modyfikacji dokonanych w deklaracjach. | ||
'''Pytanie:''' czy | '''Pytanie:''' czy kolejność poszczególnych deklaracji ma znaczenie? | ||
Teraz | Teraz określimy semantykę bloku: | ||
<math> | <math> | ||
Linia 144: | Linia 119: | ||
</math> | </math> | ||
Czyli instrukcja | Czyli instrukcja wewnętrzna wykonywana jest w środowisku (i stanie) ''wytworzonym'' (rozszerzonym) przez deklarację <math>d</math>. | ||
''wytworzonym'' (rozszerzonym) przez | Stan końcowy po wykonaniu bloku to po prostu stan końcowy <math>s''</math> po wykonaniu instrukcji wewnętrznej. | ||
Stan | Może się to wydawać niepokojące, gdyż oznacza, że nowe lokacje, powołane podczas deklaracji <math>d</math>, zachowują przechowywaną wartość również po wyjściu z bloku. | ||
po wykonaniu instrukcji | Na szczęście wykonanie bloku nie ma wpływu na środowisko, a to ono decyduje, które identyfikatory są widoczne (zadeklarowane) i ogólniej, które lokacje są przypisane identyfikatorom. | ||
A więc deklaracja <math>d</math> nie ma wpływu na środowisko, w którym zostaną wykonane kolejne instrukcje. | |||
Widać to jasno w regule dla złożenia sekwencyjnego: | |||
bloku nie ma | |||
identyfikatory | |||
nie ma | |||
instrukcje. | |||
<math> | <math> | ||
Linia 166: | Linia 134: | ||
Pozostało nam jeszcze określenie semantyki wywołania procedury: | |||
<math> | <math> | ||
Linia 173: | Linia 141: | ||
\quad \mbox{ o ile } | \quad \mbox{ o ile } | ||
E(x_1) = \langle x, I, E' \rangle \in \mathbf{Proc} \mbox{ i } | E(x_1) = \langle x, I, E' \rangle \in \mathbf{Proc} \mbox{ i } | ||
E(x_2) = l \in \mathbf{Loc} | E(x_2) = l \in \mathbf{Loc}</math> | ||
</math> | |||
Czyli uruchamiane jest | Czyli uruchamiane jest ciało <math>I</math> procedury, w środowisku <math>E'</math> z miejsca deklaracji tej procedury zmodyfikowanym o przypisanie <math>x \mapsto l</math> lokacji <math>l</math> do parametru formalnego <math>x</math> procedury. | ||
w | Stan, w którym uruchomione jest <math>I</math>, to po prostu stan bieżący z miejsca wołania procedury; o prawidłowe wiązanie identyfikatorów "troszczy" się wyłącznie środowisko <math>E'</math>. | ||
zmodyfikowanym o przypisanie <math>x \mapsto l</math> | |||
lokacji <math>l</math> do parametru formalnego <math>x</math> procedury. | |||
Stan, w którym uruchomione jest <math>I</math> to po prostu | |||
stan | |||
'''Pytanie:''' czy | '''Pytanie:''' czy powyższa reguła dopuszcza rekurencyjne wywołania procedury? | ||
Okazuje | Okazuje się, że niestety nie, gdyż w środowisku <math>E'[x \mapsto l]</math> nie ma informacji o procedurze <math>x_1</math>, którą właśnie wołamy. | ||
<math>E'[x \mapsto l]</math> nie ma informacji o procedurze <math>x_1</math>, | Może się zdarzyć, że <math>E'[x \mapsto l] \in \mathbf{Proc}</math>, ale oznacza to tylko, że w miejscu deklaracji procedury <math>x_1</math> była już wcześniej (statycznie) zadeklarowana inna procedura o tej samej nazwie. | ||
Aby umożliwić rekurencję, powinniśmy zadbać sami o to, aby środowisko, w którym wykonujemy <math>I</math> zawierało informację o naszej procedurze: | |||
oznacza to tylko, | |||
o tej samej nazwie. | |||
Aby | |||
o naszej procedurze: | |||
<math> | <math> | ||
Linia 202: | Linia 157: | ||
\quad \mbox{ o ile } | \quad \mbox{ o ile } | ||
E(x_1) = \langle x, I, E' \rangle \in \mathbf{Proc} \mbox{ i } | E(x_1) = \langle x, I, E' \rangle \in \mathbf{Proc} \mbox{ i } | ||
E(x_2) = l \in \mathbf{Loc} | E(x_2) = l \in \mathbf{Loc}</math> | ||
</math> | |||
Zauważmy, że przypisanie <math>x_1 \mapsto \langle x, I, E' \rangle</math> dodane do <math>E'</math> wystarcza na tylko jedno wywołanie rekurencyjne (licząc w głąb). | |||
dodane do <math>E'</math> wystarcza na tylko jedno | Ale każde kolejne wywołanie dodaje tę informację, czyli każde wywołanie procedury przygotowuje środowisko dla następnego. | ||
( | I to jest wystarczające. | ||
czyli | |||
I to jest | |||
'''Pytanie:''' czy | '''Pytanie:''' czy możliwa jest rekurencja wzajemna? | ||
Pozostaje jeszcze jedna kwestia: dodajemy do | Pozostaje jeszcze jedna kwestia: dodajemy do środowiska <math>E'</math> dwa przypisania: | ||
dwa przypisania: | <math>x \mapsto l</math> oraz <math>x_1 \mapsto \langle x, I, E' \rangle</math>, ale dlaczego w takiej kolejności? | ||
<math>x \mapsto l</math> oraz <math>x_1 \mapsto \langle x, I, E' \rangle</math>, | |||
ale dlaczego w takiej | |||
'''Pytanie:''' co | '''Pytanie:''' co się stanie, gdy zamienimy tę kolejność: | ||
<math> | <math> | ||
Linia 226: | Linia 175: | ||
\quad \mbox{ o ile } | \quad \mbox{ o ile } | ||
E(x_1) = \langle x, I, E' \rangle \in \mathbf{Proc} \mbox{ i } | E(x_1) = \langle x, I, E' \rangle \in \mathbf{Proc} \mbox{ i } | ||
E(x_2) = l \in \mathbf{Loc} | E(x_2) = l \in \mathbf{Loc}</math> | ||
</math> | |||
Czy obydwie semantyki | Czy obydwie semantyki są sobie równoważne? | ||
Okazuje | Okazuje się, że tak nie jest, oto dwa kontrprzykłady: | ||
<math>\mathbf{begin}\ | <math>\mathbf{begin}\ </math>, | ||
<math>\mathbf{var}\ | <math>\mathbf{var}\ </math>, y = 7; | ||
<math>\mathbf{proc}\ | <math>\mathbf{proc}\ </math>, x(x); x := x+1; | ||
<math>\mathbf{call}\ | <math>\mathbf{call}\ </math>, x(y); | ||
\,\mathbf{end} | <math>\,\mathbf{end}</math> | ||
\mathbf{begin}\, | <math>\mathbf{begin}\ </math>, | ||
<math>\mathbf{var}\ | <math>\mathbf{var}\ </math>, y = 7; | ||
<math>\mathbf{proc}\ | <math>\mathbf{proc}\ </math>, x(x); | ||
<math>\mathbf{if}\ | <math>\mathbf{if}\ </math>, y > 0 <math>\,\mathbf{then}\ </math>, y := y-1; <math>\mathbf{call}\ </math>, x(y) <math>\,\mathbf{else}\, \mathbf{skip}</math>; | ||
<math>\mathbf{call}\ | <math>\mathbf{call}\ </math>, x(y); | ||
\,\mathbf{end} | <math>\,\mathbf{end}</math> | ||
Każdy z tych programów uruchomi się poprawnie w dokładnie jednym z wariantów semantyki. | |||
jednym z wariantów semantyki. | |||
'''Pytanie:''' Który w którym? | '''Pytanie:''' Który w którym? | ||
Semantyka | Semantyka pozostałych instrukcji i wyrażeń języka TINY pozostaje bez zmian. | ||
Rozważmy na koniec problem dealokacji. | |||
<br> | <br> | ||
Linia 266: | Linia 212: | ||
<br> | <br> | ||
Chcemy, aby | Chcemy, aby "posprzątano" po zakończeniu wykonania bloku, tzn. aby zwolniono te lokacje, które były używane tylko w tym bloku i w związku z tym nie będą już potrzebne. | ||
tzn. aby zwolniono te lokacje, które | Oznacza to, że powinniśmy przywrócić nieokreśloność stanu dla wszystkich tych lokacji, które były użyte podczas deklaracji <math>d</math>. | ||
i w | |||
Oznacza to, | |||
wszystkich tych lokacji, które | |||
Jest wiele | Jest wiele możliwości wyrażenia tego dodatkowego warunku, np. możemy dodać do powyższej reguły | ||
np. | |||
<math> | <math> | ||
Linia 282: | Linia 224: | ||
</math> | </math> | ||
dodatkowy warunek postaci <math>\mbox{ gdzie } \bar{s} = \ldots</math>. | dodatkowy warunek postaci: <math>\mbox{ gdzie } \bar{s} = \ldots</math>. | ||
Proponujemy tu | Proponujemy tu rozwiązanie ciut bardziej eleganckie. | ||
Zacznijmy od tranzycji | Zacznijmy od tranzycji dokonujących dealokacji, które mogą być np. postaci <math>s'', L \,\longrightarrow\, \bar{s}</math>. | ||
które | Zakładamy tutaj, że znamy zbiór nowo zaalokowanych lokacji <math>L \subseteq \mathbf{Loc}</math>. | ||
zbiór nowo zaalokowanych lokacji <math>L \subseteq \mathbf{Loc}</math>. | |||
<math> | <math> | ||
Linia 294: | Linia 234: | ||
\frac{s \setminus \{ (l, s(l)) \} , L \setminus \{ l \} \,\longrightarrow\, s'} | \frac{s \setminus \{ (l, s(l)) \} , L \setminus \{ l \} \,\longrightarrow\, s'} | ||
{s, L \,\longrightarrow\, s'} | {s, L \,\longrightarrow\, s'} | ||
\quad \mbox{ o ile } l \in L \mbox{ i } s(l) \mbox{ jest | \quad \mbox{ o ile } l \in L \mbox{ i } s(l) \mbox{ jest określone} | ||
</math> | </math> | ||
Teraz albo napiszemy w dodatkowym warunku, | Teraz albo napiszemy w dodatkowym warunku, że <math>L = \mathrm{dom}(s'') \setminus \mathrm{dom}(s)</math>: | ||
<math>L = \mathrm{dom}(s'') \setminus \mathrm{dom}(s)</math>: | |||
<math> | <math> | ||
Linia 309: | Linia 248: | ||
</math> | </math> | ||
albo | albo możemy poprosić o pomoc w posprzątaniu tego, kto "nabałaganił", czyli deklarację <math>d</math>. | ||
Niech deklaracja zwraca, oprócz pary <math>(E', s')</math>, dodatkowo zbiór <math>L</math>. | |||
Niech deklaracja zwraca, oprócz pary <math>(E', s')</math>, | Oto poprawione reguły dla deklaracji: | ||
dodatkowo zbiór <math>L</math>. | |||
Oto poprawione | |||
Linia 327: | Linia 264: | ||
E \,\vdash\, \mathbf{proc}\, x_1(x_2);\, I, s \,\longrightarrow\, (E', s, \emptyset) | E \,\vdash\, \mathbf{proc}\, x_1(x_2);\, I, s \,\longrightarrow\, (E', s, \emptyset) | ||
\quad \mbox{ gdzie } | \quad \mbox{ gdzie } | ||
E' = E[x_1 \mapsto \langle x_2, I, E \rangle] | E' = E[x_1 \mapsto \langle x_2, I, E \rangle]</math> | ||
</math> | |||
<math> | <math> | ||
Linia 337: | Linia 273: | ||
</math> | </math> | ||
Wtedy ostatecznie | Wtedy ostatecznie reguła dla bloku wygląda następująco: | ||
<math> | <math> | ||
Linia 343: | Linia 279: | ||
\quad \quad | \quad \quad | ||
E' \,\vdash\, I, s' \,\longrightarrow\, s'' | E' \,\vdash\, I, s' \,\longrightarrow\, s'' | ||
\quad \quad | |||
s'', L \,\longrightarrow\, \bar{s}} | s'', L \,\longrightarrow\, \bar{s}} | ||
{E \,\vdash\, \mathbf{begin}\, d;\, I \,\mathbf{end}, s \,\longrightarrow\, \bar{s}} | {E \,\vdash\, \mathbf{begin}\, d;\, I \,\mathbf{end}, s \,\longrightarrow\, \bar{s}} | ||
Linia 348: | Linia 285: | ||
</div></div> | </div></div> | ||
Zastanówmy | {{cwiczenie|2 (wiązanie dynamiczne)|cw2| | ||
aby | |||
Rozumiemy przez to, | Zastanówmy się, jakich modyfikacji wymaga nasza semantyka, aby wiązanie identyfikatorów było dynamiczne zamiast statycznego. | ||
Rozumiemy przez to, że w przypadku odwołania do zmiennej wewnątrz procedury, odwołujemy się do środowiska w miejscu wywołania tej procedury, zamiast do środowiska z miejsca deklaracji jak dotychczas. | |||
Tak samo powinno być w przypadku wywołania innej procedury wewnątrz procedury, czyli wiązanie dynamiczne dotyczy i zmiennych i procedur. | |||
deklaracji jak dotychczas. | |||
Tak samo powinno | |||
i procedur. | |||
}} | }} | ||
Linia 368: | Linia 299: | ||
}} | }} | ||
<math>\mathbf{begin}\ | <math>\mathbf{begin}\ </math>, | ||
<math>\mathbf{var}\ | <math>\mathbf{var}\ </math>, x = 1; | ||
<math>\mathbf{proc}\ | <math>\mathbf{proc}\ </math>, p(y); y := y+x; | ||
<math>\mathbf{begin}\ | <math>\mathbf{begin}\ </math>, | ||
<math>\mathbf{var}\ | <math>\mathbf{var}\ </math>, x = 10; <math>\mathbf{var}\ </math>, z = 0; | ||
<math>\mathbf{call}\ | <math>\mathbf{call}\ </math>, p(z); | ||
<math>\,\mathbf{end}</math> | <math>\,\mathbf{end}</math> | ||
<math>\,\mathbf{end}</math> | <math>\,\mathbf{end}</math> | ||
Wartość końcowa zmiennej <math>z = 10</math>. | |||
Gdyby | Gdyby wiązanie zmiennej <math>x</math> było statyczne, to wartość ta wynosiłaby <math>1</math>. Podobnie dla procedur: | ||
<math>\mathbf{begin}\ | <math>\mathbf{begin}\ </math>, | ||
<math>\mathbf{proc}\ | <math>\mathbf{proc}\ </math>, q(x); x := x+1; | ||
<math>\mathbf{proc}\ | <math>\mathbf{proc}\ </math>, p(y); <math>\mathbf{call}\ </math>, q(y); <math>\mathbf{call}\ </math>, q(y); | ||
<math>\mathbf{begin}\ | <math>\mathbf{begin}\ </math>, | ||
<math>\mathbf{proc}\ | <math>\mathbf{proc}\ </math>, q(x); x := x+x; | ||
<math>\mathbf{var}\ | <math>\mathbf{var}\ </math>, z = 2; | ||
<math>\mathbf{call}\ | <math>\mathbf{call}\ </math>, p(z); | ||
<math>\,\mathbf{end}</math> | <math>\,\mathbf{end}</math> | ||
<math>\,\mathbf{end}</math> | <math>\,\mathbf{end}</math> | ||
Wartość końcowa zmiennej <math>z = 8</math>. | |||
Gdyby | Gdyby widoczność procedury <math>q</math> była statyczna, to wartość ta wynosiłaby <math>4</math>. | ||
A oto przykład programu, który nie wykonałby się poprawnie przy wiązaniu statycznych, a wykonuje się poprawnie przy dynamicznym: | |||
A oto | |||
przy | |||
dynamicznym: | |||
<math>\mathbf{begin}\ | <math>\mathbf{begin}\ </math>, | ||
<math>\mathbf{proc}\ | <math>\mathbf{proc}\ </math>, q(x); <math>\mathbf{call}\ </math>, r(x); | ||
<math>\mathbf{proc}\ | <math>\mathbf{proc}\ </math>, p(y); | ||
<math>\mathbf{begin}\ | <math>\mathbf{begin}\ </math>, | ||
<math>\mathbf{proc}\ | <math>\mathbf{proc}\ </math>, r(x); x := x+x; | ||
<math>\mathbf{call}\ | <math>\mathbf{call}\ </math>, q(x); | ||
<math>\,\mathbf{end}</math> | <math>\,\mathbf{end}</math> | ||
<math>\mathbf{var}\ | <math>\mathbf{var}\ </math>, z = 7; | ||
<math>\mathbf{call}\ | <math>\mathbf{call}\ </math>, p(z); | ||
<math>\,\mathbf{end}</math> | <math>\,\mathbf{end}</math> | ||
Wartość końcowa zmiennej <math>z</math> wynosi <math>14</math>. | |||
Procedura <math>p</math> | Procedura <math>p</math> wywołuje procedurę <math>q</math> "przekazując" jej również środowisko z miejsca wołania, zawierające informację o procedurze <math>r</math>. | ||
Ta ostatnia przy widoczności statycznej byłaby lokalna, a teraz nie jest. | |||
przy | |||
<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"> | |||
Spójrzmy na regułę dla wywołania procedury: | |||
Spójrzmy na | |||
<math> | <math> | ||
Linia 438: | Linia 363: | ||
\quad \mbox{ o ile } | \quad \mbox{ o ile } | ||
E(x_1) = \langle x, I, E' \rangle \in \mathbf{Proc} \mbox{ i } | E(x_1) = \langle x, I, E' \rangle \in \mathbf{Proc} \mbox{ i } | ||
E(x_2) = l \in \mathbf{Loc} | E(x_2) = l \in \mathbf{Loc}</math> | ||
</math> | |||
O | O statyczności decyduje to, że wnętrze procedury <math>I</math> wykonywane jest w środowisku zmiejsca deklaracji <math>E'</math>. | ||
wykonywane jest w | Wystarczy więc, jeśli zignorujemy to środowisko, a zamiast niego użyjemy bieżącego środowiska <math>E</math> (czyli środowiska z miejsca wywołania): | ||
Wystarczy | |||
niego | |||
z miejsca | |||
<math> | <math> | ||
Linia 452: | Linia 373: | ||
\quad \mbox{ o ile } | \quad \mbox{ o ile } | ||
E(x_1) = \langle x, I, E' \rangle \in \mathbf{Proc} \mbox{ i } | E(x_1) = \langle x, I, E' \rangle \in \mathbf{Proc} \mbox{ i } | ||
E(x_2) = l \in \mathbf{Loc} | E(x_2) = l \in \mathbf{Loc}</math> | ||
</math> | |||
Oczywiście w takim przypadku w środowiskach wystarczy przechowywać dla procedur pary <math>\langle x, I \rangle</math>. | |||
wystarczy | |||
Zastanówmy | Zastanówmy się teraz, jak uwzględnić rekurencję? | ||
Moglibyśmy postąpić tak jak poprzednio, np. | |||
<math> | <math> | ||
Linia 469: | Linia 388: | ||
</math> | </math> | ||
ale | ale jeśli uważnie przyjrzymy się tej regule, to okazuje się, że "przelewamy z pustego w próżne". | ||
Sciślej, dodajemy do <math>E</math> parę <math>x_1 \mapsto \langle x, I \rangle</math>, która już tam tak naprawdę jest! | |||
dodajemy do <math>E</math> | Okazuje się, że dzięki dynamicznemu wiązaniu identyfikatorów "za darmo" otrzymujemy rekurencje! Tak jest na przykład w programie: | ||
która | |||
Okazuje | |||
darmo" otrzymujemy rekurencje! Tak jest na | |||
<math>\mathbf{begin}\ | <math>\mathbf{begin}\ </math>, | ||
<math>\mathbf{proc}\ | <math>\mathbf{proc}\ </math>, p(x); <math>\mathbf{if}\ </math>, x < 100 <math>\,\mathbf{then}\ </math>, x := x+x; <math>\mathbf{call}\ </math>, p(x) <math>\,\mathbf{else}\, \mathbf{skip}</math>; | ||
<math>\mathbf{var}\ | <math>\mathbf{var}\ </math>, z = 8; | ||
<math>\mathbf{call}\ | <math>\mathbf{call}\ </math>, p(z); | ||
<math>\,\mathbf{end}</math> | <math>\,\mathbf{end}</math> | ||
ponieważ w momencie wywołania rekurencyjnego <math>\mathbf{call}\, p(x)</math>, w środowisku jest już informacja nt. procedury <math>p</math>! | |||
w | |||
</div></div> | </div></div> | ||
{{cwiczenie|3 ( | {{cwiczenie|3 (różne mechanizmy przekazywana parametru)|cw3| | ||
Rozważ inne mechanizmy przekazywania parametru (wiązanie statyczne): | |||
* przez | * przez wartość | ||
<!-- | <!-- | ||
* przez | * przez nazwę leniwie (jak przez wartość, ale leniwa strategia ewaluacji parametru aktualnego) | ||
* przez | * przez nazwę dynamicznie (dynamiczne wiązanie identyfikatorów podczas obliczania wartości parametru aktualnego) | ||
--> | --> | ||
* in-out | * in-out | ||
Należy wyjaśnić ostatni mechanizm (in-out). | |||
Parametrem aktualnym jest zmienna, której | Parametrem aktualnym jest zmienna, której wartość jest przypisywana parametrowi formalnemu (tak jak przy przekazywaniu przez wartość, gdy parametrem aktualnym jest zmienna). | ||
przypisywana parametrowi formalnemu (tak jak przy przekazywaniu | Ale po zakończeniu procedury, wartość parametru formalnego, która mogła zmienić się w czasie dzialania procedury, jest z powrotem przypisywana na zmienną będącą parametrem aktualnym (a więc podobnie do przekazywania przez zmienną). | ||
przez | |||
Ale po | |||
która | |||
(a | |||
Parametr formalny jest traktowany jak zmienna lokalna procedury. | Parametr formalny jest traktowany jak zmienna lokalna procedury. | ||
}} | }} | ||
<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"> | ||
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie (przekazywanie parametru przez wartość)</span> | |||
<div class="mw-collapsible-content" style="display:none"> | |||
Omówimy tylko | Omówimy tylko instrukcję wywołania procedury, której składnia jest teraz szersza niż poprzednio: | ||
teraz szersza | |||
<math> | <math> | ||
Linia 528: | Linia 437: | ||
</math> | </math> | ||
Przed | Przed wywołaniem procedury należy zaalokować nową lokację, którą przypiszemy parametrowi formalnemu; następnie obliczamy wartość parametru aktualnego i umieszczamy ją w tej lokacji: | ||
<math> | <math> | ||
Linia 543: | Linia 450: | ||
</div></div> | </div></div> | ||
<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 (przekazywanie parametru in-out)</span> | |||
parametrowi formalnemu | <div class="mw-collapsible-content" style="display:none"> | ||
(<math>x_2</math> w regule | |||
Zasadnicza | Początkowo postępujemy tak jak w przypadku przekazywania przez wartość: alokujemy nową lokację <math>l</math>, którą przypiszemy parametrowi formalnemu i umieszczamy w niej wartość zmiennej (<math>x_2</math> w regule poniżej) będącej parametrem aktualnym. | ||
<math>l</math> powinna | Zasadnicza różnica widoczna jest po zakończeniu procedury: wartość z lokacji <math>l</math> powinna zostać spowrotem przypisana zmiennej <math>x_2</math>. | ||
Oto | Oto reguła: | ||
<math> | <math> | ||
Linia 562: | Linia 466: | ||
\quad \mbox{ o ile } | \quad \mbox{ o ile } | ||
E(x_2) = l_2 \in \mathbf{Loc} \mbox{ i } | E(x_2) = l_2 \in \mathbf{Loc} \mbox{ i } | ||
s(l_2) \mbox{ jest | s(l_2) \mbox{ jest określone i } | ||
E(x_1) = \langle x, I, E' \rangle \in \mathbf{Proc} \mbox{ i } | </math> | ||
l = \mathtt{newloc}(s) | <math> | ||
</math> | E(x_1) = \langle x, I, E' \rangle \in \mathbf{Proc} \mbox{ i } l = \mathtt{newloc}(s)</math> | ||
</div></div> | </div></div> | ||
== Zadania domowe == | == Zadania domowe == | ||
Linia 576: | Linia 478: | ||
{{cwiczenie|1|cw1.dom| | {{cwiczenie|1|cw1.dom| | ||
Zaadaptuj | Zaadaptuj semantykę "deklaracji równoległych" dla rekurencji wzajemnej wewnątrz jednej deklaracji równoległej. | ||
dla rekurencji wzajemnej | To znaczy, że procedury zadeklarowane w jednej deklaracji równoległej mogą się wzajemniej wywoływać bez żadnych ograniczeń. | ||
To znaczy, | |||
}} | }} | ||
Linia 585: | Linia 485: | ||
{{cwiczenie|2|cw2.dom| | {{cwiczenie|2|cw2.dom| | ||
Zamiast procedur, | Zamiast procedur, rozważ funkcje z pojedynczym parametrem przekazywanym przez zmienną. | ||
przekazywanym przez | Wywołanie funkcji może teraz pojawić się wewnątrz wyrażenia: | ||
<math> | <math> | ||
e \, ::= \,\, | e \, ::= \,\, | ||
\ldots \,\,|\,\, | \ldots \,\,|\,\, | ||
\mathbf{call}\, x_1(x_2) | \mathbf{call}\, x_1(x_2)</math> | ||
</math> | |||
Wewnątrz funkcji jej nazwą można posługiwać się jak zmienną lokalną. | |||
Wynik zwracany przez funkcję to wartość tej zmiennej w momencie wyjścia z funkcji. | |||
Uwaga na efekty uboczne! | Uwaga na efekty uboczne! | ||
Linia 601: | Linia 502: | ||
{{cwiczenie|3|cw3.dom| | {{cwiczenie|3|cw3.dom| | ||
Rozważ następujące warianty: | |||
* | * wiązanie zmiennych jest statyczne, a procedur dynamiczne | ||
* | * wiązanie zmiennych jest dynamiczne, a procedur statyczne. | ||
}} | }} | ||
Linia 610: | Linia 511: | ||
{{cwiczenie|4|cw4.dom| | {{cwiczenie|4|cw4.dom| | ||
Rozszerz | Rozszerz semantykę procedur tak, aby parametrem aktualnym procedury mogła być procedura. | ||
}} | }} |
Aktualna wersja na dzień 21:39, 11 wrz 2023
Zawartość
Ostatnie zajęcia poświęcone semantyce naturalnej. Rozszerzymy język TINY o deklaracje zmiennych i procedur z jednym parametrem. Rozważymy statyczną i dynamiczną widoczność (wiązanie) identyfikatorów oraz różne mechanizmy przekazywania parametrów. Pracujemy ze środowiskami i stanami.
Semantyka naturalna procedur
Ćwiczenie 1 (procedury)
Rozszerzmy język TINY o deklaracje i wywołania procedur:
Konstrukcja deklaruje procedurę o nazwie , której parametrem formalnym jest . Zakładamy statyczne wiązanie identyfikatorów i przekazywanie parametrów przez zmienną (to znaczy, że w momencie wywołania procedury powinna zostać przekazana lokacja odpowiadająca parametrowi aktualnemu). Instrukcja wywołuje procedurę z parametrem aktualnym .
Rozwiązanie
Ćwiczenie 2 (wiązanie dynamiczne)
Zastanówmy się, jakich modyfikacji wymaga nasza semantyka, aby wiązanie identyfikatorów było dynamiczne zamiast statycznego. Rozumiemy przez to, że w przypadku odwołania do zmiennej wewnątrz procedury, odwołujemy się do środowiska w miejscu wywołania tej procedury, zamiast do środowiska z miejsca deklaracji jak dotychczas. Tak samo powinno być w przypadku wywołania innej procedury wewnątrz procedury, czyli wiązanie dynamiczne dotyczy i zmiennych i procedur.
Przykład
Spójrzmy na kilka prostych programów.
, , x = 1; , p(y); y := y+x; , , x = 10; , z = 0; , p(z);
Wartość końcowa zmiennej .
Gdyby wiązanie zmiennej było statyczne, to wartość ta wynosiłaby . Podobnie dla procedur:
, , q(x); x := x+1; , p(y); , q(y); , q(y); , , q(x); x := x+x; , z = 2; , p(z);
Wartość końcowa zmiennej .
Gdyby widoczność procedury była statyczna, to wartość ta wynosiłaby .
A oto przykład programu, który nie wykonałby się poprawnie przy wiązaniu statycznych, a wykonuje się poprawnie przy dynamicznym:
, , q(x); , r(x); , p(y); , , r(x); x := x+x; , q(x); , z = 7; , p(z);
Wartość końcowa zmiennej wynosi .
Procedura wywołuje procedurę "przekazując" jej również środowisko z miejsca wołania, zawierające informację o procedurze .
Ta ostatnia przy widoczności statycznej byłaby lokalna, a teraz nie jest.
Rozwiązanie
Ćwiczenie 3 (różne mechanizmy przekazywana parametru)
Rozważ inne mechanizmy przekazywania parametru (wiązanie statyczne):
- przez wartość
- in-out
Należy wyjaśnić ostatni mechanizm (in-out). Parametrem aktualnym jest zmienna, której wartość jest przypisywana parametrowi formalnemu (tak jak przy przekazywaniu przez wartość, gdy parametrem aktualnym jest zmienna). Ale po zakończeniu procedury, wartość parametru formalnego, która mogła zmienić się w czasie dzialania procedury, jest z powrotem przypisywana na zmienną będącą parametrem aktualnym (a więc podobnie do przekazywania przez zmienną). Parametr formalny jest traktowany jak zmienna lokalna procedury.
Rozwiązanie (przekazywanie parametru przez wartość)
Rozwiązanie (przekazywanie parametru in-out)
Zadania domowe
Ćwiczenie 1
Zaadaptuj semantykę "deklaracji równoległych" dla rekurencji wzajemnej wewnątrz jednej deklaracji równoległej. To znaczy, że procedury zadeklarowane w jednej deklaracji równoległej mogą się wzajemniej wywoływać bez żadnych ograniczeń.
Ćwiczenie 2
Zamiast procedur, rozważ funkcje z pojedynczym parametrem przekazywanym przez zmienną. Wywołanie funkcji może teraz pojawić się wewnątrz wyrażenia:
Wewnątrz funkcji jej nazwą można posługiwać się jak zmienną lokalną. Wynik zwracany przez funkcję to wartość tej zmiennej w momencie wyjścia z funkcji.
Uwaga na efekty uboczne!
Ćwiczenie 3
Rozważ następujące warianty:
- wiązanie zmiennych jest statyczne, a procedur dynamiczne
- wiązanie zmiennych jest dynamiczne, a procedur statyczne.
Ćwiczenie 4
Rozszerz semantykę procedur tak, aby parametrem aktualnym procedury mogła być procedura.