5. Parametre podprogramov

Rovnako ako algoritmy vďaka možnosti zadávať rôzne vstupné údaje, dokážu riešiť úlohu pre rôzne vstupné hodnoty, i vhodne napísané podprogramy dokážeme využiť tak, aby úlohu riešili pre rôzne vstupy. Jeden zo spôsobov, akým možno do podprogramu „poslať“ hodnoty sme už prezentovali – spracúvali sme údaje uložené v globálnej premennej.

Ak však chceme, aby podprogram predstavoval samostatný a nezávislý celok (vďaka čomu by bolo jeho použitie univerzálne) a aby sme sa nemuseli zaoberať neustálym sledovaním správaneho obsahu používaných globálnych premenných, potrebujeme použiť iný aparát.

Funkcie i procedúry umožňujú využívať tzv. formálne parametre. Formálny parameter je špeciálna lokálna premenná, ktorej hodnota sa nastaví pri volaní funkcie a v momente spustenia tela podprogramu už obsahuje hodnotu príslušného typu. Počas vykonávania kódu môžeme do nej priraďovať hodnoty a pracovať s ňou ako s obyčajnou premennou, no po ukončení podprogramu sa z pamäte uvoľní a zmeny hodnoty sa neprejavia. Všeobecný zápis vyzerá napr. nasledovne:
 

procedure Nazov1(par1,par2:typ1;par3:typ2);
{procedure Vypis(a,b:integer;c:string)}

function Nazov2(par1,par2:typ2;par3:typ2):typ3;
{function Vypocet(a,b:integer;c:string):integer}

 

pričom parametre rovnakého typu oddeľujeme čiarkou, v prípade použitia premenných viacerých typov použijeme na oddelenei skupín bodkočiarku.

Volanie podprogramu môže vyzerať:

 Nazov1(a,b,c);             {Vypis(10,20,’test’)}
 vysledok:=Nazov2(a,b,c);       {vysledok:=Vypocet(a,10,’
test’)}

 pričom ako parameter môžeme použiť konkrétnu hodnotu alebo premennú, z ktorej sa hodnota prečíta (manipulácia s ňou nemá zatiaľ žiaden vplyv na obsah premennej, z ktorej sa prečítala).

Napíšte podprogram, ktorý pre zadané n vypíše do prvého riadku jednu hviezdičku, do druhého dve atď. až po n-tý riadok (v Delphi vkladajte riadky do Listboxu).
 

procedure Hviezdy(n:integer);
var i:integer;
   
riadok:string; 

begin
 riadok:=’’;        {obsahuje pocet hviezd, ktore treba vypisat}
 for i:=1 to n do begin
  riadok:=riadok+’*’; {v kazdom riadku sa prida jedna *}
  Listbox1.Items.Add
(riadok);    {vypis}
 end;

end;

Premenná n obsahuje počet riadkov, ktoré sa majú spracovať a využíva sa v podprograme len na čítanie. Vykonanie procedúry by sme mohli zabezpečiť napr. ako Hviezdy(4);.

 Napíšte podprogram, ktorý na základe procedúry z predchádzajúcej úlohy vykreslí polovicu vianočného stromu v podobe ako na obrázku:

*
**
*

**
***
*
**
***
****

Úlohu vyriešime dvoma spôsobmi:
- v hlavnom programe zavoláme procedúru na vykreslenie trojuholníkov postupne so zväčšujúcim sa parametrom,
- napíšeme podprogram, ktorý bude vytvárať strom na základe zadania jeho výšky

...
begin
 for i:=1 to 4 do Hviezdy(i);
end;

Procedúra sa volá najprv s hodnotou 1 (t.j. vykreslí sa jedna *), potom s parametrom 2 (vykreslí sa trojuholník s prvým riadkom obsahujúcim jeden znak a druhým dva znaky atď.).

Druhá možnosť je zaujímavejšia – v procedúre Strom budeme volať procedúru Hviezda vďaka čomu získame pomerne univerzálnu procedúru:

