Pole je druh proměnné, která obsahuje několik stejných proměnných. Typickým polem je vektor. Vektor nadefinujeme v oddílu var příkazem
var a : array[1..10] of real;
, kde a je jméno vektoru, [1..10] je rozsah, ve kterém se pohybuje index pole v průběhu programu (celé číslo), real je typ jednotlivých položek tohoto pole. Napravo od klíčového slova of může být jakýkoli typ, tedy i znovu array. Pole o dvou indexech, tedy klasická matice, tedy bude mít definici
var b : array[1..10] of array[1..10] of real;
, kterou ovšem můžeme zapsat zkráceně pomocí čárky
var b : array[1..10,1..10] of real;
Definice jsou totožné, a tomu odpovídá i rozmístění jednotlivých buněk pole v paměti (matice je zapsána po sloupcích). Matici o třech dimenzích nadefinujeme například následovně:
var c : array[1..7,1..9,1..5] of real;
Ve výkonné části programu (mezi závorkami begin a end) se na matici odkazujeme tak, že index příslušného prvku napíšeme do hranatých závorek. Indexem může být jakýkoli výraz, nabývající celočíselných hodnot v rozsahu, uvedeném v deklaraci proměnné typu pole.
a[5] := 11; b[3,7] := 5; c[2,4,3] := b[2,7]+a[9]; b[i,j] := c[8-j,j,i]+a[i+j];
V Pascalu nelze pracovat s jinými prvky matice, než s jednotlivými buňkami (komentář).
Největší problém je vymyslet jednoduchý příklad, na kterém se procvičí pole. Pomoci by nám mohl SpinEdit.
SpinEdit je jedna z ukázkových komponent, které nalezneme na liště Samples. Vychází z komponenty Edit, která umožňuje zadávat řetězce. SpinEdit je upraven tak, aby bylo možné zadávat pouze celá čísla, a přiloženými šipičkami lze tato čísla dokonce zvětšovat či zmenšovat (po jedné).
Otevřte nový projekt (nezapomeňte jej uložit do adresáře, kde máte právo na zápis). Nastavte u něj následující preperties:
Value | 1 | počáteční hodnota pracovní proměnné |
MinValue | 1 | nejmenší nastavitelná hodnota |
MaxValue | 10 | největši nastavitelná hodnota |
Hodnotu SpinEdit1.Value tak bude možné měnit jen od jedné do deseti. Pokud si nyní nadeklarujeme pole o deseti prvcích, můžeme si hodnotu zvoleného prvku měnit v připojeném editboxu. Přidejte si proto ještě komponentu Edit1. Na začátek programu, do místa, kde je nadeklarována proměnná Form1, přidejte další řádek s deklarací matice o deseti prvcích (v následujícím příkladu jsou položky pole typu integer, aby se lépe zobrazovaly)
var Form1: TForm1; a : array[1..10] of integer;
Nyní obsloužíme změnu hodnoty proměnné SpinEdit1.Value. Pokud ji někdo změnil, zřejmě chce zobrazit zvolený prvek:
procedure TForm1.SpinEdit1Change(Sender: TObject); begin Edit1.Text := IntToStr(a[SpinEdit1.Value]); end;
Aby byl program použitelný, mělo by být možné hodnotu jednotlivých prvků pole také měnit. Zdá se logické je přepisovat v přiloženém editboxu. Vhodné místo je obsluha události Edit1Change:
procedure TForm1.Edit1Change(Sender: TObject); begin a[SpinEdit1.Value]:=StrToInt(Edit1.Text); end;
Takto provedená obsluha bude reagovat špatně na situaci, kdy číslo nejprve smažeme, a pak místo něj napíšeme jiné. Situace by se měla řešit obsloužením vyjímky, ale pro naší potřebu bude stačit, když se nebudeme pokoušet převádět prázdný řetězec (v Pascalu se zapíše jako dva apostrofy za sebou)
if Edit1.Text > '' then a[SpinEdit1.Value]:=StrToInt(Edit1.Text);
Pokud program přesto skončí na nějaké chybě, je nejlépe jej spustit znovu (v menu Run položkou Run, nebo ikonkou).
Na počátku jsou v proměnné vždy nějaké neurčité počáteční hodnoty. Ačkoli v našem případě to budou nejčastěji nuly (počítač byl před chvílí spuštěn), nemusí tomu tak být vždy. Bylo by proto dobré matici něčím naplnit. K tomu by se hodil příkaz cyklu. Zkuste si vytvořit dvě tlačítka, jedno matici naplní vzestupnými čísly, druhé sestupnými.
V Pascalu je několik příkazů cyklu, pro daný účel (předem se ví, kolik proběhne cyklů) se nejlépe hodí cyklus for - do. Má následující syntaxi:
for přiřazení_počáteční_hodnoty_proměnné_cyklu to konečná_hodnota_proměnné_cyklu do příkaz;
Místo jednoho příkazu se může v cyklu provádět více příkazů, pokud jsou uzavřeny mezi programové závorky begin - end. Proměnná cyklu musí být nadeklarována předem, nejčastěji jako lokální proměnná (za hlavičkou procedury). Tato proměnná pak nabývá v cyklu postupně hodnot od počáteční do konečné (včetně). (Je-li již na počátku přiřazena počáteční hodnota větší než konečná, neproběhne cyklus ani jednou). Například obsluha tlačítka, které vyplní matici vzestupnými čísly, může vypadat následovně:
procedure TForm1.Button1Click(Sender: TObject); var i : integer; begin for i:=1 to 10 do a[i] := i*3 + 11; end;
Pole se naplní čísly 14,17, 20 a tak dále, jak je popsáno v proceduře. V proceduře chybí řádek
Edit1.Text := IntToStr(a[SpinEdit1.Value]);
Měl by být napsán na konci (bezprostředně před end), aby se změněný prvek matice ihned také zobrazil. Obdobně bude vypadat obsluha druhého tlačítka, které má naplnit matici nějakými sestupnými čísly.
Cyklus for - do postupuje vždy po jedné od zadané hodnoty až po konečnou. Existuje ovšem varianta cyklu, kdy je tento krok záporný; u tohoto cyklu je místo klíčového slova to slovo downto. Například cyklus
for i:=10 downto 1 do a[i] := i*3 + 11;
naplní pole vzestupnými čísly (není-li vazba na předchozí průchod cyklu, je jedno, z které strany polem procházíme).
Pokud je třeba použít cyklus, který má jiný krok, musíme buďto proměnnou cyklu před zamýšleným použitím vynásobit nebo vydělit nějakým (např. reálným) číslem, nebo použít jiné příkazy cyklu.
Memobox je jakýsi zápisník, kam lze napsat i kratší textový soubor. Nám by se mohl hodit na zobrazení obsahu proměnné typu pole. Musíme si ale zvyknout na poněkud jiný způsob práce.
Memo obsahuje svůj text v property Lines, která je typu TStrings. Do Lines lze psát stejně jako do matice, ale před zápisem je třeba pečlivě zkontrolovat, zda řádek, do kterého se chystáme zapisovat, v této property skutečně existuje; jinak by mohlo dojít k poškození paměti (například jiné proměnné). Počet právě přítomných řádek udává property Lines.Count. Jednodužší je přizpůsobit se a místo přímého zápisu pracovat specifickým postupem, obvyklým u TStrings: Nejprve všechny řádky smazat (Lines.Clear), a pak je jeden po druhém znovu vložit pomocí specielní procedury Lines.Add, která doplňuje řádek na konec textu. Zobrazení obsahu pole v proměnné Memo1 by se pak provedlo následovně (připsáno u tlačítka Button3):
procedure TForm1.Button3Click(Sender: TObject); var i : integer; begin Memo1.Lines.Clear; for i:=1 to 10 do Memo1.Lines.Add(IntToStr(a[i])); end;
Asi rozumnější místo, než další tlačítko, by bylo tuto obsluhu napsat do míst, kde se mění obsah matice. Je to jednak obsluha Edit1Change, jednak obsluha Button1Click (plní obsah pole).
Pokud potřebujeme naopak zapsat změněný obsah Memo1.Lines do matice a (například obsluha Memo1Change, zgenerovaná dvojklikem na komponentě Memo1), můžeme jednotlivé položky číst v cyklu přímo z jednotlivých řádek. Nejprve se přesvědčíme, že řádek je alespoň deset, a pak se je pokusíme převést. Chyby se budeme učit obsluhovat až v předmětu OOP.
procedure TForm1.Memo1Change(Sender: TObject); var i : integer; begin if Memo1.Lines.Count >= 10 then for i:=1 to 10 do a[i]:=StrToInt(Memo1.Lines[i-1]); end;
U indexu pro Memo1.Lines musí být "-1", protože řádky v Memo jsou číslovány od nuly (na rozdíl od naší matice). Vlastně by měl ještě opraven obsah edit boxu, aby zobrazoval stále aktuální hodnoty. Nejlépe z proměnné a, aby se nepřenášely případné chyby. Následující řádek by proto měl být doplněn na konec předchozího textu (před end):
Edit1.Text := IntToStr(a[SpinEdit1.Value]);
Náhodné proměnné se v Pascalech firmy Borland generují pomocí funkce random. Parametr této funkce udává rozsah, ve kterém se bude nacházet výsledek. Například
i := random(10);
bude generovat náhodná čísla v rozsahu od nuly do devíti včetně (celkem deset číslic) s rovnoměrným rozdělením. Pokud potřebujete jiný rozsah čísel, musíte si je příslušně upravit. Například
x := random(1000)/100;
generuje náhodná čísla od nuly (nic jsme nepřičetli) do 9.99, s krokem jedna setina (kdyby mělo generovat čísla do deseti včetně, musel by argument funkce být o jedničku vyšší, 1001).
Pro ladění programů je zpravidla užitečné, že funkce random generuje vždy tutéž posloupnost čísel. Při krokování pak program probíhá stejnými úseky se stejnými hodnotami - pokud opravíme dílčí chybu, není problém se do téhož místa (téže situace) vrátit. Při chodu programu by to byl nežádoucí efekt (například simulátor hodu kostkou by ukazoval stále tutéž posloupnost čísel). Funkce random přitom, stejně jako v jakémkoli jiném programovacím jazyce, generuje stále tutéž, ovšem velmi dlouhou řadu. Řešením problému náhodných čísel pak je, že se řada začne číst z jiného místa. Změna začátku pro generování řady se provádí zavoláním procedury
randomize;
Nový začátek se odvodí od systémového času a dalších vnitřních údajů operačního systému. Číselná řada v Delphi je tak dlouhá, že nehrozí, že by někdo mohl vysledovat opakování posloupnosti. O rovnoměrnosti generované řady se lze přesvědčit nejlépe v prostředí grafiky, pokud nám na to v příslušném cvičení zbyde čas.
Program naleznete v přiloženém souboru.