Vítám vás ve druhém dílu malého seriálu o jazyku Object Pascal určeném pro začátečníky, kteří se chtějí seznámit se základními principy a pojmy z oblasti programování (snad to není moc troufalá formulace…). Tento díl se zabývá datovými typy. Povídání o typech rozložím do několika dílů, dnes tedy 1. část.
Opakování z dílu 01
Věřím, že se Vám už podařilo nainstalovat a zprovoznit naše IDE (Integrated Development Environment, Integrované vývojové prostředí), čili Delphi. A pak hlavně po otevření konzoly napsat nějaký pascalovský prográmek a spustit jej ikonkou se zelenou šipkou či klávesou F9 (o tom jsem se minule zapomněl zmínit). A nezapomeňte před konec programu vložit příkaz readLn;
, aby se vám hned nezavřelo okno aplikace s výsledky – program pak čeká ještě na stisk libovolné klávesy a Enteru. Jistě jste taky sami přišli na to, že readLn
či writeLn
znamená readLine, writeLine, čili čtení řádku či zápis na řádek. Texty (řetězce znaků) se v těchto příkazech uzavírají do apostrofů ('text'). Pokud chcete psát v české klávesnici, napíšete apostrof kombinací Shift + \ (klávesa hned vlevo vedle Enteru) nebo Shift + klávesa vlevo od BackSpace nebo Alt + 39.
Snad se vám i podařilo sestavit prográmek z úkolu 2: V konzole Delphi jste měli vytvořit prográmek, který by řešil lineární rovnici a * x + b = 0
. Správně jsem měl zadání ještě upřesnit popisem vstupních a výstupních hodnot, čili do programu budou vstupovat dvě zřejmě reálná čísla (koeficienty rovnice) a vystupovat má jedno číslo (kořen, pokud ovšem existuje). Už víme, že číslo bude zastupovat nějaká proměnná, tedy místo v paměti, které hodnotu uchovává. Proměnná musí mít svůj název – identifikátor, kterým se na ni odkazujeme. Musíme si tedy také zvolit identifikátory zřejmě tří proměnných, nabízí se stejná písmenka jako ve skutečné rovnici, tedy a, b pro koeficienty a x pro kořen.
Program mohl vypadat takto (nejdřív nejjednodušší varianta):
program linearniRovniceSnadno;
{$APPTYPE CONSOLE} // direktiva Delphi pro konzolu, nutná
uses SysUtils; // přiložená knihovna Delphi
var a,b,x: real; // deklarace proměnných,
// příkaz var od variable - proměnná
begin
writeLn('Zadejte hodnoty koeficientu rovnice a*x+b=0:');
write(' a: '); readLn(a);
write(' b: '); readLn(b);
x := -b/a; // přiřazovací příkaz pro výpočet kořenu
// a přiřazení výsledku do proměnné x
writeLn('Hodnota korenu je' , x);
readLn; // pozastaví běh programu do stisku Enteru
end.
Tento prográmek má dva problémy:
- Pokud zadáme třeba koeficienty a=2 a b=4, měl by být kořen x=-2. Vypíše se ale -2.00000000000000E+0000. To je ale v pořádku, je to -2*10^0, čili -2. Pokud chcete zjednodušit zápis výsledku, upravte řádek výpisu třeba takto:
writeLn('Hodnota korenu je' , x:2:3); // výpis minimálně na 2 platná místa // (počet míst se doplní dle potřeby) // a 3 desetinná místa za desetinnou čárkou
- Horší je, že pokud zadáme třeba a=0, b=4, skončí program chybou – nelze přece dělit nulou. Pro ošetření této situace upravme program třeba dle následujícího výpisu, jistě bez výkladu pochopíte smysl podmíněného příkazu if-then-else (jestliže-pak-jinak).
program linearniRovnice;
{$APPTYPE CONSOLE} // direktiva Delphi pro konzolu
uses SysUtils; // přiložená knihovna Delphi
var a,b,x: real; // deklarace proměnných
begin
writeLn('Zadejte hodnoty koeficientu rovnice ax+b=0:');
write(' a: '); readLn(a);
write(' b: '); readLn(b);
if a=0
then
begin
if b=0
then writeLn('Tato rovnice ma nekonecne mnoho korenu')
else writeLn('Tato rovnice nema zadny koren');
end
else
begin
x:=-b/a;
writeLn('Rovnice ma koren' ,x:2:3);
end;
readLn;
end.
Poznámka: Všimněte si, že je tu výjimka ve zdánlivém pravidle, že za každým řádkem bychom měli udělat středník. Středník totiž ukončuje příkaz, a když v podmíněném příkazu následuje za částí then
ještě část else
, je to pořád jeden podmíněný příkaz. Takže před else
středník napsat nemůžeme. A dále, jak už víme z minula, pokud je v části programu víc příkazů, jedná se o blok programu, který ohraničíme příkazy begin
a end
(v prvním případu to zde ani nebylo nutné, protože podmíněný příkaz if-then-else je jediným příkazem, i když zabírá více řádků).
Konec opakování.
Datové typy v Pascalu, 1. část – přípustné hodnoty
Datové typy v Pascalu
Každá proměnná v Pascalu musí mít určeny tři věci:
- velikost proměnné (počet bytů), aby si program mohl pro proměnnou na začátku rezervovat vhodné místo v paměti,
- přípustné hodnoty (např. celá čísla, desetinná čísla, znaky, řetězce znaků atd.),
- přípustné operace, které lze s danou proměnnou provádět (např. násobení, dělení, nalezení bezprostředního následníka, nalezení bezprostředního předchůdce, výpočet odmocniny…).
Všechny tyto údaje jsou shromážděny v tzv. datovém typu. Typů je v Pascalu víc, rozdělit je můžeme na jednoduché a strukturované. Jiné dělení je na typy standardní a definované uživatelem, popř. také na typy ordinální či neordinální. Zde začneme s typy jednoduchými, přitom některé z nich budou standardní a jiné definované uživatelem, většina z nich je ordinální, jeden ne.
Rozdělení jednoduchých datových typů:
Datový typ | Označení typu (klíčové slovo v Pascalu) |
Datový typ celých čísel | integer |
Datový typ znak | char |
Datový typ výčet | název vytváří sám programátor |
Datový typ interval | název vytváří sám programátor |
Datový typ logických hodnot | boolean |
Datový typ reálných čísel | real |
První až pátý z těchto typů nazýváme ordinální typy, protože jejich prvky mají jasně stanovené pořadí. Ke každému prvku můžeme najít předchůdce i následovníka (pokud existuje). Př.: k celému číslu 23 existuje předchůdce (22) i následovník (24). Reálná čísla mají jistě také své pořadí, ale bylo by velmi pracné hledat následovníka či předchůdce, berou se tedy jako neordinální.
Úkol k promyšlení
- Do jaké paměti si program uloží při své práci proměnné? (RAM, pevný disk, disketa, flash disk, CD, DVD).
- Proč je potřeba deklarovat proměnnou před jejím použitím v programu (víme, že se to v Pascalu provádí příkazem
var
)?
Odpovědi najdete na konci dílu.
Hodnoty těchto datových typů
- Typ integer (celá čísla): jeho rozsah je dán implementací Pascalu. Základní typ integer měl dřív velikost od -32768 do 32767 (což kdysi stačilo a hlavně odpovídalo možnostem počítače). V Object Pascalu v Delphi je to víc. Existují také další varianty celočíselného typu, které můžeme zvolit, pokud potřebujeme větší rozsah, nebo naopak víme, že proměnná bude mít rozsah menší a chceme ušetřit paměť. Přehled variant typu integer v Pascalu:
- Shortint – znaménkový 8bitový (-128…127)
- Integer – znaménkový 32bitový (-2147483648…2147483647)
- Longint – znaménkový 32bitový (-2147483648…2147483647)
- Byte – bezznaménkový 8bitový (0…255)
- Word – bezznaménkový 16bitový (0…65535), atd.
- Typ char (znak): zkratka z angl. character (písmeno, znak). Proměnná tohoto typu bude obsahovat jediný znak (z ASCII kódu). Př.: a, A, *, @, … Pozor, podobně jako texty (řetězce) musíme psát v Pascalu znaky do apostrofů ('a' apod.)
- Typ výčet: programátor si sám nadefinuje uspořádanou množinu konstant. Příklad:
Poprvé jsme se zde setkali s klíčovým slovemtype TypBarva = (bila, cervena, modra, zelena, zluta, cerna); TypKarta = (kara, piky, srdce, krize); TypPohlavi = (muz, zena);
type
, kterým začíná část programu, ve které se deklarují (zavádějí) typy proměnných. Názvy typů doporučuji odlišit od názvů proměnných, třeba předponou Typ na začátku (samotné písmeno T bych nechal rezervované pro označení třídy v Delphi, viz další díl o OOP).
Za částítype
následuje nám už známá část deklarace proměnných (var
), ve které zavádí programátor proměnné a přiřazuje jim právě typy – buď už připravené Pascalem (standardní, např. integer, char, real), nebo programátorem vytvořené, třeba právě výčet. Příklad deklarace následující za definicí typů:
Konstanty, definované ve výčtovém typu, získávají definicí i svoje pořadí (je udáno celočíselnou hodnotou, kterou my sice nevidíme, ale každé hodnotě je přiřazena, a to od nuly výše). Takže je typ výčet také ordinální. S výčtovým typem ale myslím moc pracovat nebudete.var pohlavi: Tpohlavi; barvaAuta: Tbarva; barvaKola, barvaKvetin, barvaOci: Tbarva; // při deklaraci lze na jeden řádek zapsat více proměnných téhož typu
- Typ interval: ten může být pro nás užitečný. Typ interval je vždy podmnožinou nějakého jiného ordinálního typu zvaného hostitelský typ, např. integer, longint apod. Dolní a horní mez musí být stejného ordinálního typu a první musí být menší nebo rovna druhé.
Příklad deklarace typů interval a pak proměnných s těmito typy:
Podobnou definicí typů interval nejen ušetříme paměť, ale také dáváme proměnným jasný povolený rozsah, a tím získáváme i kontrolu, jestli během programu některá proměnná tento rozsah nepřekročí (program by pak nahlásil chybu).type TypPocetStudentu = 1 .. 60; TypPocetDnu = 1 .. 31; TypPocetMesicu = 1 .. 12; TypIndex = 0 .. 10; var počet_1A, počet_2A, počet_2B: TypPocetStudentu; den, denNarozeni: TypPocetDnu; mesic, mesicNarozeni: TypPocetMesicu; index1, index2, i, j: TypIndex;
- Typ boolean (logických hodnot): obsahuje jen dvě hodnoty: true (pravda) a false (nepravda). Použijeme jej u proměnných, které mohou nabýt jen těchto dvou hodnot, setkáme se s ním také při vyhodnocování logických výrazů apod. Příklad:
(Zde není nutné definovat nejdřív v části type nějaký typ, použijeme hotový typ boolean rovnou v části programuvar jePlnolety: boolean;
var
.) - Typ real (reálných čísel): pro čísla s desetinnou čárkou, říká se reálná, ale přesněji jde o racionální čísla (desetinných míst může přece být v paměti PC vždy jen konečný počet). Čísla typu real se v programu zapisují s desetinnou tečkou nebo v semilogaritmickém tvaru, např.: 15.45, 3.25E-10 (to je 3.25*10-10) , 1e10 (to je 1*1010).
Jako typ integer nedovoluje real pracovat s libovolně velkými čísly. Implementací je stanoven rozsah, v Delphi kladné hodnoty asi od 10-324 do 10308, obdobně záporné.
Pojmy k zapamatování
Bylo by dobré vědět, že, a zhruba proč, datové typy existují, jak si vytvořit uživatelský typ (příkaz type
, např. pro typ interval nebo výčet) a že typy mají svůj rozsah hodnot, o který se většinou nemusíme starat, ale víme o nebezpečí jeho překročení.
Příklad k překročení rozsahu hodnot datového typu
Např. při výpočtu faktoriálu pomocí Delphi (prográmek následuje, vyzkoušejte) zjistíme tyto hodnoty:
Číslo (n) | Faktoriál čísla (n!) |
10 | 3628800 |
11 | 39916800 |
12 | 479001600 |
14 | 1278945280 |
17 | -288522240 |
První tři hodnoty jsou dobře, ale 14! je ve skutečnosti 87 178 291 200 a žádný faktoriál nemůže být záporný!
Když si projdete rozsah typu integer v bodu 1 výše, přijdete na to, co způsobuje chybu. (Odpověď na konci pod bodem c.)
Přikládám zdrojový text prográmku pro výpočet faktoriálu, který jsem použil. Zkuste ho zkopírovat do Delphi a použít. Úvahou nebo porovnáním s kalkulačkou (třeba tou z Windows) zjistěte, zde 13! je v našem programu ještě v pořádku. V programu jsou nějaké věci, které v kurzu ještě nebyly, použil jsem poprvé cyklus, a to cyklus repeat-until (opakuj-až do té doby, kdy) a cyklus for-to-do (pro-až k-prováděj). Syntaxe Pascalu je opět myslím tak jasná, že to pochopíte bez dalšího vysvětlování. K cyklům se ale ještě dostaneme.
program faktorial;
{$APPTYPE CONSOLE}
uses SysUtils;
var fakt: longint;
i,n: integer;
pokracovat: char;
begin
pokracovat := 'a';
repeat
write('Zadejte cislo, z nehoz se ma urcit faktorial: ');
readLn(n);
fakt:=1;
for i:=1 to n do
fakt:=fakt*i;
writeLn('hodnota faktorialu cisla ', n,' je ', fakt);
writeLn;
write('Pro pokracovani napiste pismeno a, ');
write('pro ukonceni jiny znak a Enter: ');
readLn(pokracovat);
until pokracovat <> 'a';
end.
Řešení otázek z tohoto dílu
- Do jaké paměti si program uloží při své práci proměnné? (RAM, pevný disk, disketa, flash disk, CD, DVD)
Jistě do operační paměti – RAM. Proto pokud budeme chtít, aby data z programu zůstala trvale uložena, budeme je muset v programu uložit do nějakého souboru na HD (o tom také později). - Proč je potřeba deklarovat proměnnou před jejím použitím v programu (víme, že se to v Pascalu provádí příkazem
var
)?
Právě kvůli operační paměti. Program si tak může hned na začátku své práce rezervovat v RAMce paměťová místa, se kterými pak bude prostřednictvím proměnných pracovat. A díky typům s jejich známým rozsahem hodnot může rezervovat pro každou proměnnou akorát tolik paměti, kolik jí opravdu potřebuje.
Kromě toho je deklarace pro programátora významnou pomůckou při psaní programu: mnohokrát se při psaní programu přepíšete (třeba místo názvu proměnné čili identifikátoru pokracovat jsem mohl podruhé napsat porkacovat) a překladač vás na tuto chybu sám upozorní, protože proměnnou porkacovat nezná! V některých programovacích jazycích (JavaScript) proměnné deklarovat nemusíte. Dá se to brát jako zjednodušení práce, ale paměť RAM se musí o proměnné postarat stejně, akorát že si pro všechny musí rezervovat dostatečný prostor, no a v malých prográmcích v JavaScriptu se celkem bez hlídání proměnných obejdeme, ale ve větších programech bych se této pomoci nerad vzdával. - Faktoriál: První tři hodnoty jsou dobře, ale 14! je ve skutečnosti 87 178 291 200 a žádný faktoriál nemůže být záporný! Když si projdete rozsah typu integer v bodu 1 výše, přijdete na to, co způsobuje chybu.
Jistě, je to omezený rozsah hodnot typu, zde integer nebo longint (v naší verzi Delphi mají stejný rozsah). Horní mez je 2.147.483.647, což nebylo překročeno u 12! (479.001.600), ale u 14! či 17! už rozhodně ano. Pokud by měl být rozsah překročen, pokračuje se jaksi znovu od začátku, proto může být výsledná hodnota i záporná (rozsah longint je přece od -2.147.483.648 do 2.147.483.647). Proto se správně nespočítá už ani 13!, který není 1.932.053.504, ale 6.227.020.800, což už překročí povolený rozsah typu.
Úkoly do příště
- Vyzkoušejte a promyslete si program pro výpočet faktoriálu.
- Napište jednoduché prográmky, které budou třeba:
- požadovat zadání celého čísla a vypíší jeho druhou a třetí mocninu (vše může být integer)
- požadovat zadání libovolného čísla a vypíší jeho druhou odmocninu (bude potřeba typ real, pro výpočet odmocniny použijte funkci
sqrt
, kterou Delphi nabízí, např. příkazemodmocnina := sqrt(cislo);
. - už s použitím cyklu, třeba for – např. napsat program, který zjistí, jestli zadané číslo je prvočíslo.
A co příště?
Zkusím začít se základními pojmy z objektově orientovaného programování.
A jak říkají na závěr jednoho pěkného rozhlasového pořadu: Mějte se dobře a buďte na sebe hodní :-).