Základní typy proměnných

Dnes toho bude trochu víc, snad vás následující přehled neodradí - jde jen o opakování a utřídění dosavadních poznatků:

Podíváme na typy dat. Tato lekce bude teoretičtější, ale nedá se nic dělat.

Na začátku bych chtěl, abyste si ujasnili rozdíl mezi procedurou a funkcí.

Od teď budu procedury a funkce rozlišovat takto:
NázevProcedury(parametry:TypParametrů); (Jen středník - procedura )
NázevFunkce(parametry):VýslednýTyp; (Dvojtečka, výsledný typ - funkce)

Tedy Inc(Proměnná : longint); Je procedura. (=Samostatný příkaz)
a Sqrt(Proměnná : Real) : Real je funkce (= Není to samostatný příkaz, pouze vrací nějakou hodnotu toho typu, který je uveden za dvojtečkou (v tomto případě typu Real).
Například Sqrt(4) = 2. A 2 není žádný příkaz. Je to hodnota, kterou můžeme někam přiřadit, vypsat, použít jako parametr...)

Nejdříve si zopakujeme, co víme. Objevte v následujícím programu všechny chyby:
program VelkaChyba;
{Jestli se vám tento program přeloží, tak máte fakt špatný IDE}

var Znak : integer;
           Cislo : string;
           I : Real;
           Retezec : char;
           A,B,C,D:integer;
begin
 Znak:='a';
 Cislo:=15+17;
 I:=1 div 17;
 Retezec:='Ahoj lidi';
 Writeln('a'+7);
 A:=A +1;
 B:=C mod D;
end.

Program přepište (objevené chyby nezapisujte). Pokud jste na nějakou zapomněli, překladač vám ohlásí error. Jestliže jste neobjevili všechny, prostudujte si kapitolu Proměnné v oddíle pro Začátečníky a napište více vlastních programů (chybami se člověk učí).

Typy ordinální

Zatím známe pouhé 4 typy proměnných (Integer, Real,char,string), což občas nestačí. (Určitě jste už sami narazili na problém s typem integer - jeho maximální hodnota je asi 30 000. Překročíme-li tuto mez, jsou výsledky nesmyslné.)

Naštěstí Pascal nabízí hned několik celočíselných typů :

Typ Rozsah Nároky na paměť
byte 0..255 1 byte
shortint -128..127 1 byte
word 0..65535 2 byty
integer -32 768..32 767 2 byty
longint -2 147 483 648 ..
2 147 483 647
4 byty

Vybíráme-li typ pro nějakou proměnnou, je dobré volit jeho meze s rezervou.

Char

Proměnná tohoto typu může obsahovat jeden znak z ASCII tabulky. Char má blízko k celočíselným typům. Může nabývat jenom několik (256) hodnot a každá hodnota má pevně určeného předchůdce i následovníka. Chceme-li přiřadit proměnné typu char hodnotu, máme několik možností -

  1. přiřazovací příkaz +
    1. výpis znaku v apostrofech (Znak := 'A');
    2. výpis znaku přes #(Číslo znaku v ASCII tabulce) (Znak := #64)
    3. výpis znaku přes Chr(Číslo znaku) - Znak :=Chr(64); (Jedná o funkci - parametrem může být na rozdíl od zbylých možností i proměnná)
    4. výpis pomocí ^Písmeno - Znak := ^A
  2. pomocí Readln a Read

Uvědomte si:

Tabulku ASCII si vyhledejte a prostudujte sami.

Booleovské typy

Tato skupina souvisí s celými čísly. Proměnné těchto typů mohou nabývat pouze dvou hodnot - False (Nepravda) a True (Pravda).
Hodí se k odlišení pravdivostních hodnot.

Hlavní použití tohoto typu je u podmínek. (If Podmínka then, until Podmínka;...)
Podmínka se totiž sama vyhodnocuje jako Booleovský výraz.
Máme-li tedy proměnnou Konec (var Konec:Boolean); je správné přiřazení Konec:=1=2;
Výrok 1=2 se vyhodnotí jako nepravdivý a v proměnné Konec bude po přiřazení hodnota False.
Proměnné Booleovského typu lze porovnávat i mezi sebou, přičemž platí: False < True;
Můžeme tedy například psát :

Konec := ((2+1)>7)<((3+15)=18);

Tento výraz se vyhodnotí takto 3>7 je Nepravda a 3+15=18 je Pravda. Celkem tedy False < True, což je pravda, a do proměnné Konec se tedy přiřadí True.

