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;
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
.
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.
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á.
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í 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
).
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;