8.1 Funkce a jejich užití

Pojem funkce znáte z matematiky. Volně řečeno, funkce je nějaký předpis, který bere vstupy, nějak je využije a vrátí nějaký výstup. Funkce může např. vzít vektor pozorování, spočítat z nich průměr a ten vrátit jako svůj výstup. Tento význam mají funkce i v R. Technicky přesněji je funkce zapouzdřený kus kódu s jasným rozhraním (interface). Uživatel nemusí vědět nic o tom, co se děje uvnitř funkce – stačí mu vědět, jak funkci zavolat (jaké má jméno a jaké bere vstupní parametry) a jaké vrací hodnoty. Jako příklad si představte funkci mean(), kterou už znáte. Abyste ji mohli použít, nepotřebujete vědět nic o tom, jak je funkce naprogramovaná. Stačí, když znáte její jméno, seznam parametrů, které bere, a rozumíte tomu, jakou hodnotu vrací.

Existuje několik důvodů, proč vytváříme a používáme funkce:

  • Některé řádky kódu spouštíme znovu a znovu. Pokaždé dané řádky kopírovat je hloupé: váš výsledný kód by byl zbytečně dlouhý a nepřehledný. Navíc při kopírování vznikne snadno chyba, a to zejména v situaci, kdy v kódu potřebujete změnit jména proměnných. Rozkopírovaný kód se také špatně udržuje: pokud chcete něco změnit, musíte to změnit na mnoha místech a doufat, že jste na žádné nezapomněli. Při změnách také snadno něco pokazíte. Pokud jste naproti tomu svůj kód uložili do funkce, stačí, když jej změníte na jednom jediném místě.
  • Je dobré oddělit kód a jeho interface. Pak můžeme vlastní kód snadno změnit. Pokud se nezměnil interface, je volání kódu stále stejné, takže nemusíme měnit nic jiného.
  • Je snazší předat uživateli funkci než kód. Uživatel (včetně vašeho budoucího já) nemusí tušit, co se děje uvnitř; stačí mu, když ví, jak funkci spustit.
  • Kód je modulárnější, úspornější a lépe se čte a udržuje. Pamatujte, že váš skript musí být schopen přečíst a porozumět mu nejen počítač, ale také lidé (minimálně vy sami v budoucnosti).

Ukažme si to na příkladu. Řekněme, že chceme normovat několik vektorů tak, že každé hodnotě ve vektoru přiřadíme odpovídající hodnotu v kumulativní distribuční funkci. Srovnejte následují dvě možnosti: kód, který vznikl rozkopírováním:

e1 <- ecdf(x1)
x1n <- e1(x1)
e2 <- ecdf(x2)
x2n <- e2(x2)
e3 <- ecdf(x3)
x3n <- e3(x3)
e4 <- ecdf(x4)
x4n <- e4(x4)

s kódem pomocí funkce:

normuj <- function(x) {
    e <- ecdf(x)
    e(x)
}
x1n <- normuj(x1)
x2n <- normuj(x2)
x3n <- normuj(x3)
x4n <- normuj(x4)

Kód napsaný s pomocí nové funkce je přehlednější a stručnější: čím více vektorů chcete normovat, tím více řádků kódu ušetříte. Je také menší šance, že při kopírování něco pokazíte (např. zapomenete na jeden řádek, kde se volá funkce ecdf() nebo v posledním řádku zapomenete změnit x1 na x4). Kód napsaný s pomocí funkce se také lépe udržuje. Představte si, že se rozhodnete svá data normovat jinak, např. tak, že spočítáte jejich \(z\)-skóre. V prvním případě budete muset přepsat celý kód. Ve druhém stačí změnit jen tělo funkce normuj(). Kód napsaný pomocí funkce navíc šetří operační paměť počítače, protože nezaplevelil pracovní prostředí R čtyřmi mezivýpočty (funkcemi e1()e4()).

Pokud to zobecníme, svůj kód byste měli přepsat jako funkci v jednom z několika následujících případů:

  1. Pokud nějaký kus kódu rozkopírováváte, měli byste z něj raději vytvořit funkci. (V programování obecně platí zásada, že byste se neměli nikdy opakovat, takže každá konkrétní hodnota nebo operace by měla být v kódu definovaná jen na jednom jediném místě, kde je snadné ji konzistentně změnit.)

  2. Pokud potrebujete, abyste mohli nejake funkci predat svuj kod jako parametr, pak jej take potrebujete zabalit do funkce (touto situaci se budeme zabyvat v kapitole 10).

  3. Pokud je váš kód velmi dlouhý (nevejde se na obrazovku), pak byste měli zvážit jeho rozdělení na kusy. Zde se nabízí dvě možnosti: (a) rozdělit kód na kusy vizuálně pomocí komentářů a hlaviček (RStudio umožňuje vložit do kódu komentářové hlavičky klávesovou zkratkou Ctrl-Shift-R; zobrazuje je pak vlevo dole v okně editoru a případně v osnově v pravém pruhu vedle editoru, což zapnete ikonkou osnovy); nebo (b) rozdělit svůj kód na funkce. (Stejně tak, pokud vytvoříte funkci, jejíž kód je delší než jedna obrazovka, měli byste zvážit ji rozdělit do více kratších funkcí, protože tím výrazně zvýšíte čitelnost svého kódu.)

Pokud jste na pochybách, zda vytvořit funkci, raději ji vytvořte. Většina začátečníků chybuje spíše tím, že vytváří složitý dlouhý monolitický kód, než že by jej příliš členila do jednotlivých funkcí.