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čí).
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.
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í -
Uvědomte si:
Tabulku ASCII si vyhledejte a prostudujte sami.
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.
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.
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.
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 funkce | Vý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ě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.
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í.
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.