Wariant 1 (tylko stan)
Oczywiście powinniśmy odróżniać zmienne zadeklarowane (i zarazem zainicjowane) od tych niezadeklarowanych.
Zatem przyjmijmy, że
.
Zachowujemy wszystkie reguły semantyczne dla języka Tiny i dodajemy jedną nową:
mówiącą, że instrukcja wewnętrzna ewaluowana jest w stanie, w którym dodatkowo zaznaczono wartość zmiennej równą wartości, do której oblicza się .
Oczywiście takie rozwiązanie jest niepoprawne, gdyż wartość zmiennej pozostaje w stanie nawet po wyjściu z bloku ("wycieka" z bloku).
Musimy dodać "dealokację" zmiennej :
I znów napotykamy podobne trudności jak na wcześniejszych zajęciach: powyższa reguła nie obejmuje przypadku, gdy jest nieokreślone.
I choć moglibyśmy naprawić ten mankament podobnie jak kiedyś (co pozostawiamy Czytelnikowi), istnieje inne, bardzo eleganckie i elastyczne rozwiązanie tego problemu, które teraz omówimy.
Wariant 2 (stan i środowisko)
Podstawowy pomysł polega na rozdzieleniu informacji przechowywanej dotychczas w stanie, czyli odwzorowania nazw zmiennych w wartości, na dwa "etapy".
Pierwszy z nich odwzorowuje identyfikatory zmiennych w lokacje, a drugi lokacje w wartości.
Mamy więc środowiska , będące funkcjami częściowymi z do , zbioru lokacji:
oraz stany, będące teraz funkcjami częściowymi z do :
Dla ścisłości używamy innej nazwy (), ale będziemy zwykle używać podobnych symboli jak dotychczas itp. do nazywania stanów.
Intuicyjnie można myśleć o lokacjach w pamięci operacyjnej maszyny. Środowisko zawiera informację o lokacji, w której
przechowywana jest wartość danej zmiennej a stan opisuje właśnie zawartość używanych lokacji.
Zakładamy przy tym, że mamy do dyspozycji nieskończony zbiór lokacji i że w każdym momencie tylko skończenie wiele spośród nich jest wykorzystywane.
Formalnie mowiąc, dziedzina funkcji częściowej jest zawsze skończona.
Daje nam to pewność, że zawsze jest jakaś nieużywana lokacja.
Środowisko początkowe, w którym uruchamiany będzie program, będzie z reguły puste.
Ponadto obraz funkcji częściowej będzie zawsze zawarty w zbiorze aktualnie używanych lokacji, czyli zawarty w dziedzinie funkcji częściowej , oznaczanej .
Pożytek z tego podziału na dwa etapy będzie taki, że będziemy umieli łatwo i elastycznie opisywać deklaracje zmiennych, przesłanianie identyfikatorów, itp.
Tranzycje będą teraz postaci:
czyli instrukcja będzie modyfikować stan, ale nie będzie zmieniać środowiska . Dla czytelności będziemy zapisywać nasze reguły w następujący sposób:
podkreślając w ten sposób, że środowisko nie ulega zmianie.
Ale należy pamiętać, że konfiguracja, w której "uruchamiamy" instrukcję składa się naprawdę z trójki .
Deklarując nową zmienną dodamy po prostu do parę , gdzie jest nową, nieużywaną dotychczas lokacją.
Dla wygody zapisu załóżmy, że mamy do dyspozycji funkcję
która zwraca jakąś nową, nieużywaną lokację. Formalnie wymagamy, by
Dla ilustracji popatrzmy na przykładową regułę dla deklaracji.
Zauważmy, że stan po zakończeniu instrukcji zawiera informację o zmiennej lokalnej , tzn. jest określone.
Ale lokacja jest "nieosiągalna" w środowisku , gdyż para została dodana tylko do środowiska, w którym ewaluuje się wnętrze bloku, a środowisko całego bloku nie jest modyfikowane.
Poniżej przedstawiamy reguły dla pozostałych instrukcji.
Przede wszystkim złożenie sekwencyjne:
Reguła ta uzmysławia nam różnicę pomiędzy środowiskiem a stanem: środowisko pozostaje to samo, gdy przechodzimy od jednej instrukcji do następnej, a stan oczywiście ewoluuje wraz ze zmieniającymi się wartościami zmiennych.
Reguły dla przypisania, instrukcji warunkowej i pętli nie przedstawiają żadnych nowych trudności.
Musimy tylko najpierw ustalić postać reguł dla wyrażeń:
która jest zupełnie naturalna w naszym podejściu opartym o środowiska i stany.
Reguły mogą wyglądać np. tak:
Reguły dla wyrażeń są oczywiste:
i tak dalej -- pomijamy pozostałe reguły.
Wariant 3 (dealokacja)
Przypomnijmy sobie semantykę bloku
i zastanówmy się, jak "posprzątać" po zakończeniu wykonania bloku, tzn. zwolnić lokację , która była używana tylko w tym bloku i w związku z tym nie będzie już potrzebna.
Oznaczałoby to przywrócenie lokacji do puli wolnych (nieużywanych) lokacji.
Zmodyfikowana reguła dla instrukcji bloku powinna wyglądać mniej więcej tak:
gdzie to stan "okrojony" do dziedziny stanu .
Powinniśmy po prostu przywrócić nieokreśloność stanu dla lokacji .
Natomiast oczywiście nie ma potrzeby dealokownia środowiska!
Oto rozwiązanie: