V dnešním cvičení si zkusíme udělat jednoduchý program, který může úspěšně nahradit kalkulačku. Ovšem vzhledem k tomu, že Delphi jsou zvyklé v edit boxech pracovat s texty (s řetězcovými proměnnými, string), je nutné text nejprve vždy převést na číslo, a číslo pak zpět na text. Pro celá čísla to provádí konverzní funkce StrToInt a IntToStr.
Příklad - nadefinujme si proměnné i jako celočíselnou, s jako řetězec znaků. Příklad je opět v klasickém Pascalu:
var i : integer; s : string; begin s := IntToStr(i); i := StrToInt(s);
Povšimněte si, že parametry funkcí jsou v kulatých závorkách. Podotýkám, že nalevo od znaku přiřazení (:=) nemůže být žádná funkce. V našem případě musíme udělat konverze obě v jednom řádku, abychom nemuseli deklarovat lokální proměnnou. Vytvořme si například program se dvěma edit boxy a tlačítkem, po jehož stisknutí se obsah edit boxů převede na čísla, ta se sečtou a výsledek se zapíše do prvního z edit boxů. Graficky si vytvoříme následující Form1:
Button1 by mohl být užší a mohl by mít property Caption (pomocí F11, Object Inspectoru) nastavenou na hodnotu řetězce "+" (sčítací znaménko). Edit1 a Edit2 by mohly mít property Text nastavenou na počáteční hodnotu 0 (nula). Button2 by mohlo sloužit jako ukončovací - mohlo by mít Caption nastavenu na "konec" a dvojklikem přiřazenu obsluhu "Form1.Close;". Obsluha sčítacího tlačítka by mohla být následující:
Form1.Edit1.Text := IntToStr(StrToInt(Form1.Edit1.Text) +StrToInt(Form1.Edit2.Text));
Poznámka: Pokud jsme uvnitř proměnné Form1 (doslova - pokud se podíváte na definici procedur do první kapitoly "type", kde je deklarován Form1, uvidíte, že procedury jsou jeho součástí), nemusíme text "Form1." psát; v dalším jej budu uvádět jen tam, kde to přispěje zpřehlednění kódu (například při práci s vlastnostmi formuláře). Stejný význam tedy má:
Edit1.Text := IntToStr(StrToInt(Edit1.Text) +StrToInt(Edit2.Text));
Obsah každého editboxu se nejprve převede na celé číslo, výsledky se sečtou, to se převede na řetězec, a ten již lze přiřadit property Text komponenty Edit1.
Úplně stejně by se provádělo i odčítání a násobení. Číslo lze do edit boxu samozřejmě napsat včetně záporného znaménka. Zkuste si udělat další tlačítka pro odčítání a násobení. ("-", "*").
Pokud píšete obdobný text, jako je již v programu jinde, označte jej myší a přeneste kombinací kláves Ctrl a C do schránky. Pak vygenerujte proceduru pro obsluhu tlačítka (dvojklikem na tlačítko) a schránku do ní zkopírujte kombinací kláves Ctrl a V. V našem případě následně místo znaménka plus napište pomlčku nebo hvězdičku.
Poněkud složitější je situace s dělením, protože se jedná o celá čísla (lomítko vrací jako výsledek číslo v plovoucí řádové čárce). Operace celočíselného dělení se píší slovy div a mod a představují celou část podílu a zbytek po dělení. Pro čtyři celá čísla, například proměnné i,j,k a l, by zápis vypadal následovně:
i := j div k; {celá část podílu} l := j mod k; {zbytek po celočíselném dělení}
Před a za slovy mod a div musí být nějaký oddělovač, nejčastěji je to mezera, závorka, nebo konec řádky. Funkce dělení tedy bude mít dva výsledky. Zkusme si první napsat do prvního edit boxu, druhý do druhého (obsluha tohoto tlačítka bude mít dva řádky).
Form1.Edit1.Text := IntToStr(StrToInt(Form1.Edit1.Text) div StrToInt(Form1.Edit2.Text)); Form1.Edit2.Text := IntToStr(StrToInt(Form1.Edit1.Text) mod StrToInt(Form1.Edit2.Text));
Prvním příkazem se do proměnné Form1.Edit1.Text vloží hodnota podílu. V druhém příkazu (třetí a čtvrtý řádek) se pak již počítá se špatnou hodnotou. Řěšením je si obsah Form1.Edit1.Text na chvilku schovat do lokální proměnné. Celá procedura pak bude vypadat následovně (dělení je na tlačítku číslo 4, které má změněnu Caption, nikoli property Name):
procedure TForm1.Button4Click(Sender: TObject); var s : string[33]; {maximální délka řetězce - 10 by asi stačilo} begin s := Form1.Edit1.Text; Form1.Edit1.Text := IntToStr(StrToInt(s) div StrToInt(Form1.Edit2.Text)); Form1.Edit2.Text := IntToStr(StrToInt(s) mod StrToInt(Form1.Edit2.Text)); end;
Proměnná se vytvoří při vstupu do procedury vždy znovu, v zásadě se v ní objeví nový náhodný obsah. Prosím, nenechte se zmást tím, že při ladění (spouštění programu v prostředí Delphi) jsou proměnné často na začátku vynulovány a při opakovaném volání procedury zde zůstává poslední hodnota proměnných. Při spouštění samotného přeloženého .exe souboru tomu tak není.
Poznámka - v textu používám šedé či světlejší odstíny pro části, které vygeneroval počítač, či které jsou přejaty z minulého příkladu.
Pokud v edit boxech chceme zadávat čísla v plovoucí řádové čárce, stačí změnit převodní funkce. Řetězec na číslo v plovoucí čárce převádí funkce StrToFloat, zpět funkce FloatToStr. Tato čísla také často podle převažujícího typu v deklaraci (real) označujeme jako reálná. V zápise reálného čísla se může vyskytnout nejen desetinná čárka, ale také exponent, a to za písmenem e (za kterým bezprostředně může následovat i znaménko). Naproti tomu se v něm samozřejmě nemůže vyskytovat žádná mezera ani jiný oddělovač. Například:
3
6,25
-4,7
67,56757898e-27
-83,0568707e3
Ještě jednou dodávám, že Pascal nevyžaduje dodržovat velká a malá písmena (i to "e" může být velké). Dělení reálných čísel se zapíše lomítkem. Opravte Váš program tak, aby pracoval s reálnými čísly.
Funkce se používají ve výrazech (viz následující odstavec) tak, že mají svůj argument v kulatých závorkách. Nejznámější matematické funkce jsou:
sin(x) | vrací hodnotu funkce sinus |
cos(x) | vrací hodnotu funkce kosinus |
arctan(x) | vrací hodnotu funkce arcus tangents |
ln(x) | vrací přirozený logarimus čísla x |
exp(x) | vrací hodnotu funkce ex |
sqr(x) | druhá mocnina čísla x |
sqrt(x) | druhá odmocnina z x |
pi | konstanta o hodnotě Ludolfova čísla |
Jako další bychom si mohli udělat tlačítko, po jehož stisknutí se místo hodnoty v prvním edit boxu dosadí hodnota sinu z této hodnoty. Pro tlačítko (doporučuji property Caption nastavit na "sin") zapíšeme tedy následující obsluhu:
Form1.Edit1.Text := FloatToStr(sin(StrToFloat(Form1.Edit1.Text)));
Obdobně by mohlo tlačítko "pi" dosadit tuto hodnotu:
Form1.Edit1.Text := FloatToStr(pi);
Přidejte tlačítka pro funkce cos, ln, exp a arctan. Podotýkám, že Pascal počítá v radiánech. Pokud používáte raději jinou úhlovou míru, použijte příslušný přepočet (např. sin(x/180*pi) ).
Protože k praktickému příkladu se ještě delší dobu nedostaneme, zopakujme si část teorie. Příkaz přiřazení má na pravé straně výraz. Výraz může mít tvar výpočtu, obsahující řadu proměnných a čísel, pospojovaných matematickými operátory. Existuje priorita operací - nejdříve se provede vše v závorkách, pak funkce, pak všechna násobení a dělení a jako poslední sčítání, vše zleva doprava. K prioritě operací se vrátíme při probírání logických funkcí.
Několik příkladů (vždy se jedná jen o reálná čísla):
x := sin(pi /3); y := cos(alfa + beta); z := exp( ln(a) *b); {obecná mocnina} a1:= sin(alfa)/cos(alfa); {tangenta} a2:= arctan(sqrt( 1-sqr(x)) / x); {arccos} h := (c*d + e*(f-1) +g)/(c*c + d*d); x1:= (-b+sqrt(sqr(b)-4*a*c))/2/a; x2:= (-b-sqrt(sqr(b)-4*a*c))/(2*a);
Překladač implementovaný v Delphi 4 používá následující typy proměnných v plovoucí čárce:
typ | počet platných míst | velikost v paměti |
single | 7 až 8 | 4 |
real48 | 11 až 12 | 6 |
double | 15 až 16 | 8 |
extended | 19 až 20 | 10 |
Původní real se stal generickým typem, tedy mění své vlastnosti od implementace k implementaci. Původně totiž představoval současný typ real48, který poskytuje dostatečnou přesnost výpočtu. Z důvodu rychlosti výpočtu byl převeden na double.
Pokud dojde k uschovávání velkého množství dat, má smysl používat typ single. Je úsporný a přesnost je pro technické aplikace dostatečná. Extended oproti tomu představuje vnitřní reprezentaci dat v koprocesoru Intel 8087 a jeho nástupcích, takže se ušetří konverzní funkce koprocesoru (zaokrouhlování). Zpravidla se nevyužije toho, že šířka sběrnice v operační paměti procesorů pentium je 64 bit (právě velikost typu double), protože málo kdy se povede uložit proměnnou na tu správnou adresu (jeden z osmi možných případů).
Pokud chceme výsledek výpočtu (např. hodnotu proměnné) typu integer zapsat do proměnné některého z typů v plovoucí řádové čárce, můžeme tak učinit; před zapsáním do paměti proběhne automatická konverze. V opačném směru musíme reálné číslo na celočíselné převést ručně. K tomu slouží další dvě funkce. V dalším předpokládám, že x je proměnná typu real, a i je celočíselná proměnná.
x := i; {automatická konverze} i := round(x); {zaokrouhlení} i := trunc(x); {oříznutí celé části}
Funkce trunc je označována jako zaokrouhlení směrem k nule. Pokud je číslo menší než nula, vrátí nejbližší větší celé číslo, pokud je větší než nula, nejbližší menší celé číslo. Pozor na reprezentaci reálných čísel - např. místo čísla 9 může v paměti být napsáno 8,999999999999997.
Funkce round se s trochou snahy nechá použít i na zaokrouhlování. Zaokrouhleme například číslo x na dvě desetinná místa:
x := round(x * 100)/100;
V příkladech často uvádím deklaraci proměnných, začínající slovem var, čímž naznačuji, že se proměnné deklarují v takto označené kapitole programu. Je ovšem důležité, kam vlastně můžeme dopsat toto klíčové slovo.
Zkuste si zavřít aplikaci a vygenerovat novou (File - Close All, File - New Application, ve starších verzích New Project; a samozřejmě, jako vždy u nového projektu, File - Save Project As). Podívejte se do okénka, ve kterém je zdrojový kód programu. Někde (prozatím v polovině, ale všechny generované části se umísťují za) naleznete klíčové slovo var, nějak jako
var Form1 : TForm1;
Za tuto deklaraci jediné proměnné, kterou Delphi vygeneruje, si můžete připsat deklarace vlastních proměnných. Tyto proměnné budou dostupné z celého následujícího programu a označujeme je proto jako globální. Poznámka.
Jiné místo, kde se často deklarují proměnné, je na začátku každé procedury (jak jsme zkoušeli již v příkladu s celočíselným dělením). Vygenerujte si proceduru pro obsluhu nějakého tlačítka (přidat komponentu Button1, dvojklikem vygenerovat kód). Pokud budete v této proceduře potřebovat nějakou proměnnou pro uložení mezivýsledků, můžete si kapitolu var přidat mezi klíčová slova begin a procedure. Například si můžeme nadeklarovat proměnné x a y typu extended a proměnnou i typu integer:
procedure TForm1.Button1Click(Sender: TObject); var x,y : extended; i : integer; begin {nějaký kód} end;
Je snadno představitelné, že tyto proměnné jsou dostupné jen uvnitř té procedury, kde jsou nadefinované. Při jejich použití si však je také nutno uvědomit, že Pascal je vytvoří teprve v okamžiku, kdy je tato procedura spuštěna, a zruší je okamžitě po ukončení (či vystoupení z) procedury. Pokud tedy proceduru použijeme vícekrát, budou mít tyto lokální proměnné vždy novou náhodnou počáteční hodnotu.
Poznámka: Počáteční hodnota proměnných je při tomto způsobu deklarace obecně náhodná, ať již se to týká lokálních, nebo globálních proměnných. K deklaraci proměnných se proto ještě později vrátíme.
Upravte program tak, aby používal lokální proměnné. Při výpočtu se tedy nejprve převedou obsahy Edit1 a Edit2 do proměnné, pak se provede matematická operace, a nakonec se převede výsledná proměnná do příslušného edit boxu. Hotová procedura pro sčítání tedy bude vypadat následovně (oproti svým zvyklostem uvádím nejen text, který jsem doplnil, ale i hlavičku vygenerovanou prostředím Delphi, včetně programových závorek begin a end):
procedure TForm1.Button1Click(Sender: TObject); var x,y : extended; begin x := strtofloat(form1.edit1.text); y := strtofloat(form1.edit2.text); x:=x+y; form1.edit1.text := floattostr(x); end;
Nadefinujte globální proměnnou M typu extended a použijte ji jako paměť (funkce MStore, M+, MR). Úloha vyžaduje nadefinovat globální proměnnou M (v Pascalu je možné zaměňovat malá a velká písmena), přidat tři tlačítka, a jejich příslušnou obsluhu. Deklaraci globální proměnné zde neuvádím, je v programu promenne v adresáři s dnešními úlohami. Z tohoto programu uvádím jen obsluhu druhého a třetího tlačítka.
Přičtení do paměti se obslouží příkazem:
M := M + strtofloat(form1.edit1.text);
Vyvolání paměti do komponenty Edit1 se provede následovně:
form1.edit1.text := floattostr(M);
Hotové úlohy z tohoto cvičení jsou v souboru "cisla".
Poznámka. Pro praktické použití by se hodilo, kdyby i při použití tlačítek kurzor stále zůstával v políčku Edit2. Toho by se dosáhlo tím, že by se na konci obsluhy každého tlačítka zavolala metoda SetFocus tohoto objektu, která do něj přenese kurzor:
Form1.edit2.text.setfocus;
Vytváření podobných programů je dobré procvičení použití kláves Ctrl+C a Ctrl+V, resp. Ctrl+Insert a Shift+Insert.