Programowanie funkcyjne/Wyliczanie procesów: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Przemek (dyskusja | edycje)
Nie podano opisu zmian
 
Kubica (dyskusja | edycje)
Nie podano opisu zmian
 
(Nie pokazano 9 wersji utworzonych przez jednego użytkownika)
Linia 1: Linia 1:
==Środowisko i ramki.==
[[Programowanie funkcyjne/Model obliczeń]]
Podział środowiska na ciąg ramek połączonych wskaźnikami w listę
jednokierunkową.
Poszukując w środowisku wartości stałej przeglądamy kolejne ramki.
Definiowanie nowych wartości powoduje dodanie nowej ramki
zawierającej wprowadzane definicje.
Można przesłaniać wartości już istniejące w słowniku.
Nie powoduje to jednak ich usunięcia ze słownika.
 
Każda ramka reprezentuje pewne środowisko.
Aktualne środowisko jest reprezentowane przez ostatnio dodaną ramkę.
Środowisko, do którego prowadzi wskaźnik od danej ramki nazywamy
środowiskiem otaczającym.
 
 
==Model obliczeń==
<tt>let</tt>(globalny) tworzy nową ramkę zawierającą definiowaną wartość(-ci).
Dotychczasowe środowisko staje się środowiskiem otaczającym tej ramki.
Utworzona ramka staje się aktualnym środowiskiem (globalnym).
\begin{przyklad}
%%
%%  '''let''' a = 2;;
%%  '''let''' b = 2 * a;;
%%  '''let''' a = 2 * b + a;;
%%  '''let''' b = "{}ala";;
%%
\begin{center}
\includegraphics{rys-3-1}
\end{center}
\end{przyklad}
Ramka może zawierać więcej niż jedną definicję --- <tt>and</tt>.\begin{przyklad}
<!--%        \begin{code}
-->
<!--%          let a = 2 and b = 4;;\\
-->
<!--%          let a = 2 * b and b = 4 * a;;\\
-->
<!--%          a;;\\
-->
<!--%          b;;
-->
<!--%        \end{code}
-->
\begin{center}
\includegraphics{rys-3-2}
\end{center}
\end{przyklad}
 
===Procedura jako wartość===
Na wartość procedury składają sie trzy elementy:
nazwy argumentów%
\footnote{%
Formalnie rzecz biorąc, jest to jeden argument.
Dopóki jednak nie wprowadzimy procedur wyższych rzędów,
oraz w sytuacjach, gdy takie uproszczenie nie będzie
prowadzić do nieporozumień, będziemy dopuszczać
obiekty proceduralne z wieloma argumentami.
}%
, treść procedury i wskaźnik do ramki
reprezentującej środowisko, w którym zdefiniowano
procedurę.
Pierwsze dwa elementy przekładają się, w trakcie kompilacji,
na kod procedury.
Natomiast wskaźnik do środowiska, w którym zdefiniowano
procedurę może być ustalany w trakcie obliczeń ---
dotyczy to np.\ procedur lokalnych.
reasumując, wartość procedury jest reprezentowana jako para
wskaźników: do kodu skompilowanej procedury i do
środowiska, w którym procedura została zdefiniowana.
W przypadku procedur rekurencyjnych ,,środowisko, w którym
zdefiniowano procedurę'' zawiera jej własną definicję.
 
===Wartości różnych typów danych===
Wartości elementarnych typów wbudowanych (takie jak
\codeline{int}, czy <tt>bool</tt>)są pamiętane wprost.
Wartości typów złożonych są pamiętane jako wskaźniki do odpowiednich
wartości.
Dzięki temu wynikiem obliczenia wartości jest zawsze albo wskaźnik,
albo wartość, która zajmuje porównywalnie dużo miejsca w pamięci
do wskaźnika.
W rezultacie definiując stałe o złożonych wartościach nie kopiujemy
tych wartości, a jedynie wskaźniki do nich.
       
Wartości różnych złożonych typów danych są generalnie tworzone jako
rekordy złożone ze wskaźników do wartości składowych:
**;Element produktu kartezjańskiego jest reprezentowany jako
*;n-tka wskaźników do wartości tworzących go.
**;Rekord --- podobnie do produktu kartezjańskiego.
**;Lista to albo pusta lista, albo para: głowa i ogon.
**;Warianty są pamiętane jako stała wyznaczająca operator
*;i wskaźnik(i) do argumentu(-ów).
 
===Aplikacja procedury do argumentów===
Obliczenie wartości procedury polega na:
**;wyliczeniu wszystkich elementów: procedury i jej argumentów,
**;stworzeniu nowej ramki, dla której środowiskiem otaczającym
*;jest środowisko, w którym zdefiniowano procedurę, oraz
*;nazwom parametrów formalnych przyporządkowano wartości
*;argumentów,
**;wyliczeniu w takim środowisku wartości wyrażenia
*;stanowiącego treść.
 
\begin{przyklad}
Następujące definicje:
         
'''let''' x = 2;;
'''let''' square x = x * x;;
'''let''' pitagoras x y = square x + square y;;
         
będą reprezentowane w środowisku w taki oto sposób:
\begin{center}
\includegraphics{rys-3-3}
\end{center}
Obliczenie wyrażenia:
         
pitagoras x (x + 1) + pitagoras x ((square x) + 1);;
         
będzie przebiegać następująco:
\begin{center}
\includegraphics{rys-3-4}
\end{center}
\end{przyklad}
         
\begin{przyklad}
Prześledźmy obliczenie prostego wyrażenia zawierającego
<math>\lambda</math>-abstrakcję:
\begin{center}
\includegraphics{rys-3-5}
\end{center}
\end{przyklad}
       
\begin{przyklad}
Wyliczanie procedur rekurencyjnych:
         
let '''rec''' silnia n =
&nbsp;&nbsp;if n < 2 then 1 '''else''' n * silnia (n - 1);;
silnia 3;;
         
\begin{center}
\includegraphics{rys-3-6}
\end{center}
\end{przyklad}
 
===Rekurencja ogonowa===
Jeśli wartości zwracane przez wszystkie wywołania rekurencyjne,
bez żadnego przetwarzania są przekazywane jako wynik danej procedury,
to mówimy o rekurencji ogonowej.
W przypadku rekurencji ogonowej, w momencie wywołania rekurencyjnego
nie musimy tworzyć nowej ramki --- możemy wykorzystać istniejącą ramkę
zmieniając jedynie wartości argumentów.
 
\begin{przyklad}
Silnia z akumulatorem:
         
let '''rec''' s a x =
&nbsp;&nbsp;if x <= 1 then a '''else''' s (a * x) (x - 1);;
'''let''' silnia x = s 1 x;;
silnia 3;;
         
\begin{center}
\includegraphics{rys-3-7}
\end{center}
\end{przyklad}
 
\begin{przyklad}
Liczby Fibonacciego z akumulatorem:
         
let '''rec''' fibpom a b n =
&nbsp;&nbsp;if n = 0 then a '''else''' fibpom b (a + b) (n - 1);;
'''let''' fib n = fibpom 0 1 n;;
fib 5;;
         
\begin{center}
\includegraphics{rys-3-8}
\end{center}
\end{przyklad}
 
===Definicje lokalne===
Ewaluacja <tt>let-in</tt> jest analogiczna do rozwinięcia tej formy specjalnej do zastosowania <math>\lambda</math>-abstrakcji.
Wyrażenie postaci:
       
let <math>a</math> = <math>b</math> '''in''' <math>c</math>
       
jest równoważne:
       
(function <math>a</math> -> <math>c</math>) <math>b</math>
       
Schemat obliczenia wyrażenia <tt>let</tt> jest następujący:**;tworzona jest ramka zawierająca definicję symboli
*;lokalnych (otaczającym ją środowiskiem jest aktualne środowisko),
**;w tak powstałym środowisku wyliczana jest wartość wyrażenia,
**;po czym aktualnym środowiskiem jest to, co poprzednio.
 