procedure Strom(n:integer);
var i:integer;

begin
 for i:=1 to n do Hviezda(i);
end;

Rovnako ako do procedúr, môžeme i do funkcií posielať parametre. Tento prístup je pre nás v určitej podobe známy už z matematiky.

Napíšte funkciu, ktorá pre dve zadané hodnoty vráti väčšiu z nich. Ak sú rovnaké, návratová hodnota bude jedna z nich.

function Max(a,b:integer):integer;

begin
 if a>b then Max:=a {ak je a vacsie, Max vrati a}
        else Max:=b;       {inak je b vacsie a Max vrati b}
end;

Do funkcie posielame dve celé čísla a výsledok bude tiež celočíselný, vo funkcii dôjde k ich porovnaniu a na základe neho sa do názvu funkcie priradí väčšia hodnota.
Volanie môže mať podobu:

vysledok:=Max(10,20) alebo vysledok:=Max(a,b)

prípadne vysledok:=Max(Max(10,20),30)

pričom jednoznačne najzaujímavejšie je posledné volanie. V ňom sa najprv nájde hodnota vnorenej funkcie Max(10,20). Výsledok z nej sa potom použije na ďalšej úrovni a zavolá sa Max(20,30), ktorá vráti skutočný a správny výsledok.

Napíšte funkciu, ktorá pre zadané n vráti n!
Napíšte funkciu, ktorá pre zadané hodnoty a, n vráti an.

Do podprogramov sme doposiaľ posielali len konkrétne hodnoty, ktoré sa v ich tele spracúvali. Podprogram ich vykonal, prípadne ak šlo o funkciu, vrátil výsledok. V praxi sa však často stretneme s prípadmi, keď od podprogramu potrebujeme vrátiť viac ako jednu hodnotu alebo potrebujeme upraviť obsah premennej, či poľa.

Použitie globálnych parametrov predstavuje obvykle zbytočné komplikovanie a prekombinovanie kódu, pretože máme k dispozícii aparát, ktorý nám umožní ovplyvniť iným spôsobom.

Dve triedy súťažia v zbere papiera. Evidujte odovzdané množstvá jednotlivých žiakov prostredníctvom poľa. Zistite, ktorá trieda nazbierala viac papiera. Zistite, v ktorej triede bol vyšší priemerna žiaka (v prípade Delphi načítavajte údaje z Listboxu a ako jeden z parametrov pošlite do procedúry názov listboxu, pričom jeho typ bude TListbox).

A tu sa dostávame do situácie, keď pre načítanie hodnôt do oboch polí použijeme prakticky rovnaký postup (načítavanie postupnosti ukončené nulou), čo nám ponúka možnosť využiť podprogram. Okrem toho by sme v rámci podprogramu mohli už pri načítaní zistiť priemery i celkové nazbierané množstvo.

Vzhľadom na množstvo údajov, ktoré vyžadujeme, nám funkcia vracajúca ako výsledok jedinú hodnotu, určite nepostačí. Navyše by bolo vhodné, aby sme si údaje uložené do poľa i zapamätali.

Podprogramy, s ktorými sme sa doteraz stretli pracovali s parametrami volanými hodnotou. Do podprogramu vstupovali prostredníctvom lokálnych premenných hodnoty, s ktorými sa pracovalo, a ktoré sa po ukončení podprogramu z pamäte uvoľnili.

Pokiaľ chceme, aby sa zmeny v premenných uchovali i po ukončení podprogramu, potrebujeme ich upraviť na parametre volané adresou. Pamäť pre parametre volané hodnotou sa vytvára tak, aby bola po ukončení podprogramu uvoľnená. Pokiaľ je parameter volaný adresou, nemôže byť na jeho pozícii konkrétna hodnota, ale vždy len názov premennej. Pri inicializácii podprogramu sa preň nevytvorí nové pamäťové miesto, ale manipuluje sa s tým pamäťovým miestom, na ktorom je uložená príslušná premenná – t.j. mení sa ako hodnota formálneho parametra v podprograme, tak i hodnota premennej v tele programu (obe premenné ukazujú na rovnaké miesto). Po ukončení podprogramu sa pamäťové miesto neuvoľnuje, pretože pri jeho inicializácii nevzniklo.

 
Obr. Postup pri parametroch volaných hodnotou a adresou