Jelikož Booleovské proměnné nejdou rozumně sčítat (Pravda + Pravda = ?2Pravdy?), zavedly se u nich tzv. logické operace. Raději se podívejte do tabulky - zde jsou výsledky jednotlivých logických operací pro dvě nezávislé Booleovské proměnné A, B.

A B A and B A or B A xor B not A
False False False False False True
False True False True True True
True False False True True False
True True True True False False

Raději ještě uvedu jejich české ekvivalenty: and - a současně; or - nebo; xor - buď a nebo; not - opak, neplatí, že.

Má-li se výraz vyhodnotit jako Booleovská proměnná, je nutné, aby byl v závorkách!!!, jinak překladač ohlásí chybu. Je tedy správně if A then Delay(10), ale if 15>x and 8 <9 then Něco; je chybou, neboť jsme zapomněli na závorky - správně to má vypadat takto if (15>x) and (8<9) then

Pascal rozeznává několik typů Booleovských proměnných :

Název typu Nároky na paměť
Boolean 1byte
ByteBool 1byte
WordBool 2Byty
LongBool 4Byty

Nám si stačí zapamatovat typ Boolean. Ostatní typy nevyužijete (nechcete-li v Pascalu programovat operační systémy)

Hlavní využití Booleovských proměnných je asi takovéto - máme dlouhý cyklus, který vždy musí proběhnout až do konce. Jestli bude ukončen, se však zjistí už na začátku a výsledek se uloží do Booleovské proměnné Pokracuj. (Jiní programátoři využívají proměnnou Skonci nebo Konec).

Druhé využití Booleovských proměnných je použít je jako výstup funkce (Takto je definována například funkce Odd(X) - Je X liché? - Výsledkem můžou být pouze dvě hodnoty - True nebo False). Napište funkci, která zjistí, zda je zadané číslo (Longint) prvočíslem.

Hodnota Booleovské proměnné jde vypsat pomocí Writeln(Writeln(1>15);), ale nejde načíst pomocí Readln.

Výčtový typ

Občas se hodí, když proměnná nabývá pouze hodnot z nějaké množiny. K takovémuto účelu Pascal nabízí tzv. výčtový typ.
Proměnnou lze deklarovat přímo, stylem: var a : (modra, zelena, cervena);
Tento způsob má však nevýhody, lepší je tento definovat ho v sekci type takto:
type NázevTypu = PopisTypu;

Proměnná výčtového typu se tedy správně definuje pomocí:

type Barvy = (Modra, Zelena, Cervena);
      MujInteger = Integer;{V sekci type muzeme definovat i jine vlastni typy proměnných}
var Barva : Barvy;

Prvky jsou uspořádány podle pořadí, v jakém byly zapsány. (Modra < Zelena <Cervena);
Pokud to zvýší přehlednost našeho programu, má smysl definovat vlastní názvy i pro typy, které již v Pascalu jsou.
U výčtovém typu nesmí mít žádný prvek výčtu jméno již použitého identifikátoru.
Raději na příkladu:
BarvyAut = (Bila, Zelena, Modra, Cervena, Zluta, Cerna);
BarvyVlasu = (Blond,Hneda, Cerna); //chyba !! Cerna už byla použita dřív.
Těmto potížím se programátoři vyhýbají tím, že před název píšou malými písmeny zkratku jména typu, ke kterému název patří. V našem případě:
BarvyAut = (baBila,baZelena,baModra,baCervena,baZluza,baCerna);
BarvyVlasu = (bvBlond,bvHneda,bvCerna);

Některým líným programátorům se nechce vymýšlet ani názvy typů a tak je pojmenují stejně jako proměnné, jenom na začátek dají velké T.
type
TBarvaAuta=(baBila,baZelena,baModra,baCervena,baVyblita);
var
BarvaAuta : TBarvaAuta;
Ale toto označení se častěji používá pro tzv. třídy (o tom někdy příště).
Bohužel výčtový typ nejde vypsat pomocí writeln, ani zadat pomocí readln. Používáme ho hlavně kvůli přehlednosti - a pokud chceme - můžeme k výpisu použít if či case.

Typ Interval

Proměnné typu Interval leží v určitém rozmezí. Hranice náleží k některému typu, který jsme zde jmenovali (celočíselný, Boolean, výčtový, char)
Deklarace vypadá takto: DolníHranice..HorníHranice (mezi tím jsou dvě tečky!);

I tento typ lze definovat přímo ve var:
var
a : 1..7; (Dolni hranice .. dve tecky .. Horni hranice)
My však dáme přednost deklaraci pomocí type:
type TSloupec = 'a'..'h';
var Sloupec : TSloupec;

