Semantyka i weryfikacja programów/Ćwiczenia 1: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Sl (dyskusja | edycje)
Nie podano opisu zmian
m Zastępowanie tekstu – „.↵</math>” na „</math>”
 
(Nie pokazano 15 wersji utworzonych przez 5 użytkowników)
Linia 1: Linia 1:
== Zawartość ==
== Zawartość ==


Linia 6: Linia 4:




== Zadania z rozwiązaniami ==
== Semantyka operacyjna wyrażeń ==




==== Zadanie 1 (przygotowawcze) ====
{{cwiczenie|1|cw1|
Rozważmy bardzo prosty język wyrażeń, którego składnia opisana jest
 
następującą gramatyką:
Rozważmy bardzo prosty język wyrażeń, którego składnia opisana jest następującą gramatyką:


<math>
<math>
Linia 29: 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">
==== Rozwiązanie ====
<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">


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, ze stałe  
liczbowe są wyrażeniami, czyli <math> \mathbf{Num} \subseteq \mathbf{Exp} </math>.


Będziemy potrzebować zbioru ''stanów'', opisujących wartości
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 przyjąc, że stan to funkcja
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 oznaczac będziemy przez <math> s, s_1, s', \ldots \in \mathbf{State} </math>.


W naszej semantyce będziemy potrzebowac tranzycji dwóch postaci.
W naszej semantyce będziemy potrzebowac tranzycji dwóch postaci.
Po pierwsze, tranzycje postaci
Po pierwsze, tranzycje postaci


<math>
<math>
e, s \,\Longrightarrow\, e', s
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 zmiania 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.


Po drugie, tranzycje postaci
Po drugie, tranzycje postaci
<math>
 
e, s \,\Longrightarrow\, n   
<math>
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 83: Linia 70:
</math>
</math>


a konfiguracje końcowe to <math> \mathbf{Num} </math>.
a konfiguracje końcowe to <math>\mathbf{Num}</math>.


'''Uwaga:''' Tak naprawdę to druga postać tranzycji nie jest
{{
niezbędna, gdyż moglibyśmy umówić się, że konfiguracje końcowe
uwaga||uwaga1|
to <math> \mathbf{Num} \times \mathbf{State} </math>.
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>.
'''(koniec uwagi)'''
}}
 
<!--
'''Uwaga:'''
Tranzycje pierwszej postaci mogłyby również wyglądać
następująco:
<math>
e, s \,\Longrightarrow\, e'.
</math>
Wtedy zbiorem konfiguracji byłby zbiór
<math>
( \mathbf{Exp} \times \mathbf{State} ) \, \cup \, \mathbf{Exp}
</math>
a konfiguracje końcowe pozostałyby bez zmian.
'''(koniec uwagi)'''
-->


Najprostsze są tranzycje prowadzące do konfiguracji końcowej:
Najprostsze są tranzycje prowadzące do konfiguracji końcowej:


<math>
<math>
n, s \,\Longrightarrow\, n
n, s \,\Longrightarrow, n
</math>
</math>


Zauważmy, że <math> n </math> po prawej 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, 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\, n, s \quad \mbox{ o ile } s(x) = n.
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 ''lewostronna''), otrzymamy regułę:


<math>
<math>
e_1 + e_2, s \,\Longrightarrow\, e'_1 + e_2, s  
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.
e_1, s \,\Longrightarrow, e'_1, s</math>
</math>


Reguły tej postaci będziem zapisywac tak:
Reguły tej postaci będziemy zapisywać tak:


[[
<math>
\frac{e_1, s \,\Longrightarrow\, e'_1, s}
\frac{e_1, s \,\Longrightarrow, e'_1, s}
{e_1 + e_2, s \,\Longrightarrow\, e'_1 + e_2, s}.
    {e_1 + e_2, s \,\Longrightarrow, e'_1 + e_2, s}
</math>
</math>


Czyli mały krok w <math> e_1 </math> stanowi też mały krok w <math> e_1 + e_2 </math>.
Czyli mały krok w <math>e_1</math> stanowi też mały krok w <math>e_1 + e_2</math>.
Po zakończeniu obliczania <math> e_1 </math> przechodzimy do <math> e_2 </math>:
Po zakończeniu obliczania <math>e_1</math> przechodzimy do <math>e_2</math>:


<math>
<math>
\frac{e_2, s \,\Longrightarrow\, e'_2, s}
\frac{e_2, s \,\Longrightarrow, e'_2, s}
{n + e_2, s \,\Longrightarrow\, n + e'_2, s}.
{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, s \quad \mbox{ o ile } n = n_1 + n_2.
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ń 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>.
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:


Zauważmy tutaj pewną subtelność, dotyczącą dwóch wystąpień
<math>
symbolu ''+'': pierwsze wystąpienie oznacza jedną z konstrukcji składniowych
\mathrm{add}(n_1, n_2), s \,\Longrightarrow, n, s \quad \mbox{ o ile } n = n_1 + n_2</math>
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ą abstrajkcyjną, więc zamiast <math> e_1 + e_2 </math> moglibyśmy równie
dobrze pisać np. <math> {\mathrm{add}}(e_1, e_2) </math>.


Inna 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:
''prawostronna'', którą otrzymujemy zastępując pierwsze dwie z trzech
powyższych reguł przez:


<math>
<math>
\frac{e_2, s \,\Longrightarrow\, e'_2, s}
\frac{e_2, s \,\Longrightarrow, e'_2, s}
     {e_1 + e_2, s \,\Longrightarrow\, e_1 + e'_2}
     {e_1 + e_2, s \,\Longrightarrow, e_1 + e'_2}
\quad \quad
\quad \quad
\frac{e_1, s \,\Longrightarrow\, e'_1, s}
\frac{e_1, s \,\Longrightarrow, e'_1, s}
     {e_1 + n, s \,\Longrightarrow\, e'_1 + n, s}.
     {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ę
''równoległą'', polegającą na obliczaniu jednocześnie <math> e_1 </math> i
<math> e_2 </math>:


<math>
<math>
\frac{e_1, s \,\Longrightarrow\, e'_1, s}
\frac{e_1, s \,\Longrightarrow, e'_1, s}
     {e_1 + e_2, s \,\Longrightarrow\, e'_1 + e_2, s}
     {e_1 + e_2, s \,\Longrightarrow, e'_1 + e_2, s}
\quad \quad
\quad \quad
\frac{e_2, s \,\Longrightarrow\, e'_2, s}
\frac{e_2, s \,\Longrightarrow, e'_2, s}
     {e_1 + e_2, s \,\Longrightarrow\, e_1 + e'_2}
     {e_1 + e_2, s \,\Longrightarrow, e_1 + e'_2}
\quad \quad
\quad \quad
n_1 + n_2, s \,\Longrightarrow\, n, s \quad \mbox{ o ile } n = n_1 + n_2.
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  
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.
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\, e'_1, s}
\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}
     {\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\, e_2, s \quad \mbox{ o ile } n \neq 0
\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\, e_3, s \quad \mbox{ o ile } n = 0
\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>


==== Zadanie 2 ====
 
 
{{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 230: 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ą na zmienna
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>;
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 reguły przesłaniania zmiennych.
Np., jeśli w <math> e_2 </math> występuje podwyrażenie <math> \mathbf{let}\, x = \ldots \,\mathbf{in}\, e </math> to
odwołania do <math> x </math> wewnątrz <math> e </math> odnoszą się do ''najbliższej''
deklaracji zmiennej <math> x </math>.


Zakładamy, że na początku wartości wszystkich zmiennych są
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.
''nieokreślone'', czyli zmienne są niezainicjowane, a odwołanie do  
niezainicjowanej zmiennej jest uważane za niepoprawne.




==== Przykłady ====
 
{{przyklad|||


<math>
<math>
Linia 260: 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; odwołanie do niezainicjowanej
zmiennej}\, x
</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
Linia 274: Linia 218:
</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">


==== Rozwiązanie ====
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,  
Przyjmijmy zatem, że stan to skończona funkcja częściowa z <math>\mathbf{Var}</math> do <math>\mathbf{Num}</math>.
stan powinien opisywać wartości przypisane zmiennym.
Oznaczmy symbolem <math>\mathbf{State}</math> zbiór wszystkich takich funkcji:
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>.
Oznaczmy symbolem <math> \mathbf{State} </math> zbiór wszystkich takich funkcji:
<math>
<math>
\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 ''pusty'', tzn.
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 trochę ogólniejsza:


<math>
<math>
e, s \,\Longrightarrow\, e', s'.
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 rozszerzyc semantykę z poprzedniego zadania.
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\, n, s \quad \mbox{ o ile } s(x) \mbox{ jest określone i } s(x) = n
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ż obliczne, wyatarczy reguła:


<math>
<math>
\mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow\, e_2, s[x \mapsto n].
\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.
Formanie


<math>
<math>
Linia 336: 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\, e'_1, s'}
\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'}
{\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 360: 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\, \quad \ldots \quad \,\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> ''zapominamy'' przywrócić zmiennej <math> z </math>
poprzednią wartość (a właściwie brak wartości w przykładzie powyżej).
Przedyskutujmy kilka wariantów.
Przedyskutujmy kilka wariantów.


===== Wariant 1 =====
<br>
'''Wariant 1'''
<br>


Wygodne i eleganckie rozwiązanie tego problemu jest możliwe, jeśli  
Wygodne i eleganckie rozwiązanie tego problemu jest możliwe, jeśli rozszerzymy składnię naszego języka.  
rozszerzymy składnię naszego języka.  
Intuicyjnie, reguła
Intuicyjnie, reguła


<math>
<math>
\mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow\, e_2, s[x \mapsto n].
\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{,,przywróć wartość zmiennej x''}, s[x \mapsto n].
\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 401: Linia 322:
e \,  ::=  \,\,   
e \,  ::=  \,\,   
         \ldots  \,\,|\,\,
         \ldots  \,\,|\,\,
         e \,\mathbf{then}\, x := n.
         e \,\mathbf{then}\, x := n</math>
</math>


Zauważmy, że 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 (choc niewątpliwie istotna) różnica  
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\, e_2 \,\mathbf{then}\, x := n', s[x \mapsto n] \quad
\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 428: Linia 341:
</math>
</math>


gdzie symbol <math> \bot </math> oznacza brak wartości.
gdzie symbol <math>\bot</math> oznacza brak wartości.
Dodajemy również regułę:
Dodajemy również regułę:


<math>
<math>
\mathbf{let}\, x = n \,\mathbf{in}\, e_2, s \,\Longrightarrow\, e_2 \,\mathbf{then}\, x := \bot, s[x \mapsto n] \quad
\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, ''scalających'' semantykę dla <math> e \,\mathbf{then}\, x := n </math>
z semantyką pozostałych wyrażeń:


<math>
<math>
\frac{e, s \,\Longrightarrow\, e', s'}
\frac{e, s \,\Longrightarrow, e', s'}
{e \,\mathbf{then}\, x := n, s \,\Longrightarrow\, e' \,\mathbf{then}\, x:= n, s'}
{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', s[x \mapsto n]
n' \,\mathbf{then}\, x := n, s \,\Longrightarrow, n', s[x \mapsto n]
</math>
</math>


<math>
<math>
n' \,\mathbf{then}\, x := \bot, s \,\Longrightarrow\, n', s' \quad \mbox{ o ile } s(x)  
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>
'''Wariant 2'''
<br>


===== Wariant 2 =====
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>.
Zanim przejdziemy do kolejnego wariantu, zastanówmy się czy  
Pomysł może polegać na rozszerzeniu zbioru <math>\mathbf{Num}</math> o dodatkowy element <math>\bot</math>:
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>.
Pomysł może polegać na rozszerzeniu
zbioru <math> \mathbf{Num} </math> o dodatkowy element <math> \bot </math>:


<math>
<math>
Linia 470: 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ć sie w wyrażeniach podobnie jak stałe.
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
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>.
 


===== Wariant 3 =====
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>
'''Wariant 3'''
<br>


Zrewidujmy teraz podstawowe założenia, które dotychczas poczyniliśmy.
Zrewidujmy teraz podstawowe założenia, które dotychczas poczyniliśmy.
Linia 501: Linia 398:


<math>
<math>
e, s \,\Longrightarrow\, e', s'
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 ?
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\, e'_1, s}
\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}
{\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\, e', s[x \mapsto n] }
\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}.
     {\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ść
''tylko'' na potrzeby obliczania podwyrażenia <math> e </math>!
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\, n', s
\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>
 
== Zadania domowe ==
== Zadania domowe ==




==== Zadanie 1 ====
{{cwiczenie|1|cw1.dom|


Zapisz wariant 2 semantyki z poprzedniego zadania.
Zapisz wariant 2 semantyki z poprzedniego zadania.
}}




==== Zadanie 2 ====
{{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>
}}




==== Zadanie 3 ====
{{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 598: Linia 474:
b \, ::= \,\,  
b \, ::= \,\,  
         \mathbf{true}  \,\,|\,\,
         \mathbf{true}  \,\,|\,\,
         \mathbf{true}  \,\,|\,\,
         \mathbf{false}  \,\,|\,\,
         e_1 \leq e_2  \,\,|\,\,
         e_1 \leq e_2  \,\,|\,\,
         \neg b  \,\,|\,\,
         \neg b  \,\,|\,\,
Linia 616: 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{true} </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ą:

n::=0|1|

x::=(identyfikatory)

e::=n|x|e1+e2|𝐢𝐟e1𝐭𝐡𝐞𝐧e2𝐞𝐥𝐬𝐞e3

Wynikiem wyrażenienia warunkowego 𝐢𝐟e1𝐭𝐡𝐞𝐧e2𝐞𝐥𝐬𝐞e3 jest wartość wyrażenia e2, o ile wyrażenie e1 oblicza się do wartości różnej od zera; w przeciwnym przypadku wynikiem jest wartość wyrażenia e3.

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ę

e::=|𝐥𝐞𝐭x=e1𝐢𝐧e2

Wyrażenie 𝐥𝐞𝐭x=e1𝐢𝐧e2 zawiera w sobie deklarację x=e1, która stanowi mechanizm wiązania identyfikatorów w naszym języku. Deklaracja x=e1 wprowadza nową zmienną x oraz przypisuje jej wartość. Wartość wyrażenia 𝐥𝐞𝐭x=e1𝐢𝐧e2 obliczamy następująco: najpierw oblicza się wartość e1, podstawia ją za zmienną x, a następnie oblicza wyrażenie e2. Zakresem zmiennej x jest wyrażenie e2, czyli wewnątrz e2 można odwoływać się (wielokrotnie) do zmiennej x; 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 e2 występuje podwyrażenie 𝐥𝐞𝐭x=e𝐢𝐧e, to deklaracja x=e "przesłania" deklarację x=e1 w wyrażeniu e.

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|||

𝐥𝐞𝐭x=0𝐢𝐧𝐥𝐞𝐭y=7𝐢𝐧𝐥𝐞𝐭x=y+3𝐢𝐧x+x+ywynik=24

𝐥𝐞𝐭y=5𝐢𝐧𝐥𝐞𝐭x=(𝐥𝐞𝐭y=3𝐢𝐧y+y)𝐢𝐧x+ywynik=11

𝐥𝐞𝐭z=5𝐢𝐧x+z brak wyniku, odwołanie do niezainicjowanej zmiennej x

𝐥𝐞𝐭x=1𝐢𝐧𝐥𝐞𝐭x=x+x𝐢𝐧x+xwynik=4

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 e w stanie s jest niemożliwe bo wystąpił błąd, to

e,s*Blad


Ćwiczenie 3

Rozważ rozszerzenie języka wyrażeń o wyrażenia boolowskie:

n::=0|1|

x::=(identyfikatory)

b::=𝐭𝐫𝐮𝐞|𝐟𝐚𝐥𝐬𝐞|e1e2|¬b|b1b2

e::=n|x|e1+e2|𝐢𝐟b𝐭𝐡𝐞𝐧e2𝐞𝐥𝐬𝐞e3|𝐥𝐞𝐭x=e1𝐢𝐧e2

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 b1b2, gdy b1 zostało obliczone do 𝐟𝐚𝐥𝐬𝐞, w podejściu leniwym nie ma wogóle potrzeby obliczania b2.