Anglická verze
logolink

< Zpět na seznam lekcí

Procedury a funkce

AlgortimyObsah lekce:

  • Co je to procedura
  • Syntaxe procedury
  • Použití procedur
  • Procedura pro součet čísel
  • Procedura pro součet čísel s použitím parametru

Co je to procedura

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:

  1. chceme-li program zpřehlednit rozložením jeho celého kódu na části (procedury),
  2. opakuje-li se v programu jeden složitější příkaz či poslopnost příkazů,
  3. nějakou část kódu si přejeme používat i v dalších programech.

Syntaxe procedury

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.

Syntaxe procedury v Pascalu
procedure název (parametry procedury); //parametry jsou volitelné
var proměnné //volitelná deklarace proměnných pro proceduru
begin
  programový kód procedury
end;

Použití procedur

Uvažujme program z předchozí lekce o dvojrozměrném poli, který uměl načíst zvolený počet čísel a vypsat je.

Naplnění a vypsání pole
 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ů:

  1. zadání počtu vkládaných čísel,
  2. vkládání zadaných čísel do pole,
  3. výpis zadaných čísel z pole.

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:

  1. procedura zadanipoctucisel - zadání počtu vkládaných čísel,
  2. procedura vlozenicisel - vkládání zadaných čísel do pole,
  3. procedura vypiscisel - výpis zadaných čísel z pole.
Procedura zadanipoctucisel
 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;
Procedura vlozenicisel
 procedure vlozenicisel;
 begin
  for i:=1 to pocet do
  begin
    readln(p[i]);
  end;
 end;
Procedura vypiscisel
 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.

Kompletní program složený z procedur
 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)).

Procedura pro součet čísel

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 pro součet čísel
 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.

Cvičení

Pokuste se tento program popsat slovně v krocích a následně jej rozložte do procedur jako v předchozím případě.

Řešení cvičení

Tento program lze funkčně rozdělit na následující posloupnost kroků (varianta A):

  1. zadání čísel a a b,
  2. výpočet součtu čísel a a b.

případně varianta B:

  1. zadání čísla a,
  2. zadání čísla b,
  3. výpočet součtu čísel a a b.

Ukažme si další postup na obou variantách, které porovnáme.

Varianta A

Procedura zadanicisel
 procedure zadanicisel;
 begin
 writeln('Zadejte prosim cislo a:');
 readln(a);
 writeln('Zadejte prosim cislo b:');
 readln(b);
 end;
Procedura vypocetsouctucisel
 procedure vypocetsouctucisel;
 begin
 writeln('Součet čísel ',a,' a ',b,' je ',a+b,'.');
 end;

Varianta B

Procedura zadanicislaa
 procedure zadanicislaa;
 begin
 writeln('Zadejte prosim cislo a:');
 readln(a);
 end;
Procedura zadanicislab
 procedure zadanicislab;
 begin
 writeln('Zadejte prosim cislo b:');
 readln(b);
 end;
Procedura vypocetsouctucisel
 procedure vypocetsouctucisel;
 begin
 writeln('Součet čísel ',a,' a ',b,' je ',a+b,'.');
 end;

Která z variant se vám zdá vhodnější? Proč?

Procedura pro součet čísel s použitím parametru

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.

Cvičení

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).

Řešení cvičení

Výsledný program může vypadat třeba nějak takto:

Varianta A

Program pro zadání a součet dvou dvojic čísel
 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.

Varianta B

Program pro zadání a součet dvou dvojic čísel
 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 pro zadání a součet dvou dvojic čísel
 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 pro zadání a součet dvou dvojic čísel
 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.

Domácí úkol

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).

Domácí úkol

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í.

Další čtení

Odkazy
webdesign, xhtml, css, php - Mgr. Michal Mikláš