Pochopitelně nám nic nebrání použít předem definovaného výčtového typu k vytvoření intervalu:
type
TBarvaVeci = (bvBila,bvCerna,bvSeda,bvZluta,bvZelena,bvModra,bvFialova,bvCervena,bvOranzova);
TBarvaPestra = bvZluta..bvOranzova;
var BarvaPestra : TBarvaPestra;

Jak výčtový typ, tak i typ interval mají těsnou spojitost s celými čísly.

Skončili jsme s výčtem ordinálních typů.

Nyní si povíme o příkazech, které se s nimi dají provádět:
Pro všechny ordinální typy (celá čísla, Boolean, char, výčtový typ a interval) jsou definovány tyto funkce:
Ord(Proměnná):Longint - funkce vrátí pořadí prvku (Ord(2) =2, Ord('A') = 65 (podle ASCII tabulky).

Pozor - u výčtového typu má první prvek množiny přiřazenu nulu.
U typu interval má nulu přiřazena dolní hranice.
Typ proměnné se pozná podle její deklarace.

Pred(Proměnná):TypProměnné - funkce vrátí prvek předcházející (tedy Pred('z') = 'y');
Pokud prvek předchůdce nemá, nastane chyba (Pred(False) = ?)
Ohlídat, aby nám výsledek "nepřetekl", musíme sami. (Mějme funkci, která přiřadí každému písmenu abecedy jeho předchůdce - Pred('a') pak dává z hlediska abecedy nesprávný výsledek (který je ovšem správně podle ASCII). U ostatních typů se objevují jiné nesmysly
Succ(Proměnná):TypProměnné - funkce vrátí prvek následující. Rovněž zde si musíme ohlídat rozsah.
if Succ(bvSeda) = bvZluta then Writeln('Je to tak');

To by byly všechny funkce definované pro všechny ordinální typy. Dále se podíváme na specifické funkce a operace některých typů:

Všechny operace s Booleovským typem jsme již probrali. (Zkuste sami vymyslet, jak se dají pomocí probraných funkcí napsat spojky Právě tehdy, když a Jestliže, potom)

Podívejme se nyní na typy celočíselné. Nejdříve operace a operátory:

A B Operátor Operace Typ výsledku
celočíselné celočíselné + A+B (součet) celočíselný
celočíselné celočíselné - A-B (rozdíl) celočíselný
celočíselné celočíselné * A*B (součin) celočíselný
celočíselné celočíselné / A/B (podíl) reálný
celočíselné celočíselné div A div B (celočíselné dělení) celočíselný
celočíselné celočíselné mod A mod B (zbytek po dělení) celočíselný

Všimněte si, že výsledkem obyčejného dělení je typ reálný!!! Tedy například A := 4/2; je chyba, pokud je A celočíselný typ - výsledným typem je typ reálný bez ohledu na výsledek operace!!!

A nyní si proberme procedury a funkce u celočíselných typů :
Dec(Proměnná); - Tato procedura sníží hodnotu Proměnné o jedna
Dec(Proměnná, N); - Tato procedura sníží hodnotu Proměnné o N
Inc(Proměnná); - Zvýší hodnotu Proměnné o jedna
Inc(Proměnná,N); - Zvýší hodnotu Proměnné o N;
Odd(Proměnná):Boolean; Vrací, zda je Proměnná liché číslo (Odd(1) = true)

Možná si říkáte, k čemu jsou tyto procedury, když to samé dělá přiřazovací příkaz: A:=A-N; A:=A+N; Tyto procedury poskytují trochu lepší výsledný kód (šetří pamětí a zvyšují rychlost), já se jim ale vyhýbám, neboť snižují přehlednost programu.

U typu char byste měli znát tyto dvě funkce:
Chr(N:Byte) : Char; tato funkce najde N-tý znak v ASCII tabulce. Tedy Chr(67) = 'C'; (připomínám, že Byte může nabývat hodnot 0..255) zápisy Chr(-1) a Chr(256) jsou tedy nesmyslné.
Upcase(Znak) : char; Tato funkce převede malé písmeno na velké. Ostatní znaky nechává beze změny :UpCase('a') = 'A'; UpCase('A') = 'A'; UpCase('1')='1';
Znaky nejdou odčítat, násobit ani dělit. Sčítáním dvou znaků vznikne řetězec ('A'+ 'b' = 'Ab'.)

Nyní již víte o ordinálních typech úplně vše. Napište několik programů, ve kterých využijete svých nově získaných vědomostí, a až si budete jisti, že jste všechno pochopili a že jste se všechno naučili, čtěte dál.

Typy reálné

Ačkoliv se jedná o typy číselné, nejde u nich vyjádřit následující a předcházející prvek.
My z těchto typů známe zatím pouze Real; - Občas ale potřebujeme vědět výsledek přesněji. Pascal nabízí hned celou řadu reálných typů:

Typ Rozsah Přesnost Nároky na paměť
single 1.5*10-45 až 3.4*1038 7 platných číslic 4byty
real 2.9*10-39 až 1.7*1038 11 6bytů
double 5.0*10-324 až 1.7*10308 15 8bytů
extended 3.4*10-4932 až 1.1 * 104932 19 10bytů
comp -263+1 až 263-1 19 8bytů

U reálných typů se jako dolní mez neuvádí nejmenší možné číslo, ale číslo s nejmenší možnou absolutní hodnotou (nejmenší číslo, které ještě lze rozlišit od nuly). Výjimku tvoří typ comp, který může nabývat pouze celočíselných hodnot (ale jeho rozsah je natolik obrovský, že se řadí mezi typy reálné = nefunguje Succ apod.)
Pokud chcete používat i jiné reálné typy než Real, musíme v Options / Compiler zaškrtnout tato dvě políčka : 8087/80287 a Emulation. Jinak nám překladač oznámí chybu.

Podívejme se nyní na standardní funkce reálných typů:

Název funkceVýznam
Sin(x) Sinus x. Pozor, x je v radiánech
Cos(x) Cosinus x - x je v radiánech
ArcTan(x) ArcusTangens (známý též jako Tan-1 x) - výsledek v radiánech
Pi hodnota čísla Pi
Sqr(x) x2
Sqrt(x) druhá odmocnina z x
Exp(x) ex
Ln(x) ln x (přirozený logaritmus)
Frac(x) desetinná část x
Int(x) celá část x

Výsledným typem všech těchto funkcí je typ Real. To platí dokonce i v případě funkce Int!!!
Pokud jsme ovšem zapnuli Emulation a 8087/80287, mění se výsledný typ na Extended - a podle potřeby se zaokrouhluje.

Pokud budete potřebovat další matematické funkce, musíte si je nadefinovat sami:

function Tan(x:Real) : Real;
begin
 Tan:=Sin(x)/Cos(x);
end;

Stačí vyjít ze známých vzorečků:
Napište funkce pro y-tou mocninu a y-tou odmocninu z x (x,y jsou reálná čísla). (Návod: využijte rovnosti
xy=eln(xy)=ey*ln(x). Také si vyzkoušejte napsat funkci pro dekadický logaritmus a logaritmus o základu a.

Až to budete mít, čtěte dál. Podíváme se na poslední skupinu typů, kterou dnes budeme probírat.

Řetězce

Řetězec je skupina znaků. Tedy např. Retezec = 'A#@@(*& #%&asd8xck' Konec a začátek řetězce označujeme apostrofy. (Potřebujeme-li napsat apostrof, musíme ho zdvojit):
Retezec := ' Tak takto se pise apostrof : '' jasne?!';
Jediný typ řetězce, který jsme poznali byl string. Jeho délka v paměti je 256 znaků, což občas nestačí a občas je to zbytečně mnoho. Proto Pascal nabízí i jiné typy řetězců:

Typ řetězce výhody nevýhody
string může obsahovat jakékoliv znaky, snadná manipulace, rychlé zjištění délky řetězce maximální délka pouze 255 znaků, velké nároky na paměť (256 bytů)
string[N] stejné jako u stringu + v paměti zabírá jen N+1 bytů maximální délka N znaků
OpenString stejně jako string. Tento typ je povolen pouze jako parametr funkce či procedury a umožňuje nedodržovat jednotnou délku řetězce. maximálně 255 znaků, ztížená manipulace
PChar neomezená délka, zabírá v paměti jen místo, které skutečně obsazuje Nemůže obsahovat znak #0, poměrně špatně se zjišťuje jeho délka, velice špatně se s ním manipuluje. Jeho použití vyžaduje použití programové jednotky Strings

Poznámky: K typu OpenString. Máme-li definovány proměnné Jmeno : string[20] a Prijmeni : string[40], tak bychom museli mít dvě funkce na hledání prvního písmene. Jednu pro string[20] a druhou pro string[40]. Parametr typu OpenString nám umožňuje toto omezení obejít:

function NajdiPrvniPismeno(Retezec:OpenString):char;
begin
 NajdiPrvniPismeno:=Retezec[1];
end;

U řetězců lze jednotlivé znaky indexovat pomocí Retezec[Pořadí písmene]. Mějme Retezec='Ahoj lidi' Pak platí Retezec[3]='o'.

Nyní se podívejme na funkce a procedury pracující s řetězci :
Řetězce lze sčítat : 'A' + 'hoj' = 'Ahoj'
Length(Retezec):Integer; vrátí skutečnou (ne maximální) délku řetězce
Length('Bacha') = 5
Concat(Retezec1,Retezec2,...):string;
Spojí řetězce v jediný - kód je o něco lepší než při použití přiřazení a +
Concat('Hell','o',' Wor', 'ld!!') = 'Hello World!!';
Pos(Podretezec,Retezec):Byte;
Funkce najde místo výskytu Podretezce v Retezci. Vrací místo, kde začíná Podretezec. V případě neúspěchu vrací 0.
Pos('solut','Absolutne na nic') = 3;
Copy(Retezec,Od,Kolik):string; Funkce začne v řetězci od Od-tého znaku a vrátí jich Kolik.
Copy('Ahoj',2,2) = 'ho';
Delete(Retezec,Od,Kolik); Procedura vymaže Kolik znaků počínajíc od Od.
Insert(Vkladany,Puvodni,Na); Do řetězce Puvodni je na místo Na vložen Vkladany řetězec.

Tím bychom skončili řetězce. Dcv: Napište program, kde využijete všechny zde uvedené funkce a procedury.
Nyní se podíváme na zbylé funkce pracující s proměnnými.

Funkce a procedury konverzní

Tyto procedury a funkce slouží k převodu jednoho typu na jiný. Pascal nabízí celou řadu převodních funkcí a procedur. Stručně:
O funkcích Chr a Ord jsme se již zmínili.
Round(X:Real) : longint; zaokrouhlí Real (popř. Extended) na Integer(Popř. longint);
Trunc(X:Real) : longint; "usekne" desetinnou část - výsledkem je zase Longint
Str(X:Real,s:string); převede číslo X na řetězec znaků s(i zde můžeme zadávat počet číslic a počet míst za desetinnou čárkou pomocí X:Šířka:ZaDesetinnou; - jako u Write a Writeln;
Val(s:string,vysledek;kód : integer); Tento příkaz do proměnné vysledek vloží číslo, které je v proměnné s jako řetězec znaků. Proběhne-li převod bez chyby, je po provedení procedury kód = 0, jinak je v hodnotě kód číslo prvního znaku, u kterého došlo k chybě (nezapomeňte, s musí být číslo - tedy žádné mezery, žádné desetinné čárky (protože Pascal používá tečky), žádná písmena kromě E. Dle běžné konvence totiž např. 1.5E-12 je 1.5 krát 10-12)
To by bylo ke konverzním funkcím vše. Napište si pár programů, ve kterých si vyzkoušíte, co jednotlivé procedury dělají.

Ostatní funkce a procedury

Některé funkce se vymykají běžnému zařazení:
Abs(x):TypProměnné; Tato funkce vrací absolutní hodnotu proměnné. Výsledek je stejného typu jako proměnná (=Real,Extended,Integer,....);
High(Identifikátor):TypIdentifikátoru; Tato funkce vrací nejvyšší možnou hodnotu, jaké může Identifikátor nabýt. Identifikátorem může být buď název typu, nebo samotná proměnná
Pokud je Identifikátorem řetězec, vrací se jeho maximální možná délka.
Low(Identifikátor):TypIdentifikátoru; Funkce vrací nejnižší možnou hodnotu, které může identifikátor nabýt. Je-li Identifikátor řetězec, vrací Low nulu.
Raději na příkladech :
var A:string[117];
Poté tedy :
Abs(-15) = 15;
Abs(133) = 133;
High(Boolean)=True;
High(A)=117;
Low(Boolean) = False;
Low(A)=0;

Než přistoupíme k dalším funkcím, je nutné si uvědomit, jak vypadá proměnná typu Word či Integer v paměti - zabírá 2 byty. Občas ale potřebujeme znát jen jeden (horní nebo dolní). A občas potřebujeme tyto byty prohodit:
Hi(x) : byte; vrací horní byt proměnné x
Lo(x) : byte; vrací dolní byt proměnné x
Swap(x):TypProměnné; Vrací hodnotu, která vznikne přehozením bytů proměnné (výsledným typem je Integer nebo Word);

Poslední funkci oceníme při objektovém programování.
SizeOf(Identifikátor):Word; Pokud je Identifikátor proměnná, udává, kolik místa zabírá v paměti tato proměnná. Pokud je Identifikátor název typu, udává, kolik bytů zabere proměnná, která je daného typu :
var A:Extended;
SizeOf(Extended) = 12;
SizeOf(Boolean) = 1;

To by bylo pro dnešek vše.
DCV: Napište na každý typ a každou zde probranou funkci, proceduru či metodu alespoň dva krátké prográmky.