| MFF UK / Ústav teoretické fyziky / Tomáš Ledvinka |
|
Objektů se nelekejte (na tečky nehleďte) Tendence a paradigmata v programování jsou v mnohém
podobná evoluci biologických druhů. Ve velkých množstvích
je to jednoduché, přežije the fittest. Pokud ovšem
mluvíme o dostatečně malém rybníku, hraje velkou roli i
náhoda. Existuje několik pokusů o Objektově Orientované Vědecké Výpoočty, ale bohužel současné počítačové jazyky neposkytují zdaleka vše, co by bylo potřeba. (Není divu, počítačové jazyky jsou méně a méně vyvíjeny s ohledem na naše potřeby, typický zákazník není student ani učitel fyziky. Evoluční boj se dnes odehrává v pro nás vzdálených oblastech C#, Javy a servletů - abych vás praštil žargonem). Připoměňme si základní události v dávné historii počítačových jazyků (zamlčuji COBOL, PL1, ...) 1945 - stroj. kód Především, jak že se má pracovat s proměnnou typu
záznam? program Maticka;
type tVektor = array of real;
tMatice = record
M , N : integer;
a : array of tVektor;
end;
procedure VytvorJednotkovou(var A : tMatice; N : integer);
var i,j : integer;
begin
SetLength(A.a,N,N);
A.M := N;
A.N := N;
for i := 0 to N-1 do for j := 0 to N-1 do
if i=j then A.a[i,j]:=1 else A.a[i,j]:=0;
end;
function JeCtvercova(var A : tMatice): boolean;
begin
JeCtvercova := A.M = A.N;
end;
function Stopa(var A : tMatice): real;
begin
...
end;
procedure UvolniPamet(var A : tMatice);
begin
SetLength(A.a,0,0);
A.M := 0;
A.N := 0;
end;
var S : tMatice;
begin
VytvorJednotkovou(S,3);
Writeln(JeCtvercova(S));
Readln;
end.
V programu je záměrně použito předání proměnné typu tMatice odkazem a vždy je to první parametr, takže všechny procedury a funkce se volají: UdelejNeco(PromTypuMatice, ostatní parametry); tedy vlastně (vzpomeneme-li si, že příkazy jazyka Pascal se skoro mohly/měly číst jako věty) UdělejNeco s Čím Tak a Tak. První změna, se kterou přichází OOP je obrácení slovosledu na S Tímhle UdělejNeco Tak a Tak. což psáno v Pascalu bude vypadat Proto se výše uvedený program změní takto: program MatickO;
type tVektor = array of real;
tMatice = object
M , N : integer;
a : array of tVektor;
constructor VytvorJednotkovou(k : integer);
function JeCtvercova: boolean;
function Stopa: real;
end;
constructor tMatice.VytvorJednotkovou(k : integer);
var i,j : integer;
begin
SetLength(a,k,k);
M := k;
N := k;
for i := 0 to N-1 do for j := 0 to N-1 do
if i=j then a[i,j]:=1 else a[i,j]:=0
end;
function tMatice.JeCtvercova: boolean;
begin
JeCtvercova := M = N;
end;
function tMatice.Stopa: real;
begin
...
end;
var S:tMatice;
begin
S.VytvorJednotkovou(3);
Writeln(S.JeCtvercova);
Readln;
end.
V deklarační části oznámíme, že procedura VytvorJednotkovou a dvě funkce JeCtvercova a Stopa jsou součástí součástí záznamu, který se teď jmenuje objekt. K prvkům záznamu tak přibyly tzv.metody, které s prvky záznamu pracují. Jejich vlastní deklarace vypadá jako běžná deklarace funkce, s tím, že prvky záznamu/objektu jsou přístupné, jako by to byly lokální proměnné a identifikátor metody předchází určení pro který typ objektu danou metodu vlastně deklarujeme, protože nic nebrání tomu aby dva objekty mohly mít stejně se jmenující metodu. Proto také typ objekt nemůže být beze jména: var x,y:object // nelze!!
...
Procedure MetodaX; // pod jakým jménem bych asi pak MetoduX deklaroval
end;
Navíc musí být typy objekt deklarovány jako globální ( tady si tvůrci jen ulehčili práci, když to stejně nikdo nechce). Důležité jsou pro nás objekty hlavně proto, že i když sami nebudme chtít vlastní objekty tvořit, může nějaký užitečný modul exportovat (místo typu a sady procedur pro práci s ním) právě objekt. Proto musíme vědět, že pri použití tohoto modulu budeme muset proměnnou daného typu deklarovat a používat právě způsobem, jímž se objekty používají, tedy PromennaTypuObjekt.NejakaMetoda(parametry) . Cvičení: Doplňte v obou příkladech výše tělo procedury/metody stopa.... Druhou podstatnou vlastností objektů je dědičnost a opět si ji ukážeme na příkladě, který by měl připomínat situaci ze života. Řekněme, že máme (z webu) k dispozici následující modul pro quicksort třídění: unit ObjTridic;
interface
type tTridic = object
function Porovnej( j,k : integer ) : integer; virtual; abstract;
procedure Prehod( j,k : integer ); virtual; abstract;
procedure Setrid(l,r:integer);
end;
implementation
procedure tTridic.Setrid(l,r:integer);
var i, j, k_rozhod : Integer;
begin
k_rozhod := (l + r) div 2;
i := l; j := r;
while i<j do begin
while Porovnej(i,k_rozhod)<0 do i:=i+1;
while Porovnej(k_rozhod,j)<0 do j:=j-1;
if i <= j then begin
Prehod(i,j);
if i=k_rozhod then k_rozhod:=j
else if j=k_rozhod then k_rozhod:=i;
i:=i+1; j:=j-1;
end;
end;
if l < j then Setrid(l, j);
if i < r then Setrid(i, r);
end;
end.
Procedury pro porovnání a přehozní z minulé verse s procedurálními parametry byly tentokrát nahrazeny metodami. Tento objekt je ale nehotový, umí sice třídit ale přitom nemá co a tady ani tedy neví jak to nic porovnávat a přehazovat. Metody Porovnej a Prehdo jsou sice tedy deklarovány, aby mohly být použity v metodě Setrid, ale jejich kód se odkládá do budoucna. Tentokrát ale nejde jako u předběžné (forward) deklarace jen o odložení na pozdější místo v daném modulu, fukce jsou označeny jako abstraktní a pokud je použít skončí behovou chybou. Deklarce funkcí je odložena až do doby, kdy bude co třídit. Vezmeme tedy data (pole reálných čísel) a přidáme k nim metody pro porovnácní dvou prvků a jejich přehození a vytvoříme z nich potomka objektu typu tTridic jak je tomu v následujícím programu. Navíc přidáme inicializační metodu, která tam musí být z technických důvodů (a ještě navíc má místo slova procedure psáno constructor) a využijeme ji k obsazení pole náhodnými čísly. Navíc i z kontroly správnosti setřídění učiníme metodu v souladu s principy OOP. Tak dostaneme: program ObjTridTest;
uses ObjTridic;
type tSeznamRCisel = object(tTridic) // je to potomek tTridic
Data : array [0..220000] of real;
constructor Init; // naprosto nezbytný kvůli virtuálním metodám
function Porovnej( j,k : integer ) : integer; virtual;
procedure Prehod( j,k : integer ); virtual;
function Zkontroluj : boolean;
end;
function tSeznamRCisel.Porovnej( j,k : integer ) : integer;
begin
if Data[j]<Data[k] then Porovnej := -1
else if Data[j]=Data[k] then Porovnej := 0
else Porovnej := +1;
end;
procedure tSeznamRCisel.Prehod( j,k : integer );
var s : real;
begin
s := Data[k];
Data[k] := Data[j];
Data[j] := s;
end;
constructor tSeznamRCisel.Init;
var i : integer;
begin
for i := Low(Data) to High(Data) do Data[i]:=random;
end;
function tSeznamRCisel.Zkontroluj : boolean;
var i : integer;
begin
Zkontroluj := false;
for i :=1 to High(Data) do if Data[i-1]>Data[i] then exit;
Zkontroluj := true;
end;
var Seznam:tSeznamRCisel;
begin
Seznam.Init;
Seznam.Setrid(0 , High(Seznam.data) );
if Seznam.Zkontroluj then Writeln('OK') else Writeln('Prusvih');
readln;
end.
Výklad (3 minuty): Dědičnost, virtuální metody, kostruktor. V případě náhrady var parametrů tečkou šlo jen o jakýsi přepis, který sice obrážel změnu pohledu na operace s daty (procedury --> metody), který ale např. ve výsledném strojovém kódu nemusí být vůbec vidět. (Nevypadá ale kód X.Init; X.Setrid; X.Zkontroluj nějak hezčeji...) Výše uvedený kód ale využívá také druhé klíčové vlastnosti zvané dědičnost. Ta představuje opravdovou změnu na všech úrovních. Tak jako v minulém příkladě bylo možno jednou provždy vyřešit quicksort a už jen kostruovat seznamy různých druhů, našly dnes objekty (hlavně kvůli dědičnosti) svoje velké využití v oblasti grafického rozhraní programů a toto hlavní použití ovlivnilo zpětně jazyk.(Na přednášce za 10 sekund říct proč...) Pro užití objektů ve vědeckých výpočtech ale chybí v ObjectPascalu některé důležité možnosti a tak s objekty skončíme výše uvedeným ilsutračním příkladem na třídění, který už tak používá dost prvků OOP aby k jejich úplnému vyložení bylo potřeba několik přednášek. Cvičení: Opět uvažujte seznam náhodných komplexních čísel, modifikujte výše uvedený typ tSeznamRCisel na tSeznamCCisel a pridejte do nej metody SetridPodleRealCasti, SetridPodleImagCasti a SetridPodleAbsHodnoty a asi uvazujte i tri testy ZkontrolujPodleRealCasti atd... Zkompilujte. Vyzkoušejte. |
. |