Procedury a jejich spolupráce

Zatím jsme vytvářeli programy, ve kterých jsme si vystačili s jednou procedurou. U složitějších programů je však lepší, když je rozdělíme na více procedur, které spolu pak spolupracují. Na této stránce se dozvíš, jak na to.

Vpomínáš si na první stránku a příklad s kreslením domečku? Tak přesně ten si teď ukážeme, byť trochu zjednodušený. Budeme potřebovat dvě procedury. První proceduru pojmenuj trojuhelnik -- procedura vykreslí rovnostranný trojúhelník, jehož jedna strana bude vodorovná (proto se na začátku otáčíme o 30 stpňů, počáteční poloha želvičky je totiž směrem vzhůru). Kód procedury bude vypadat takto.

otoc o 30;
opakuj 3 krat:
    vpred o 100;
    otoc o 120;
konec;
otoc o 30 doleva;

Dále se nám bude hodit procedura ctverec, která, jak název napovídá, nakreslí čtverec. Zde je uveden její kód.

opakuj 4 krat:
    vpred o 100;
    otoc o 90;
konec;

Zbývá ještě vytvořit proceduru domecek, která nakreslí domeček. Ta bude využívat dvě předešlé procedury. Jestliže do zdrojového kódu procedury domecek napiseme příkaz trojuhelnik;, spustí se procedura trojuhelnik, ta se vykoná a po jejím vykonání se začnou zpracovávat další příkazy procedury domecek. Obdobně je to i s procedurou ctverec. Teď už nám tedy nic nebrání, abychom proceduru domecek vyrobili. Procedura nejprve nakreslí čtverec zavoláním (spuštění procedury z jiné procedury se říká volání) procedury ctverec. Po jejím skončení se bude želvička nacházet v levém dolním rohu právě nakresleného čtverce. Příkazem vpred o 100; ji posuneme do horního levého rohu a odtud zavoláme proceduru trojuhelnik, která dokreslí střechu. Zde je celý zdrojový kód procedury domecek.

ctverec; {způsobí, že se provede procedura ctvrec}
vpred o 100;
trojuhelnik;

Parametry

Teď si představ, že bychom chtěli nakreslit domečky dva. Jeden o velikosti stěny 100 a druhý o velikosti stěny 50. Se znalostmi, které zatím máme, bychom museli napsat dvě procedury kreslící trojúhelník, jednu, která nakreslí trojúhelník o straně délky 100 a druhou, která nakreslí trojúhelník o straně délky 50. Stejně tak bychom museli napsat dvě procedury pro vykreslení čtverce. To by se pak používání procedur prakticky nevyplatilo.

Naštěstí lze chování procedury ovlivnit, upřesnit, co má dělat. K tomu slouží parametry. Vytvoř si novou proceduru a dej jí název bere. Do textového pole nadepsaného Napiš, jaké parametry je třeba tvé proceduře předat, napiš následující:

cele $n

Kód procedury bude takovýto:

hodnota $n;

Vytvoř ještě jednu proceduru, pojmenuj ji třeba dava, u ní textové pole s parametry nevyplňuj, její kód bude:

bere 150;

Pak procuderu dava pusť. Objeví se okno s číslem 150. Procedura dava zavolala proceduru bere, navíc jí předala parametr 150. To, jaký parametr procedura bere, jsme napsali do textového pole nad kódem procedury. Tam jsme řekli, že parametr má být celé číslo a navíc jsme Želví grafice sdělili, že hodnota parametru se má uložit do proměnné $n. Tuto proměnnou jsme vytvářet nemuseli, ta se vytvořila automaticky při zavolání procedury bere.

Lokální proměnné

Je na místě uvést ještě jednu poznámku. Proměnná, která se při zavolání funkce vytvoří a do níž se uloží parametr, je takzvaná lokální proměnná. To znamená, že tuto proměnnou smí používat jenom volaná procedura a po skončení procedury tato proměnná zanikne. Pokud by ses tedy pokusil(a) přidat za konec procedury dava třeba příkaz hodnota $n;, obdržíš chybové hlášení. To proto, že procedura dava nemá ani tušení o tom, že existuje nějaká proměnná $n. Stejně tak, když ve volané proceduře vytvoříš novou proměnnou, tato proměnná taktéž zanikne, jakmile procedura skončí. Avšak pokud vytvoříš nějakou proměnnou v kódu procedury dava a pak zavoláš proceduru bere, v kódu procedury bere můžeš tuto proměnnou použít a dokonce i měnit.

Tedy -- procedura má přístup k proměnným, které byly vytvořeny v proceduře, která je zavolala, ale volající procedury nemají přístup k proměnným, které byly vytvořeny v procedurách, které zavolaly.

Překrývání proměnných

Vytvoř si dvě procedury, první z nich nechť se jmenuje volana a má tento kód:

hodnota $a;
at cele $a = 10;
hodnota $a;

Druhé proceduře dej jméno volajici a její zdrojový kód ať vypadá takto:

at desetinne $a = 100;
volana; hodnota $a;

