Obsah lekce:
Procedura je samostatná část programu řešící určitý úkol (viz termín procedurální programování). Procedury se použíají zejména v těchto případech:
Zápis procedury se skládá z hlavičky (klíčové slovo procedure, název procedury a případně v závorce skupina parametrů procedury). Dále je možné deklarovat pro proceduru proměnné a mezi klíčová slova begin a end psát programový kód procedury.
procedure název (parametry procedury); //parametry jsou volitelné
var proměnné //volitelná deklarace proměnných pro proceduru
begin
programový kód procedury
end;
Uvažujme program z předchozí lekce o dvojrozměrném poli, který uměl načíst zvolený počet čísel a vypsat je.
Program NaplneniAVypisPole;
const n = 5000;
type pole = array[1..5000]of integer;
var p:pole;
i, pocet:integer;
begin
writeln('Zadej pocet vkladanych cisel (1-',n,')');
readln(pocet);
while ((pocet<1) or (pocet>n)) do
begin
writeln('Zadej znovu:');
readln(pocet);
end;
for i:=1 to pocet do
begin
readln(p[i]);
end;
for i:=1 to pocet do
begin
writeln(p[i]);
end;
readln;
end.
Tento program lze funkčně rozdělit na následující posloupnost kroků:
Program je sice poměrně krátký, ale když jej uvidíme poprvé, pak nám bude chvíli trvat než pochopíme, k čemu program slouží a odhalíme princip a základní kroky v jeho funkcionalitě. Toto je z praktického hlediska problém zejména v případě, že vzniká větší program v týmu programátorů. Ti by měli psát program tak, aby mu pokud možno rozumněl v krátké době kdokoli ze zúčastněných. Pojďme tedy program rozložit na jednotlivé kroky.
Rozložení na kroky bude znamenat přepis (přeskládání) jednotlivých částí programu do procedur:
procedure zadanipoctucisel;
begin
writeln('Zadej pocet vkladanych cisel (1-',n,')');
readln(pocet);
while ((pocet<1) or (pocet>n)) do
begin
writeln('Zadej znovu:');
readln(pocet);
end;
end;
procedure vlozenicisel;
begin
for i:=1 to pocet do
begin
readln(p[i]);
end;
end;
procedure vypiscisel;
begin
for i:=1 to pocet do
begin
writeln(p[i]);
end;
readln;
end;
Tímto rozkladem na dílčí kroky jsme získali tři jednoduché procedury (programy), ze kterých je teď možno složit celý přehledný program. Procedury přesuneme nad klíčové slovo begin hlavního programu a v hlavním programu místo daného kódu v proceduře jen napíšeme na požadovaném místě její jméno, což způsobí vykonání všech instrukcí obsažených v proceduře stejně jako kdyby byly napsány v místě volání procedury v hlavním programu.
Program NaplneniAVypisPole;
const n = 5000;
type pole = array[1..5000]of integer;
var p:pole;
i, pocet:integer;
procedure zadanipoctucisel;
begin
writeln('Zadej pocet vkladanych cisel (1-',n,')');
readln(pocet);
while ((pocet<1) or (pocet>n)) do
begin
writeln('Zadej znovu:');
readln(pocet);
end;
end;
procedure vlozenicisel;
begin
for i:=1 to pocet do
begin
readln(p[i]);
end;
end;
procedure vypiscisel;
begin
for i:=1 to pocet do
begin
writeln(p[i]);
end;
readln;
end;
begin
zadanipoctucisel;
vlozenicisel;
vypiscisel;
end.
Vidíme, že program obsahuje standardní hlavičku s definovanými proměnnými, pak deklarace jednotlivých procedur a nakonec hlavní program, který podle potřeby procedury spouští. Hlavní program samozřejmě může obsahovat libovolné další příkazy (nejen volání procedur).
Výsledkem našeho úsilí je program, v jehož těle jsou pouze tři pokyny: zadanipoctucisel, vlozenicisel, vypiscisel. Je tak velmi rychle jasné, co se v celém programu děje a v případě modifikace některého z těchto kroků již dojde k modifikaci pouze patřičné procedury. Na tomto příkladu jsme si ukázali první ze tří situací, kdy se nejčastěji vyplatí použít procedury: chceme-li program zpřehlednit rozložením jeho celého kódu na části (procedury)).
Pojďme si dále ilustrovat druhou možnost využití procedur (opakuje-li se v programu jeden složitější příkaz či poslopnost příkazů). Uvažujme jednoduchý program pro součet dvou uživatelem zadaných čísel (místo jednoduchého součtu by procedura mohla realizovat daleko složitější úlohu jako například výpočet nějakého složitého vzorce).
Program Soucet_cisel;
var a,b:integer;
begin
writeln('Zadejte prosim cislo a:');
readln(a);
writeln('Zadejte prosim cislo b:');
readln(b);
writeln('Součet čísel ',a,' a ',b,' je ',a+b,'.');
readln;
end.
Pokuste se tento program popsat slovně v krocích a následně jej rozložte do procedur jako v předchozím případě.
Tento program lze funkčně rozdělit na následující posloupnost kroků (varianta A):
případně varianta B:
Ukažme si další postup na obou variantách, které porovnáme.
procedure zadanicisel;
begin
writeln('Zadejte prosim cislo a:');
readln(a);
writeln('Zadejte prosim cislo b:');
readln(b);
end;
procedure vypocetsouctucisel;
begin
writeln('Součet čísel ',a,' a ',b,' je ',a+b,'.');
end;
procedure zadanicislaa;
begin
writeln('Zadejte prosim cislo a:');
readln(a);
end;
procedure zadanicislab;
begin
writeln('Zadejte prosim cislo b:');
readln(b);
end;
procedure vypocetsouctucisel;
begin
writeln('Součet čísel ',a,' a ',b,' je ',a+b,'.');
end;
Která z variant se vám zdá vhodnější? Proč?
Ať už jsme se rozhodli pro řešení A nebo B, tak se nám v programu vyskytuje kód, který se opakuje. To není vhodné, neboť v případě potřeby upravit v programu proces zadávání čísel je nutno vše opravovat 2x (případně i vícekrát - záleží na tom, kolikrát jsme kód pro zadávání opakovali). Snadno se tak stane, že program upravíme jen v některých částech a tak se program stává přinejmenším nejednotným a proces úpravy zdlouhavým (musíme jej celý procházet a hledat všechna místa nebo obdobné procedury, kde vyžadujeme úpravu). Pokud bychom například chtěli místo "Zadejte prosim cislo b:", aby program napsal jen "Zadejte pozadovane cislo:", pak musíme tuto skutečnost opravit na dvou řádcích procedury ve variantě A nebo v obou procedurách varianty B. V každém případě tak stejnou věc řešíme na dvou (případně více) místech.
Pokuste se upravit předchozí program (libovolnou ze dvou variant) tak, že program načte dvě různé dvojice čísel (řekněme dvojici a,b a dvojici c,d). Následně program spočítá součty obou dvojic (spočte tedy číslo a+b a číslo c+d).
Výsledný program může vypadat třeba nějak takto:
Program Soucet_cisel;
var a,b,c,d:integer;
procedure zadanicisel;
begin
writeln('Zadejte prosim cislo a:');
readln(a);
writeln('Zadejte prosim cislo b:');
readln(b);
writeln('Zadejte prosim cislo c:');
readln(c);
writeln('Zadejte prosim cislo d:');
readln(d);
end;
procedure vypocetsouctucisel;
begin
writeln('Součet čísel ',a,' a ',b,' je ',a+b,'.');
writeln('Součet čísel ',c,' a ',d,' je ',c+d,'.');
end;
begin
zadanicisel;
vypocetsouctucisel;
end.
Program Soucet_cisel;
var a,b,c,d:integer;
procedure zadaniciselab;
begin
writeln('Zadejte prosim cislo a:');
readln(a);
writeln('Zadejte prosim cislo b:');
readln(b);
end;
procedure zadaniciselcd;
begin
writeln('Zadejte prosim cislo c:');
readln(c);
writeln('Zadejte prosim cislo d:');
readln(d);
end;
procedure vypocetsouctucisel;
begin
writeln('Součet čísel ',a,' a ',b,' je ',a+b,'.');
writeln('Součet čísel ',c,' a ',d,' je ',c+d,'.');
end;
begin
zadaniciselab;
zadaniciselcd;
vypocetsouctucisel;
end.
Podívejme se na uvedené varianty řešení:
Ve variantě A dochází k tomu, že v proceduře zadanicisel se nám 4x opakuje identický pokyn pro zadání čísla (ignorujeme-li označení čísel a,b,c,d což je obecně přípustné) a zároveň se v proceduře vypocetsouctucisel opakuje dvakrát stejný výpočet (pokaždé pouze s jinou dvojicí čísel).
Ve variantě B dochází k obdobným problémům jako v předchozím, jen je první problém rozdělen do dvou v podstatě identických procedur zadaniciselab a zadaniciselcd.
Program se nám po přepsání do procedur sice zpřehlednil, ale nahromadily se nám duplicitní posloupnosti příkazů. A představme si program, který by například uměl sečíst naprosto libovolnou dvojici ze zadaných čísel (ab, cd, ca, bd... - celkem 8 možných dvojic). Případně bychom měli například 10 proměnných cislo1,..,cislo10 případně bychom chtěli proceduru, která sečte (nebo provede libovolný jiný výpočet) s libovolnými n čísli v poli (které má například 100 prvků). Program by se naustále prodlužoval o všechny možnosti kombinací a postupně by se stal velmi dlouhým, nepřehledným a postupně v podstatě nerealizovatelným (viz požadavek na součet libovolné dvojice čísel v poli o např. 15000 prvcích). Řešení těchto problémů představují tzv. parametry.
Parametr je proměnná, kterou posíláme do procedury a ta ji použije pro výpočet. Ukažme si příklad:
Program Soucet_cisel;
var a,b,c,d:integer;
procedure zadanicisla(var cislo:integer);
begin
writeln('Zadejte prosim cislo:');
readln(cislo);
end;
procedure vypocetsouctudvoucisel(var cislo1,cislo2:integer);
begin
writeln('Součet čísel je ',(cislo1+cislo2),'.');
end;
begin
zadanicisla(a);
zadanicisla(b);
vypocetsouctucisel(a,b);
end.
V hlavičce procedury zadanicisla přibila závorka s parametrem cislo (deklarovanou proměnnou cislo). To proceduře říká, že má na svém vstupu očekávat proměnnou cislo typu integer. Pokud chceme takovou proceduru použít, tak v programu musíme napsat její jméno a do závorky jako parametr jméno proměnné, kterou proceduře posíláme. Takže například zápisem zadanicisla(a); říkáme, že se má spustit procedura zadanicisla s tím, že jako proměnná cislo bude použita proměnná a. Takže akce, které se provedou v proceduře s proměnnou cislo se ve skutečnosti provedou s proměnnou a. Dojde vlastně k dosazení proměnné a do proměnné cislo v proceduře zadanicisla. Získáváme tak malý program (proceduru), která umí načíst číselnou hodnotu typu integer do libovolné proměnné, kterou jí pošleme jako parametr (jméno proměnné napíšeme v hlavním programu za její jméno do závorky).
Nyní již snadno nahlédneme, že zápisem zadanicisla(c) dojde v následujícím programu k načtení zadané hodnoty do proměnné c. Stejně tak zápisem vypocetsouctucisel(a,c) dojde k výpisu součtu proměnných a a c. Díky procedurám s parametry mohu danou proceduru provést s libovolnými proměnnými daného typu.
Program Soucet_cisel;
var a,b,c,d:integer;
procedure zadanicisla(var cislo:integer);
begin
writeln('Zadejte prosim cislo:');
readln(cislo);
end;
procedure vypocetsouctudvoucisel(var cislo1,cislo2:integer);
begin
writeln('Součet čísel je ',(cislo1+cislo2),'.');
end;
begin
zadanicisla(a);
zadanicisla(b);
vypocetsouctucisel(a,b);
zadanicisla(c);
vypocetsouctucisel(a,c);
vypocetsouctucisel(c,b);
end.
Vedle procedur se dnes téměř ve všech programovacích jazycích používají funkce, které mají výhodu, že mohou do hlavní části program vrátit hodnotu.
Pokuste se přepsat program hry piškvorky z minulé lekce do procedur (účelem je rozdělení programu na jednotlivé části - neberte zde v úvahu parametry).
Napište program, který načte od uživatele 5 čísel do pole p1 a do pole p2 . Následně obě pole vypíše a pole p1 setřídí a vypíše znovu po setřízení.