Maturitní otázka č. 3
3. Programování – Podprogramy a jejich využití
1. Funkce a procedury (Výhody a nevýhody)
Podprogramy jsou pojmenované, logicky ucelené bloky kódu, které řeší určitou dílčí úlohu v rámci většího programu. Místo psaní jednoho obrovského bloku kódu programátor rozdělí problém na menší části (metoda "rozděl a panuj").
Rozdíl mezi funkcí a procedurou:
- Funkce: Po svém spuštění a vykonání úkolu vrací výsledek zpět na místo, odkud byla zavolána (pomocí příkazu "return"). Například matematická funkce, která vypočítá obsah kruhu a vrátí konkrétní číslo.
- Procedura: Vykoná posloupnost příkazů, ale nevrací žádnou hodnotu. Typicky se používá pro akce, jako je výpis textu na obrazovku, uložení dat do souboru nebo smazání prvků. V jazycích typu C/C++/Java se pro procedury používá návratový typ "void".
Výhody použití podprogramů:
- Znovupoužitelnost (DRY - Don't Repeat Yourself): Kód stačí napsat jen jednou a lze jej volat z různých míst programu. Tím se drasticky zkracuje délka kódu.
- Modularita a přehlednost: Program se stává čitelnějším. Hlavní tělo programu (main) pouze logicky volá názvy funkcí, takže okamžitě vidíme, co program dělá.
- Snazší ladění (Debugging) a údržba: Pokud se objeví chyba např. ve výpočtu DPH, stačí ji opravit v jedné jediné funkci a oprava se automaticky projeví všude.
Nevýhody použití podprogramů:
- Režijní zátěž (Overhead): Každé zavolání podprogramu stojí procesorový čas a paměť. Musí se uložit návratová adresa, předat parametry přes tzv. zásobník (Call Stack) a po skončení zase obnovit původní stav.
- Ztráta kontextu: U extrémně rozkouskovaných programů na stovky malých funkcí může být někdy složité vysledovat, odkud a kam data přesně tečou.
2. Rekurze a příklady smysluplného použití
Rekurze je programovací technika, při které podprogram (funkce nebo procedura) v rámci svého těla volá sám sebe. Aby rekurze fungovala správně a neskončila nekonečným zacyklením (což by vedlo k chybě "Stack Overflow" - přetečení zásobníku), musí obsahovat:
- Ukončovací podmínku (bázový případ): Jednoduchý stav, pro který známe okamžitou odpověď a při kterém se rekurzivní volání zastaví.
- Rekurzivní krok: Krok, který postupně zmenšuje původní složitý problém tak, aby se blížil k ukončovací podmínce.
Smysluplné použití:
- Výpočet faktoriálu (N!) nebo Fibonacciho posloupnosti. Procházení stromových struktur (např. rekurzivní procházení všech složek a podsložek na pevném disku počítače). Třídicí algoritmy pracující na principu "Rozděl a panuj" (např. Quicksort nebo Mergesort).
3. Hodnota proměnné "b" na konci programu
int b=0;
int Zvedni(int j) {
int b=2;
return b*j;
}
int main() {
b=Zvedni(++b);
}
Správná odpověď: Na konci programu bude v proměnné b hodnota 2.
Detailní rozbor krokování pro komisi:
- Máme definovanou globální proměnnou b=0. V těle funkce main() se má do b přiřadit výsledek volání funkce Zvedni(). Jako parametr se předává výraz ++b. Výraz ++b je tzv. pre-inkrement. Znamená to, že se nejprve globální proměnná b zvýší na 1 a tato hodnota 1 se předá jako parametr j do funkce. Uvnitř funkce Zvedni se definuje lokální proměnná b=2. Tato lokální proměnná tzv. "zastíní" (shadowing) tu globální, takže v rámci funkce Zvedni znamená b vždy dvojku. Funkce vrací b * j (tedy 2 * 1), což je 2. Tato návratová hodnota 2 se vrátí do funkce main() a přiřadí se do původní globální proměnné b. Její konečná hodnota je tak 2.
4. Praktická úloha: Doplnění funkcí pro pole
Jako příklad uvedu řešení v jazyce Python. Program nejdříve pomocí uživatelsky definované funkce načte čísla do pole z klávesnice a následně pomocí druhé procedury (v Pythonu definované přes klíčové slovo def, ačkoliv nemá return) celé pole naformátovaně vypíše.
# Definice funkce pro načtení pole
def nacti(pocet_prvku):
pole = []
print(f"Zadejte {pocet_prvku} prvků do pole:")
for i in range(pocet_prvku):
hodnota = int(input(f"Zadej prvek {i + 1}: "))
pole.append(hodnota)
return pole
# Definice procedury pro vypsání pole
def vypis_prvek(pole):
print("\nObsah zadaného pole:")
# Využití cyklu pro průchod prvků
for prvek in pole:
print(prvek, end=" | ")
print() # Zalomení řádku na konci
# --- Hlavní tělo programu ---
pocet = 5 # Pevně deklarovaná velikost pole
moje_pole = nacti(pocet)
vypis_prvek(moje_pole)
Zde excelentně demonstrujeme výhody podprogramů. Modul "hlavní tělo" je velmi krátké a jasné (čitelnost). Funkce pro načítání přebírá jako parametr požadovanou velikost, založí prázdný seznam a přes cyklus "for" jej naplní uživatelským vstupem. Návratová hodnota (naplněné pole) se následně předá proceduře výpisu.