Když spustíš proceduru volajici, zobrazí se ti tři okénka, první s číslem 100, druhé s číslem 10, třetí opět s číslem 100. Do proměnné $a jsi totiž ve volající proceduře uložil(a) číslo 100, proto se ti po zavolání procedury volana objeví stovka (díky příkazu hodnota). Pak přijde klíčový okamžik. Vytvoří se nová proměnná a to se stejným názvem, jaký má již existující proměnná. To je možné, protože novou proměnnou vytváříme v jiné proceduře (kdybychom dvakrát za sebou vytvořili proměnnou stejného názvu ve stejné proceduře, byla by to chyba, jak již bylo řečeno). Tato nová proměnná takzvaně překryje tu starou. Takže kdykoliv použijeme v proceduře volana proměnnou $a, Želví grafika má za to, že tím míníme tu novou. Proto do ní můžeme uložit jinou hodnotu (10) a tu také zobrazit. Nově vytvořená proměnná je však lokální, takže zanikne po skončení procedury volana. Proto, když procedura volajici zobrazí okénko s hodnotou proměnné $a, je v ní uložena stará hodnota (100).

Procedura je tedy opět jakási černá skříňka, která, pokud nechce, nemusí se vůbec starat o to, kdo ji zavolal. Procedura má svá vlastní data, na která jí nikdo nesmí sáhnout. Pokud však procedura chce, může manipulovat i s daty, která patří procedurám, které ji zavolaly, musí však vědět, jak se jmenují proměnné, v nichž jsou data uložena. Poslední vlastnosti využívej s rozvahou. Snadno se ti může stát, že se program bude chovat úplně jinak, než jsi zamýšlel(a) a chyba se pak o poznání hůř hledá.

Více parametrů

Zatím jsme volané funkci předávali pouze jediný parametr. Nic nám však nebrání předat jí parametrů více. V tom případě stačí do textového pole v editačním okénku procedury napsat více proměnných a oddělit je čárkou, chceme-li parametry předat volané proceduře, oddělíme je posloupností několika netisknutelných znaků (to znamená mezer nebo konců řádků). V následujícím příkladě se volané proceduře předají dva parametry, procedura je sečte a součet vypíše. Jako první je uvedena volaná procedura. Její seznam parametrů vypadá takto:

desetinne $a, desetinne $b

Její kód má takovýto tvar:

hodnota $a + $b;

Volající procedura je velmi jednoduchá, například:

volana 15 3;

Rekurzivní volání

Rekurzivní volání procedury je takové volání, kdy procedura volá sama sebe. To na první pohled vypadá divně, jelikož by se mohlo zdát, že pak program nikdy neskončí, pořád dokola se bude volat jedna a ta samá procedura. Pokud však proceduře předáme jeden parametr navíc, který bude značit hloubku rekurze, můžeme při každém zavolání procedury tento parametr snížit o 1 a jakmile parametr dosáhne nuly, procedura se dál neprovádí.

Následující příklad tuto myšlenku ilustruje. Program nakreslí čtverec, pak menší čtverec, pak ještě menší čtverec, a tak pokračuje, než celkový počet čtverců přesáhne jisté číslo. Procedura ctverce bere dva parametry, $d značí délku strany největšího čtverce, $n značí, kolik čtverců se má nakreslit. Nejprve se nakreslí jeden velký čtverec o straně délky $d. Jestliže je parametr $n větší než jedna, znamená to, že se má v kreslení čtverců pokračovat, takže procedura zavolá sama sebe, první parametr (délku) zadá například $d/2 (takže každý čtverec bude mít poloviční délku strany než ten, který byl nakreslen před ním) a druhý parametr (hloubku rekurze) zadá $n - 1, protože tolik čtverců ještě zbývá vykreslit. Tady je seznam parametrů:

desetinne $d, cele $n

A tady je kód procedury.

opakuj 4 krat:
    vpred o $d;
    otoc o 90;
konec;
pokud $n > 1:
    ctverce $d/2 $n-1;
konec;

Úloha: přepiš výše uvedenou proceduru tak, aby kreslila stejnou věc, ale bez použití rekurze (použij třeba cyklus pocitej).

Trochu komplikovnější příklad

Následující příklad nakreslí strom. Co je to strom? Na strom můžeme nahlížet takto: strom se skládá z nějaké hlavní větve, ze které vyrůstají třeba dva menší stromy. Jak je strom košatý, to nám bude udávat takzvaný stupeň stromu. Strom o stupni nula bude pouhá jedna větvička, strom stupně $n bude hlavní větev a z ní vyrůstající dva stromu stupně $n-1. Úloha jak stvořená pro použití rekurze. Tady je kód.

cele $n

pokud $n = 0:
    vpred o 20; {vetev}
    vpred o -20; {musime se vratit na stejne misto}
jinak:
    vpred o 20; {kmen}
    otoc o 15 doleva; {podstrom vyrusta z boku}
    strom $n-1; {nakreslime podstrom}
    otoc o 30; {natocime se na druhou stranu stromu}
    strom $n-1; {a vykreslime podstrom}
    otoc o 15 doleva; {otocime se zpet}
    vpred o -20; {a posuneme se zpet}
konec;