Kľúčovým slovom, ktoré definuje parameter ako volaný adresou je var. Použitie v podprograme môže vyzerať ako v nasledujúcom príklade:

 Type TZoznam=array[1..30] of integer;    
{na to, aby sme mohli pouzit pole ako parameter podprogramu,}
{potrebujeme ho definovat ako typ}

var trieda1,trieda2:TZoznam;
    sucet1,sucet2:integer;       {sucty za triedu}
    priemer1,priemer2:real;       {priemery za triedu}

{parametre pred ktorymi sa nachadza “var” menia v podprograme hodnoty}
{premennych, ktore su pri volani umiestnene na rovnakych miestach }

procedure Operacia(var trieda:TZoznam;var suc:integer;var priem:real);
var pocet,hodnota:integer;

begin
 pocet:=0;
 suc:=0;                         {inicializacia premennych}
 repeat                                 {opakuje, kym sa nezada hodnota 0}
  Writeln(‘Zadaj ziaka c.’,pocet+1);       {preco pocet+1?}
  ReadLn(hodnota);                 {nacita sa hodnota}
  if hodnota>0 then begin          {ak je nenulova}
       inc(pocet);                {zvysi sa pocet zapojenych ziakov}
       trieda[pocet]:=hodnota;       {zapamata sa v poli}
       suc:=suc+hodnota;          {zvysi sa sucet za triedu}
  end;
 until hodnota=0;       {koniec nacitavacieho cyklu}
 priem:=suc/pocet;       {vypocita sa priemer}
end;

{********* hlavny program **********}

begin
 Operacia(trieda1,sucet1,priemer1);       {do premennej trieda1 sa ulozi}
{zoznam hodnot pre ziakov, do}
{sucet1 hodnota, ktora je}
{v procedure vedena ako sucet a}
{detto priemer1}
 Operacia(trieda2,sucet2,priemer2);       {analogia}
 if sucet1>sucet2   then WriteLn(‘Viac nazbierala 1. trieda’)
                    else WriteLn(‘Viac nazbierala 2. trieda’);
 if priemer1>priemer2   then WriteLn(‘Lepsi priemer ma 1. trieda’)
                        else WriteLn(‘Lepsi priemer ma 2. trieda’);
end;

Procedúra naplní polia pre prvú i pre druhú triedu, zároveň do premenných sucet1, priemer1sucet2, priemer2 získavame prostredníctvom formálnych parametrov vypočítané hodnoty.
 

Ošetrite program tak, aby bral do úvahy i rovnosť výsledkov.

Formálne parametre sú všetky parametre vystupujúce v tele podprogramu (volané hodnotou i adresou). Postup popísaný na formálnych parametroch sa v podprograme aplikuje na objekty určené pri volaní. Označujeme ich ako skutočné parametre. Pre jednotlivé volania podprogramu môžeme dosadzovať rôzne skutočné parametre a tým je postup, zostavený a napísaný jedenkrát, použiteľný univerzálne.

Napíšte podprogram, ktorý vymení hodnoty dvoch premenných.

Napíšte podprogram, ktorý porovná maximálne hodnoty v dvoch poliach.

Pre zadané pole zistite jeho minimum a maximum.

Napíšte podprogram, ktorý pre zadaný názov súboru a meno človeka prehľadá súbor a vypíše či a akoľkokrát sa v ňom človek nachádza (súbor môže obsahovať len zoznam mien, napr. návštevníkov budovy).

Napíšte podprogram, ktorý zaokrúhli číslo na zadaný počet desatinných miest. Číslo posielajte ako parameter volaný adresou.

Napíšte funkciu, ktorá pre zadané číslo zistí, či ide o prvočíslo. Odpoveďou bude hodnota áno/nie (true/false).