- proč psát funkce
- jak vytvořit funkci
- jak volat funkci
- (lexical scoping)
- základní řídící struktury
Funkce je 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 použít:
V R je funkce objekt jako jakýkoli jiný. To znamená, že
Funkci tvoří tři části:
Funkce v R mohou mít vedlejší účinky (side effects).
Příkladem takové funkce je funkce print()
– místo, aby vracela nějakou hodnotu, vypíše svůj argument nějakým způsobem do konzoly.
Je jednodušší a bezpečnější psát čisté funkce bez vedlejších účinků.
Funkci vytvoříme tak, že výsledek vrácený funkcí function()
uložíme do proměnné:
jmeno_funkce <- function(parametry_funkce_oddělené_čárkami) výraz_který_funkce_vyhodnocuje
Pokud tělo funkce obsahuje víc než jeden výraz, je třeba tyto výrazy sbalit do bloku pomocí složených závorek:
jmeno_funkce <- function(parametry_funkce_oddělené_čárkami) { výraz_1 výraz_2 ... výraz_n }
Funkce vrací poslední vyhodnocený výraz jako svoji hodnotu.
nasobek <- function(x, y) x * y
class(nasobek)
## [1] "function"
nasobek(3, 4)
## [1] 12
nasobek(7, 8)
## [1] 56
Funkce vrací jako svou hodnotu
return()
– při tom skončí# totéž, co předchozí funkce nasobek <- function(x, y) { n <- x * y return(n) }
Funkce return()
se obvykle použije, pokud má funkce skončit svůj běh jinde než na svém posledním řádků.
Napište funkci d()
, která vezme vektor a vrátí jeho diferenci.
Hint: použijte indexování.
R má samozřejmě funkci diff()
. Na několika vektorech otestujte, že d()
dává stejné výsledky jako diff()
.
Funkce se vždy volá se závorkami, i kdyby neměla žádné parametry.
Pokud zavoláme funkci bez závorek, vypíše se kód funkce (její tělo):
hello_world <- function() print("Ahoj, světe!") hello_world
## function() ## print("Ahoj, světe!")
hello_world()
## [1] "Ahoj, světe!"
Argumenty funkce mohou mít implicitní hodnoty – pokud není hodnota argumentu zadána, vezme se implicitní hodnota:
soucin2 <- function(x, y = 2) x * y
soucin2(3, 4)
## [1] 12
soucin2(3) # y = 2, implicitni hodnota
## [1] 6
Funkce se volá svým jménem se závorkami.
Argumenty mohou být funkci předány třemi způsoby:
f(a = 1, b = 2)
– v tomto případě nezáleží na pořadí parametrůf(a, b)
znamená volání f(1, 2)
, že \(a=1\) a \(b=2\)Při zadání parametru jménem R umožňuje jméno parametru zkrátit, pokud je zkratka jednoznačná.
Ve funkci f(number, notalk)
je možné první parametr zkrátit na num
i nu
, ovšem ne na n
, protože n
není jednoznačné.
Zkracování parametrů zjednodušuje interaktivní práci; při psaní skriptů se však výrazně nedoporučuje, protože autor funkce by později mohl přidat další parametr a zkrácené jméno už by nemuselo být jednoznačné.
Předávání parametrů jménem, pozicí a implicitní hodnotou lze libovolně míchat.
R postupuje takto:
Pokud nechcete mít v kódu zmatek, doporučuji následující:
Identická volání funkce mean(x, trim = 0, na.rm = FALSE, ...)
:
mean(x, na.rm = TRUE) mean(x, trim = 0, na.rm = TRUE) mean(x = x, trim = 0, na.rm = TRUE) mean(x, 0, TRUE)
Ale už ne:
mean(x, TRUE)
Vypsalo by chybu:Error in mean.default(x, TRUE) : 'trim' must be numeric of length one
...
(1)Speciální parametr ...
libovolný počet parametrů na dané pozici; všechny parametry uvedené za ním musejí být volány plným jménem.
Parametr ...
se používá zejména ve dvou situacích
...
(2)Funkce paste()
spojuje libovolný počet řetězců.
Počet parametrů není znám dopředu, proto funkce paste používá parametr ...
:
paste
## function (..., sep = " ", collapse = NULL) ## .Internal(paste(list(...), sep, collapse)) ## <bytecode: 0x135e5f0> ## <environment: namespace:base>
paste("Ahoj,", "lidi.", "Jak se máte?")
## [1] "Ahoj, lidi. Jak se máte?"
...
(3)Funkce print()
dokáže vypsat do konzoly obsah mnoha různých objektů – pro každý objekt volá speciální funkci přizpůsobenou tomuto objektu.
Proto je třeba mít možnost funkci print()
předat libovolné parametry, které funkce print()
předá dál:
## function (x, ...) ## UseMethod("print") ## <bytecode: 0x2d5f290> ## <environment: namespace:base>
Proměnné definované uvnitř funkce jsou lokální; po ukončení běhu funkce zaniknou. (To platí i pro parametry funkce.)
a <- 3 ; b <- 7 f <- function(x, y) { a <- 5 x <- 2 * x a + x + y } f(b, 3) # vrací 5 + 2 * 7 + 3 = 22
## [1] 22
a # hodnota a se mimo funkci nezměnila
## [1] 3
Když R hledá hodnotu nějaké proměnné, prochází série různých prostředí (environment).
Pokud použijete proměnnou x
v konzoli, začíná R hledat x
R používá lexical scoping, což znamená, že vyhledávání začíná v prostředí funkce, a pak v prostředí, ve kterém byla funkce definovaná.
Pokud je funkce definovaná v globálním prostředí, vše funguje normálně.
y <- 5 f <- function(x) { x * y # x se našlo v prostředí funkce, y v globální prostředí } f(4)
## [1] 20
Pozor: to, že R hledá proměnné i mimo funkci může vést k těžko hledatelným chybám, když zapomenete proměnnou definovat ve funkci.
Pokud však funkce definovaná uvnitř jiné funkce, pak vyhledávání začíná v prostředí vnější funkce. To slouží k tvorbě tzv. function factories a uzávěr (closures).
n <- 17 make.power <- function(n) { g <- function(x) x ^ n g } square <- make.power(2) cube <- make.power(3) c(s = square(2), c = cube(2), n = n)
## s c n ## 4 8 17
Přiřazení ve funkci přiřazuje hodnotu vždy do lokální proměnné.
Pokud se pokud se přiřazuje do proměnné mimo funkci nebo do parametru funkce, R tiše vytvoří lokální proměnnou se stejným jménem, která původní proměnnou zastíní:
x <- 5 y <- 5 f <- function(z) { x <- 2 z <- 3 x * z } c(x = x, y = y, f = f(7))
## x y f ## 5 5 6
Zatím jsme předpokládali, že kód skriptu běží řádek po řádku.
Někdy je však potřeba běh kódu různě modifikovat:
K provedení kódu, pouze pokud je splněná nějaká podmínka, slouží if
:
x <- 1 if (x == 1) print("O.K.: x je jedna!")
## [1] "O.K.: x je jedna!"
Syntaxe: v závorce je logický výraz, na druhém řádku je kód, který se provede pouze v případě, že logický výraz má hodnotu TRUE
.
Pokud se má provést více než jeden řádek kódu, je třeba jej seskupit pomocí složených závorek:
if (x == 1) { a <- 5 print("O.K.: x je jedna!") }
## [1] "O.K.: x je jedna!"
a
## [1] 5
Pokud se má nějaký kód provést při platnosti podmínky a jiný, pokud podmínka neplatí:
if (x == 1) { print("O.K.: x je jedna!") } else { print("O.K.: x není jedna!") }
## [1] "O.K.: x je jedna!"
Pozor: else
musí být na stejném řádku, jako končící složená závorka nebo kód, který se provádí při splnění podmínky.
Pokud se podmínka skládá z více logických výrazů, používají se "zkratující" logické operátory &&
a ||
:
x <- 5 y <- 7 if ((x == 1) || y == 7) print("Podmínka splněna, rytíři Brtníku!")
## [1] "Podmínka splněna, rytíři Brtníku!"
Napište funkci, která aabs(x)
, která vrátí absolutní hodnotu čísla (skaláru) x
.
(R má samozřejmě funkci abs()
.)
R má i klasické cykly, viz dokumentace:
for()
– pokud se má kus kódu opakovat \(x\)-krátwhile()
– pokud se má kus kódu opakovat, dokud je splněná podmínkarepeat
– pokud se má kus kódu opakovat do zastaveníPříliš se ale nepoužívají – R má pro opakování lepší struktury (viz příště).
K zastavení běhu skriptu slouží funkce stop()
: zastaví běh skriptu s chybou a vypíše svůj argument:
# kód se zastaví, pokud v není řetězec (zde se nezastaví) v <- "ahoj" if (!is.character(v)) stop("v není řetězec!")
Zprávy je možné do konzoly vypsat pomocí funkce message()
, varování pomocí funkce warning()
:
if (!is.list(v)) warning("Pozor: v není seznam!")
## Warning: Pozor: v není seznam!
Do konzoly je možné vypisovat i pomocí funkcí print()
, cat()
apod.
Zprávy o běhu kódu však vypisujte raději pomocí message()
a warning()
: jsou barevně odlišené a je možné je snadno potlačit.
Vylepšete funkci aabs(x)
tak, aby skončila chybovou hláškou, pokud x
není numerický skalár (tj. vektor délky 1).
(Funkce abs()
samozřejmě pracuje s celými vektory.)
K samostudiu s využitím dokumentace…
Výběr ze slovníku z kapitoly v knize Hadleyho Wickhama Advanced R:
Vytvořte funkci xMax(v, n)
, která vrátí \(n\)-tý největší prvek vektoru v
. Vektor v
musí být zadán vždy. Pokud je n
zadáno, vrátí n
-tý největší prvek; pokud n
není zadáno, vrátí druhý největší prvek.
Funkce skončí chybou, pokud
n
je zadáno, ale není to jedno celé číslo (pozor: nemusí být třída integer!, chyba "n isn't one round number!"
),n
je menší než jedna nebo větší než délka vektoru v
(chyba "n is out of bounds!"
)v
není numerický atomický vektor (chyba "v is not numeric atomic vector!"
)v
unikátní, tj. některá hodnota se ve vektoru vyskytuje vícekrát (chyba "v doesn't have unique values!"
); hint: funkce unique()
Při ukončení chybou musí funkce vypsat předepsanou chybovou hlášku.