Faktoriál
Princip volání funkce,
podprogramu
Funkce slouží k dělení programu na logické části, čímž zlepšuje
čitelnost programu. Umožňuje použití stejného kódu v různých místech
(za pomoci volání, odskoku do podprogramu). Realizace funkce (tj.
instrukce, které ji reprezentují), je v paměti pouze jednou.
Funkce se můžou volat navzájem – rozlišujeme potom funkci
volající a funkci volanou. Volající funkce „zavolá“
funkci volanou pomocí instrukce skoku do podprogramu „call“
(na rozdíl od instrukce „prostého“ skoku umístí „call“
na zásobník následující adresu, protože po ukončení volané funkce se
musí pokračovat z místa programu, ze kterého se „odskočilo“).
Volaná funkce potom provede svou činnost a je ukončena returnem
„return“ zajistí skok na adresu v programu, kterou na
zásobník uložil „call“.
Volající a volaná funkce (v jazyce C/C++) sdílí data přes
parametry/argumenty a návratovou hodnotu. Parametry jsou uvedeny svým
typem a názvem v hlavičce funkce a jsou to lokální proměnné funkce
(jejich život končí s instrukcí návratu z funkce). Návratová hodnota
je uvedena pouze svým typem a nemá jméno - „zůstane“ po
vykonání funkce jako její výsledek a dá se využít ve výrazech jako
proměnná daného typu (typ dán návratovou hodnotou). Poté co je
použita v daném výrazu (ve kterém je volána funkce) návratová hodnota
„mizí“ a již není k dispozici.
Paměťové místo pro parametry funkce i pro návratovou hodnotu je
vyhrazeno na zásobníku. Volající i volaná funkce si však musí
rozumnět v typu a počtu parametrů. Proto je nutné aby před voláním
funkce znal překladač její prototyp (jméno a typy argumentů a
návratové hodnoty. Na základě této informace překladač nejen vyhradí
správné místo na zásobníku, ale pokud je to nutné, tak provede i
konverzi parametru z typu proměnné použité ve volání na typ očekávaný
(je-li ve volání funkce, která očekává typ int proměnná typu double,
je vyhrazeno místo na int a proměnná typu double je převedena na typ
int a uložena do připraveného místa). Pro informování překladače o
prototypech funkce slouží uvedení prototypů do hlavičkového souboru,
který tímto oznamuje překladači jak má realizovat volání (konverze na
typ očekávaný volanou funkcí).
S voláním funkce je spojená režie, spočívající ve „spotřebě“
paměti a času. Paměť je nutná k vytvoření a uložení předávaných
proměnných, někdy je ještě před voláním funkce nutné „uklidit“
registry procesoru (uložit je na zásobník), protože volaná funkce je
může použít pro své výpočty a tím přemazat v nich uložená data. Po
ukončení funkce jsou tato data vrácena zpět do příslušných registrů.
Dále je nutné uložit návratovou adresu. Tato manipulace s pamětí a
časová režie pro „skoky“ na jiné adresy trvá nějaký čas –
časová režie volání funkce.
Rekurzivní funkce
Jsou funkce, které volají samy sebe (nejčastěji přímo, ale může k
tomu dojít i zprostředkovaně). Používá se při řešení složitějších
úloh (například neuronové sítě, expertní systémy), při hledání
řešení, kdy se upraví parametry a zavolá se tatáž funkce. Výhodou
také je, že se dá zpracovat část úlohy, zavolat tatáž funkce a
následně úlohu dokončit. Jednoduchým příkladem tohoto mechanizmu je
například hledání poloviční vzdálenosti k překážce s popisem :
Půlka:
udělej dva kroky,
nejsi-li u překážky vykonej „Půlka“,
udělej krok zpět.
Nevýhodou rekurzivního volání funkcí je velká paměťová náročnost –
při každém volání se vytváří nové lokální parametry, volané a
návratové hodnoty. Jelikož se jedná o nestandardně velké požadavky na
zásobník, může dojít k jeho přetečení a chybám programu. Překladače
proto mají volbu na nastavení velikosti zásobníku.
Úkol:
V souboru mainfaktorial.c
1) Napište kód realizující faktoriál ve funkci main. Vytiskněte ve
funkci main faktoriál od 0 do MAX (vhodně zvolte).
2) Napište
funkci (se jménem faktnerek) počítající faktoriál (nerekurzivní).
Funkce vrací faktoriál čísla zadaného jako parametr. Vytiskněte
pomocí této funkce ve funkci main faktoriál od 0 do MAX (vhodně
zvolte).
3) Napište funkci (se jménem faktrek), počítající
faktoriál, realizovanou rekurzivně. Funkce vrací faktoriál čísla
zadaného jako parametr. Vytiskněte ve funkci main faktoriál od 0 do
MAX (vhodně zvolte).
4) Modifikujte funkci z bodu 3 (jméno
faktrektisk) tak, aby při jediném volání vytiskla tato funkce tabulku
faktoriálů pro čísla od 0 (nuly) do zvoleného čísla.
Jaký je vhodný datový typ pro realizaci proměnných pro výpočet
faktoriálu?
Jaké je maximální číslo (pro zvolený datový typ) jehož faktoriál
se dá vypočítat? Navrhněte jak toto číslo zjistit.
Představte si, jaký je rozdíl v použití paměťi (volání funkce,
uložení proměnných …) při realizace funkce podle bodu 2 a 3.
Poznámky:
Faktoriál je označován „!“ (faktoriál pěti je:
5!=5*4*3*2*1*1= 120 ,10! = 3628800,
1000! = 4,02387260077093773543702433923e+2567). Definice
funkce faktoriál je:
i ! je pro kladná i roven součinu (i * (i-1)
* (i-2) * … 4 * 3 * 2 * 1).
Pro i=0 je i! = 1.
Zároveň je možné faktoriál definovat i jako rekurentní vztah (v
definici se objeví tatáž funkce)
i ! je pro kladná i roven (i *
(i-1)! )
pro i = 0 je i! = 1.
Rozšíření úkolu (na cvičeních pro
rychlejší studenty)
Na základě příkladu (example) na adrese
http://msdn.microsoft.com/en-us/vstudio/4e2ess30(v=vs.105).aspx
(msdn time.h clock) zkuste zjistit kolik času jednotlivé funkce
zaberou pro výpočet čísla MAX. (V případě, že je časový interval
malý, je vhodné zavolat funkci několikrát a vypočítat průměrný čas).
Upravený příklad
z uvedené adresy
http://msdn.microsoft.com/en-us/vstudio/4e2ess30(v=vs.105).aspx
// crt_clock.c
// This example displays the elapsed time for given program period.
//
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main( void )
{
long i = 6000000L;
clock_t start, finish;
double duration;
// Measure the duration of an event.
printf( "Time to do %ld empty loops is ", i );
start = clock();
while( i-- )
;
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf( "%2.1f seconds\n", duration );
}
Poslední změna 2014-02-07