\begin{przyklad}
Następujący przykład pokazuje sposób realizacji definicji
lokalnych.
         
'''let''' f x =
&nbsp;&nbsp;let y = x + 2
&nbsp;&nbsp;in
&nbsp;&nbsp;&nbsp;&nbsp;let z = 2 * y
&nbsp;&nbsp;&nbsp;&nbsp;in
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x + y + z;;
f 9;;
         
\begin{center}
\includegraphics{rys-3-9}
\end{center}
\end{przyklad}
 
\begin{przyklad}
Niniejszy przykład ilustruje mniej typową kombinację
użycia definicji lokalnych i procedur.
         
'''let''' f =
&nbsp;&nbsp;let g x = x - 8
&nbsp;&nbsp;in
&nbsp;&nbsp;&nbsp;&nbsp;fun x -> g (2 * x);;
'''let''' h x = f (x * x);;
h 5;;
         
Zwróćmy uwagę na procedurę pomocniczą <tt>g</tt>.Procedura ta jest widoczna tylko wewnątrz procedury
<tt>f</tt>, a jednak jej definicja ma tylko jednąinstancję.
\begin{center}
\includegraphics{rys-3-10}
\end{center}
\end{przyklad}
 
\begin{przyklad}
         
'''let''' f x y =
&nbsp;&nbsp;let
&nbsp;&nbsp;&nbsp;&nbsp;g a = a * x * y
&nbsp;&nbsp;in
&nbsp;&nbsp;&nbsp;&nbsp;g x + g y;;
f 4 2 - f 2 1;;
         
W tym przykładzie, w każdym wywołaniu procedury <tt>f</tt>powstaje osobna instancja procedury lokalnej <tt>g</tt>.Jest to naturalne zważywszy, że procedura <tt>g</tt> korzysta z argumentów wywołania procedury <tt>f</tt>.\begin{center}
\includegraphics{rys-3-11}
\end{center}
\end{przyklad}
 
===Odśmiecanie===
Ramka zawierająca lokalne definicje nie musi stawać się od razu
zbędna, gdyż mogą na nią wskazywać wartości proceduralne.
Zbędne ramki są usuwane w miarę potrzeb --- zbędne to takie, na które
nic nie wskazuje lub, do których nie można dotrzeć z aktualnego
środowiska poprzez wskaźniki.
 
Ramki nie tworzą listy, ale drzewo (biorąc pod uwagę wskaźniki na
otaczające środowisko).
Ramki, które trzeba trzymać w pamięci też nie muszą tworzyć
listy, tylko drzewo.
Występuje to np.\ wówczas, gdy wynikiem procedury jest
procedura.
Zahaczamy tu o procedury wyższych rzędów, ale nie będziemy
tego tematu teraz rozwijać.
\newpage
\begin{przyklad}
Niniejszy przykład pokazuje sytuację, gdy ramki powstałe w
czasie wywołania procedury mogą być potrzebne po jego
zakończeniu.
         
'''let''' f x =
&nbsp;&nbsp;let g y = x * y
&nbsp;&nbsp;in g;;
'''let''' h = f 6;;
h 7;;
         
\begin{center}
\includegraphics{rys-3-12}
\end{center}
Ramka zawierająca definicję procedury <tt>g</tt> powstaje w wyniku wywołania <tt>f 6</tt>, ale jest potrzebna później, do obliczenia <tt>h 7</tt>.\end{przyklad}
         
\begin{przyklad}
Poniższy przykład ilustruje sposób przekazywania wielu
argumentów, tak jak to jest realizowane --- po jednym na
raz.
         
'''let''' g x =
&nbsp;&nbsp;let
&nbsp;&nbsp;&nbsp;&nbsp;a = x * x
&nbsp;&nbsp;in
&nbsp;&nbsp;&nbsp;&nbsp;fun y -> y + a;;
'''let''' h x = (g x) x;;
h 6;;
         
\begin{center}
\includegraphics{rys-3-13}
\end{center}
\end{przyklad}

Aktualna wersja na dzień 12:18, 22 sie 2006