DELPHI Datový typ pole
Ing. Jaroslav Halva
Datový typ POLE Vodítkem pro tento kurz Delphi zabývající se p edevším konzolovými aplikacemi a základy programování pro mne byl semestr na vysoké škole. Studenti nyní p ipravují semestrální práce p edevším na téma základních po etních operací s maticemi. Rozhodl jsem se, že naše povídání ukon ím výkladem o polích, p íklad jednorozm rného pole, které bychom mohli p irovnat k vektoru, a také dvourozm rného pole, které je typickým zpracováním maticové algebry, pokud se chceme zabývat v programování tímto problémem. Cht l bych jen zd raznit, že využití polí je veliké a následující p íklad by m l student m pomoci s jejich semestrální prací, být vodítkem a zdrojem i n kterých konkrétních zpracování pro jejich i budoucí práci. Sou asn tak ukon íme blok konzolových aplikací, protože programování v Delphi má trochu jiné opodstatn ní a konzolová aplikace je pro m osobn pouze zp sob, jak simulovat jazyk Pascal.
Jednorozm rné pole - vektor Jestliže si budeme povídat o datovém typu POLE (array), nau íme se sou asn definovat vlastní datové typy. Není na tom nic složitého, je t eba si jen pamatovat, kam taková definice ve struktu e programu pat í: Datové typy definujeme p ed blokem prom nných, což je logické, když takovýto definovaný typ potom chceme použít. Když to vše zobecním, pak bych za átek kódu programu mohl prezentovat nap íklad takto: program MUJ_PROGRAM; {$N JAKÁ_DIREKTIVA} uses N jakýExterníModul; Type TMujTyp = N jakýDefinovanýTyp; Var MojeProm nná: TMujTyp; .. Definice procedur a funkcí .. BEGIN .. Hlavní t lo programu .. END.
Struktura, kterou jsem navrhl, slouží pouze jako ukázka jednotlivých ástí programu. Nejedná se o funk ní program, což je asi z n kterých názv patrné. Nepoužívejte proto tuto ást kódu ve svých programech, zbyte n byste se pokoušeli zde n co opravit. Blok TYPE je defini ní pro naše typy. V tšinou se využívá konvence taková, že každý námi definovaný typ ozna ujeme v názvu velkým „T“. V tomto bloku také provedeme definici pole, se kterým budeme v dnešní lekci pracovat. Za neme tedy vektorem, který v programu budeme definovat jako jednorozm rné pole, nyní již konkrétn : program POLE; {$APPTYPE CONSOLE} uses SysUtils;
1
DELPHI Datový typ pole
Ing. Jaroslav Halva
Type TMojePOle = array[1..10] of integer; Var MPole: TMojePOle;
Pro definici pole je klí ové slovo array. Aby byla definice prom nné typu pole iteln jší, provádí se nejprve definice typu v bloku TYPE, která je odvozena již od definované struktury v Pascalu nebo Delphi. (Bylo by možné prom nnou definovat jako: Var xxx: array[1..10] of integer. Definice typ iní kód ovšem iteln jší stejn jako dekompozice hlavního programu na jednotlivé procedury a funkce.) V definici typu íkám, že chci založit jakýsi nový typ, který bude desetiprvkovým polem celo íselných hodnot. Tento typ pak snadno použiji pro definici prom nné, se kterou chci pracovat a ešit n které úlohy pro vektor. V první chvíli se budeme muset vypo ádat s úlohou na tení hodnot vektoru, které zadá uživatel z klávesnice. Definujeme si tedy proceduru, jejíž parametrem typ pole, abychom pomocí tohoto podprogramu mohli na ítat hodnoty naší prom nné MPole: Procedure NactiPOle(Var p: TMojePOle); var i: integer; begin for i:= 1 to 10 do begin write('Zadejte prvek na pozici '); write(i); write(': '); readln(p[i]); end; end;
Zastavme se ješt na chvíli u výkladu kódu. Všimn me si, jakým zp sobem se na ítají prvky pole v procedu e READLN: Když nebudeme uvažovat použití cyklu, protože je datový typ pole strukturovaný, na ítání hodnot pole provádíme jednotliv po díl ích prvcích, to znamená v našem p ípad zadání deseti ísel na ur ité pozice, což nám udává prom nná i. Kdybych cht l nap íklad na íst (nebo zobrazit) t etí prvek pole, uvedl bych v hranatých závorkách konkrétní pozici: p[3], kde p je prom nná typu pole. Systém bu na ítá nebo vypisuje hodnotu v poli na t etí pozici. K na tení všech deseti pozic našeho pole využijeme proto FOR cyklus, protože po et prvk pole je p edem dán. Pro každou hodnotu prom nné i tedy provede program jednak n jaký výpis, jednak na tení hodnoty INTEGER na pozici i v poli typu TMojePole. Nyní proceduru ješt „vybavíme“ ošet ením vstupních hodnot: Procedure NactiPOle(Var p: TMojePOle); var i: integer; begin for i:= 1 to 10 do begin write('Zadejte prvek na pozici '); write(i); write(': '); {$I-} readln(p[i]); while IOResult <> 0 do begin
2
DELPHI Datový typ pole
Ing. Jaroslav Halva
write('Chyba v zadani! Zadejte znovu prvek na pozici '); write(i); write(': '); readln(p[i]); end; {$I+} end; end;
Ošet ení vstupu provedeme pomocí direktivy vstupn výstupních operací a cyklicky pomocí WHILE opakujeme zadání, dokud není hodnota správná. Pak teprve pokra ujeme v cyklu FOR na tením dalšího prvku pole na ur ité pozici. Abychom si ukázali jednak zp sob práce s jednotlivými prvky pole znovu a také zkontrolovali správnost na tení prvk pole, za adil jsem do programu jednoduchou proceduru, která provede výpis všech prvk námi zadaného pole. Stojí za povšimnutí op t zp sob výpisu hodnoty vždy na i-té pozici v poli: Procedure Vypis(p: TMOjePOle); var i: integer; begin for i:= 1 to 10 do writeln(p[i]); end;
Procedura je op t parametrizována typem TMojePole. Výpis se provádí op t v cyklu FOR a probíhá vždy pro hodnotu na i-té pozici. Jak je vid t, není v zápise rozdíl mezi na ítáním hodnoty na ur ité pozici nebo výpisem, takže se tato konvence dob e pamatuje. Pro naši úlohu si p ipravíme ješt funkci pro zjišt ní maxima z hodnot uložených v poli (nebo vektoru), kterou si podrobn popíšeme. Nezapisoval jsem komentá e proto do zdrojového kódu, protože nebude vysv tlení tak triviální. A jak jist p edpokládáte, bude se jednat o parametrizovanou funkci, tentokrát již pouze se vstupními parametry (volanými hodnotou), protože výsledek bude p edávat funkce samotná v podob nejv tšího ísla maxima. Function NajdiMaximum(p:TMojePOle): integer; Var i: integer; Max: integer; begin Max:= p[1]; for i:= 2 to 10 do if p[i] > Max then Max:= p[i]; Result:= Max; end;
Jak vyplývá z hlavi ky funkce, pracuje tato s typem TMojePole. Výsledná hodnota funkce je celé íslo typu INTEGER. Protože budeme pracovat s polem, definuji si také lokální prom nnou i, která mi poslouží v použití cyklu, kterým budeme procházet všechny prvky pole a zjiš ovat, který z nich by mohl být asi nejv tší. Dále jsem v deklaraci prom nných definoval prom nnou Max, do které budu pr b žn ukládat nejv tší ísla z již srovnaných prvk pole. Jestliže takto projdu všechny prvky, z stane v Max uložena nejv tší hodnota, kterou následn p edáme výstupní hodnot funkce. Podrobn si m žeme projít každý ádek: Nejprve do prom nné Max p i adíme hodnotu prvního prvku pole. Zatím je tedy tato maximální, protože jsme se k jiným ješt nedostali. Následn pomocí cyklu FOR 3
DELPHI Datový typ pole
Ing. Jaroslav Halva
p istupujeme k dalším (od druhého až po desátý) prvk m a pokud je hodnota na i-té pozici v tší než Max, pak do ní p i adíme práv hodnotu pole na pozici i. Pokud takto prozkoumáme všechny prvky pole, z stane nám v prom nné Max uložena maximální hodnota z definovaných prvk pole. Nakonec definujeme vlastní t lo programu pomocí p ipravených procedur a funkcí a m žeme se t šit na výsledek. program POLE; {$APPTYPE CONSOLE} uses SysUtils; Type TMojePOle = array[1..10] of integer; Var MPole: TMojePOle; Procedure NactiPOle(Var p: TMojePOle); var i: integer; begin for i:= 1 to 10 do begin write('Zadejte prvek na pozici '); write(i); write(': '); {$I-} readln(p[i]); while IOResult <> 0 do begin write('Chyba v zadani! Zadejte znovu prvek na pozici '); write(i); write(': '); readln(p[i]); end; {$I+} end; end; Procedure Vypis(p: TMOjePOle); var i: integer; begin for i:= 1 to 10 do writeln(p[i]); end; Function NajdiMaximum(p:TMojePOle): integer; Var i: integer; Max: integer; begin Max:= p[1]; for i:= 2 to 10 do if p[i] > Max then Max:= p[i]; Result:= Max; end; BEGIN NactiPOle(MPole); writeln;
//Nacteni promenne MPole
4
DELPHI Datový typ pole
Ing. Jaroslav Halva
Vypis(MPOle); //Vypis hodnot prom. MPole writeln; write('Nejvetsi prvek vektoru (MAXIMUM) je: '); writeln(NajdiMaximum(MPole)); //Pouziti funkce k nalezeni MAX readln; END.
Obr. 0-1 Program na zjišt ní maximální hodnoty ve vektoru o 10 prvcích.
Dvourozm rné pole – matice Ke zvládnutí po etních úloh s maticemi využijeme poznatk spojených s vektorem. Tyto úlohy nejsou nikterak složité, když si p edstavíme, že je matice také složena z vektor , pak sta í, když nap íklad pro na tení hodnot použijeme namísto jednoho cyklu dva, kdy je jeden do druhého vno ený. Také definice typu matice je pouze o udání další ady složit jší. V našem p íklad zkusíme pracovat s maticí o velikosti 5 x 5, což bude p edpokládat zadání celkem 25 hodnot v takovémto poli. Naším cílem bude ze zadaných hodnot v matici vybrat ze všech sloupc maxima a uspo ádat je do vektoru. Datový typ pro matici bychom definovali takto: Type TMojeMatice = array[1..5,1..5] of integer;
V hranatých závorkách jsou uvedeny celkem dva rozm ry pole, takže se jedná o tzv. dvourozm rné pole, které bychom mohli interpretovat práv jako matici. Pak už je definice prom nné velmi jednoduchá. Aby náš program mohl správn fungovat, nadefinuji si ješt typ pro vektor, ve kterém maxima uložíme a následn zobrazíme: Type TMojeMatice = array[1..5,1..5] of integer; TMojePOle = array[1..5] of integer; Var MMatice: TMojeMatice; MPole: TMojePole;
5
DELPHI Datový typ pole
Ing. Jaroslav Halva
První ástí ešení naší úlohy bude na tení hodnot, které samoz ejm ošet íme. Použijeme k tomu celkem dva v sob vno ené cykly FOR. Pro tento ú el má procedura krom parametru tyu TMojeMatice také lokální prom nné i a j, které ur ují pozici prvku v matici. • i – ádek • j – sloupec Procedure NactiMatici(Var m: TMojeMatice); var i,j: integer; begin for i:= 1 to 5 do for j:= 1 to 5 do begin write('Zadejte prvek na pozici ['); write(i); write(':');write(j); write(']'); write(': '); {$I-} readln(m[i,j]); while IOResult <> 0 do begin write('Chyba v zadani! Zadejte znovu prvek na pozici '); write(i); write(':');write(j); write(']'); write(': '); readln(m[i,j]); end; {$I+} end; end;
Vlastní obsah procedury není nikterak složitý, když jej budeme um t správn interpretovat. Zastavme se nejprve u cykl FOR: Cyklické na ítání dat zahajuje cyklus pro prom nnou i, potom je v tomto cyklu vno eno opakování pro prom nnou j a to vždy od 1 do 5. To znamená, že na ítané pozice p jdou asi v tomto po adí [i,j]: [1,1], [1,2], [1,3], .., [2,1], [2,2], .. atd. Z toho m žeme usuzovat, že budeme zadávat matici po ádcích, pokud index i p edstavuje ádek matice. (Což je obvyklé.) Vlastní blok cyklu je srozumitelný a velmi se podobá p edchozí úloze p i práci s vektorem. Z povšimnutí stojí zp sob zápisu tení hodnoty na pozici [i,j]. Procedura READLN na ítá prvek parametru m na pozici [i,j]. Celkem tak provedeme 25 zadání ísel, p i emž systém každé podrobí kontrole správnosti zadání hodnoty. Stejn jako v p ípad vektoru si pro p ehlednost a možnost kontroly správnosti ešení vypíšeme zadanou matici. Procedura op t využije celkem dvou cykl FOR, které budou do sebe vno eny, výpis bude proveden stejn jako procedura na tení, tedy po ádcích, na konci každého z nich budeme muset tzv. od ádkovat a za ít psát další prvky matice v ádku následujícím.
6
DELPHI Datový typ pole
Ing. Jaroslav Halva
Procedure Vypis(m: TMOjeMatice); var i,j: integer; begin for i:= 1 to 5 do begin for j:= 1 to 5 do begin write(m[i,j]); write(' '); end; writeln; end; end;
První cyklus prom nné i má v bloku nejen druhý cyklus, ale také od ádkování, takže obsahuje blok begin – end. V druhém cyklu probíhá výpis prvk matice na aktuální pozici [i,j], p i emž ízení pozic je stejné jako v p ípad na tení, takže bude procedura skute n vypisovat ádky matice. Mezi jednotlivými prvky v ádku zapisuji mezery, aby bylo možné ísla v ádku od sebe odlišit. Zbývá zpracovat proceduru, jejíž výstupním parametrem bude vektor maximálních sloupcových hodnot matice, vstupním parametrem bude samoz ejm typ matice. Na první pohled se to zdá být pom rn komplikovaná úloha, ale ve skute nosti je to jen pochopení procedur a funkcí, parametr procedury a p edevším cykl , kdy z staneme zase u FOR cyklu s ohledem na konstantní velikost jednak matice, jednak vektoru, se kterými pracujeme jako s prom nnými. procedure NajdiMaximum(m:TMojeMatice; Var p:TMojePOle); Var i,j: integer; Max: Integer; begin for i:= 1 to 5 do begin Max:= m[1,i]; for j:= 2 to 5 do if m[j,i] > Max then Max:= m[j,i]; p[i]:= Max; end; end;
Cykly jsou do sebe vno eny stejn jako v ostatních p ípadech, tedy op t opakujeme prom nnou j v cyklu prom nné i. Protože ovšem hledáme sloupcová maxima, musíme pozice prvku, se kterým aktuáln pracujeme, obrátit, tudíž se prvek identifikuje jako m[j,i]. Znamená to de facto, že i p edstavuje sloupec, j potom ádek. P itom stejn jako v p ípad vektoru i zde vždy pro každý sloupec i provádíme na tení první hodnoty m[1,i] do prom nné Max a pak ji porovnáváme se všemi zbývajícími prvky sloupce v cyklu pro j od 2 do 5. Tím pro jeden pr chod prvního cyklu (cyklus prom nné i) získáme jednu maximální hodnotu a to ze sloupce matice. Toto se opakuje celkem 5-krát pro každý sloupec matice, tedy pro opakování cyklu prom nné i. Kdybych to ekl zjednodušen , tak 5-krát opakujeme cyklus pro prom nnou j. Na konci druhého cyklu (cyklus prom nné j) pak provádíme zápis do vektoru maxim: p[i]:=Max; Zbývá p ipravit jen výpis výsledku (Procedura VypisVektor(p: TMojePOle)) a m žeme program vyzkoušet. Zde p edkládám zdrojový kód: 7
DELPHI Datový typ pole
Ing. Jaroslav Halva
program POLE; {$APPTYPE CONSOLE} uses SysUtils; Type TMojeMatice = array[1..5,1..5] of integer; TMojePOle = array[1..5] of integer; Var MMatice: TMojeMatice; MPole: TMojePole; Procedure NactiMatici(Var m: TMojeMatice); var i,j: integer; begin for i:= 1 to 5 do for j:= 1 to 5 do begin write('Zadejte prvek na pozici ['); write(i); write(':');write(j); write(']'); write(': '); {$I-} readln(m[i,j]); while IOResult <> 0 do begin write('Chyba v zadani! Zadejte znovu prvek na pozici '); write(i); write(':');write(j); write(']'); write(': '); readln(m[i,j]); end; {$I+} end; end; Procedure Vypis(m: TMOjeMatice); var i,j: integer; begin for i:= 1 to 5 do begin for j:= 1 to 5 do begin write(m[i,j]); write(' '); end; writeln; end; end; procedure NajdiMaximum(m:TMojeMatice; Var p:TMojePOle); Var i,j: integer; Max: Integer; begin for i:= 1 to 5 do begin Max:= m[1,i]; for j:= 2 to 5 do if m[j,i] > Max then Max:= m[j,i]; p[i]:= Max; end;
8
DELPHI Datový typ pole
Ing. Jaroslav Halva
end; Procedure VypisVektor(p: TMojePOle); var i:integer; begin for i:= 1 to 5 do begin write(p[i]); write(' '); end; end; BEGIN NactiMatici(MMatice); writeln; Vypis(MMatice); writeln; NajdiMaximum(MMatice, MPOle); writeln('Vektor sloupcovych maxim: '); VypisVektor(MPole); writeln; readln; END.
Správn interpretovat vno ení cykl do sebe je pom rn složité, nejjednodušší zp sob, jak poznáte správnou funkci procedury nebo funkce, kdy používám dva cykly vno ené do sebe je, když si sami p edstavíte sebe jako po íta a zkusíte si indexovat aktuální pozice. Pak nejlépe poznáte, jak by m l program správn fungovat. Až si sami vyzkoušíte n kolik takových úloh zjistíte, že to vlastn není nic složitého, je t eba získat jen prvotní p edstavu o tom, co se vlastn spušt ním takové struktury dvou cykl b hem ešení této úlohy odehrává. Pom že vám k tomu zdrojový kód a poznámky pod jednotlivými procedurami, které jsem p ipravil. Výsledek by mohl vypadat potom t eba takto:
9
DELPHI Datový typ pole
Ing. Jaroslav Halva
Obr. 0-2 Vektor sloupcových maxim
10