Základy algoritmizace I

Smyslem je vytvořit algoritmus, který vyřeší požadovanou úlohu. Dobré je se nejdříve zamyslet nad postupem, použít papír a tužku, a teprve pak začít tvořit program.

Podmínky

Logické operátory a operace == je rovno, != není rovno, < > <= >=, ! negace, & a zároveň, | nebo (pro všechny prvky srovnávaných vektorů), && a || porovnávají jen první prvek vektorů.

Operátory mají různé pořadí vyhodnocování (tzv. precedence operátorů). Podobně, jako * má vyšší přednost než +, tak & má vyšší přednost než |, oba mají ale vyšší přednost než např. ==. Pokud si nejsme jisti, je lepší závorkovat. Zvýší se tím i přehlednost zápisu.

identical() vrátí, zda jsou obsahy parametrů identické, xor() provádí exclusive-or operaci, which(vektor > 3) vrátí indexy prvků, splňujících podmínku (NA přeskočí), což je výhoda oproti pouhému samostatnému vektor > 3. all(vektor > 3) vrátí TRUE/FALSE, zda všechny prvky vektoru splňují podmínku, any(vektor > 3) vrátí, zda alespoň jeden prvek splňuje podmínku. is.element(co, vČem) nebo co %in% vČem vrátí, zda je prvek co obsažen ve vektoru vČem. all(kandidáti %in% vČem) ověří, že všichni kandidáti jsou součástí vČem. match(co, vČem) vrátí index první nalezené pozice.

Nápověda: ?‘if’

x <- 5

if (x == 0) {
    print("x je 0.")
}

print("Pokračujeme.")
## [1] "Pokračujeme."
x <- 0

if (x == 0) {
    print("x je 0.")
}

print("Pokračujeme.")
## [1] "x je 0."
## [1] "Pokračujeme."
a <- 5
b <- 3
if (b == 0) {
    print("Nulou nelze dělit.")
} else {
    a / b
}
## [1] 1.666667

Poznámka: else musí být na stejném řádku, jako znak }. V jiných jazycích je to jinak, ale R je R :-)

a <- 5
b <- 0
if (b == 0) {
    print("Nulou nelze dělit.")
} else {
    a / b
}
## [1] "Nulou nelze dělit."
x <- 5
if (x == 0) {
    print('x je 0')
} else if (x < 10) {
    print('x je menší než 10')
} else if (x < 20) {
    print('x je menší než 20')
} else if (x >= 20 & x <= 30) {
    print('x je mezi 20 a 30 (včetně)')
} else {
    print('x je něco, co neřešíme')
}
## [1] "x je menší než 10"

Najde se první platná podmínka, ostatní se pak již netestují. Přestože je x zároveň menší než 10 i než 20, provede se jen první platná podmínka.

pozdrav <- "ahoj"

if (pozdrav == "ahoj") {       # Porovnávání řetězců
    print("Nazdar.")
} else {
    print("Dobrý den.")
}
## [1] "Nazdar."
x <- 5

hodnota <- if (x > 3) {        # Poněkud nezvyklá, ale užitečná konstrukce.
    10
} else {
    0
}

hodnota
## [1] 10
# Konstrukce ve stylu "Excel". Takhle je to pěkné, ale pro složitější větvení
# by to bylo značně nepřehledné.
x <- 5

hodnota <- ifelse(x > 3, 10, 0)
hodnota
## [1] 10

Cykly

for (i in 1: 5) {
    print(i)    # příkaz se opakuje pro každé nové 'i'
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5

Pozor na operátor dvojtečka : a jeho prioritu, která je vyšší než aritmetické operace (na rozdíl od např. jazyka Matlab)! Pokud se mají meze cyklu počítat vzorcem, výrazy je nutno závorkovat!

1:5
## [1] 1 2 3 4 5
1-1 : 5-1      # pravděpodobně dost nečekaný výsledek
## [1] -1 -2 -3 -4 -5
(1-1) : (5-1)  # asi to, co jsme měli na mysli
## [1] 0 1 2 3 4
x <- LETTERS[1:4]
x
## [1] "A" "B" "C" "D"
for (i in 1:length(x))
    print(x[i])
## [1] "A"
## [1] "B"
## [1] "C"
## [1] "D"
for (i in seq_along(x))
    print(x[i])
## [1] "A"
## [1] "B"
## [1] "C"
## [1] "D"
for (letter in x)
    print(letter)
## [1] "A"
## [1] "B"
## [1] "C"
## [1] "D"
# Pro zvídavé :-)
letters[1:4]
## [1] "a" "b" "c" "d"
tolower(LETTERS[1:4])
## [1] "a" "b" "c" "d"
tolower("AŤ ŽIJE ČEŠTINA")
## [1] "ať žije čeština"
pocet <- 0

while (pocet < 10) {     # opakuj, dokud platí podmínka
    print(pocet)
    pocet <- pocet + 1
}
## [1] 0
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
x <- 0
repeat {           # opakuj pořád
    x <- x + 1
    
    if (x < 3)
        next       # skoč na repeat
    
    if (x > 9)
        break      # vyskoč ven z tohoto cyklu
    
    print(x)
}
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9

Pozn. takovýmto strukturám je lépe se vyhýbat, protože se jednoduše může stát chyba v úvaze a cyklus nikdy neskončí. Odborně se tomu říká tzv. zacyklení.

Vstup z klávesnice od uživatele

Nápověda: ?readline

Funguje jen v interaktivním prostředí (skript R, nikoliv v R Markdown, na webové stránce, prezentacích, Shiny aplikacích apod.)

odpoved <- readline("Jste spokojen s tímto kurzem? ")

print(paste0("Řekl jste: ", odpoved))
# defaultní oddělovač (sep) je mezera.

if (tolower(substr(odpoved, 1, 1)) == "n") {
    print("To není možné. Vymýšlíte si!")
} else {
    print("V pořádku.")
}

Tip: Zatímco Ctrl+Alt+R spustí celý skript a vypisuje i příkazy (tzv. echo), Ctrl+Shift+S spustí celý skript, ale bez echa.

Typ výstupu je character (řetězec).

vstup <- "123"
vstup + 5    # Error in "123" + 5 : non-numeric argument to binary operator

Převod na číslo: as.numeric(“123”)

vstup <- "123"
cislo <- as.numeric(vstup)
cislo + 5
## [1] 128

Tvorba funkcí

obvodKruhu <- function(polomer) {
    obvod <- 2*pi*polomer
    return(obvod)  # poslední prováděný příkaz musí tzv. vrátit výslednou hodnotu
                   # (což může být teoreticky klidně list nebo data.frame plný mnoha hodnot)
}

obvodKruhu(10)
## [1] 62.83185
obvodObdelniku <- function(a, b) {   # dva parametry nejsou problém
    obvod <- 2*(a+b)
    return(obvod)
}

obvodObdelniku(3, 5)
## [1] 16
obvodObdelniku <- function(a = 2, b = 10) {   # defaultní hodnoty parametrů, uživatel je pak nemusí vyplňovat
    obvod <- 2*(a+b)
    return(obvod)
}

obvodObdelniku(b = 8)
## [1] 20

Zaokrouhlování může být problém. Prozkoumáme-li nápovědu funkce round, chování nás může překvapit.

?round

round(0.5)
## [1] 0
round(1.5)
## [1] 2
round(2.5)
## [1] 2
round(3.5)
## [1] 4

Vyplatí se vytvořit si vlastní funkci.

zaokrouhli <- function(cislo) {
    cele <- trunc(cislo + sign(cislo)*0.5)
    return(cele)
}

zaokrouhli(0.5)
## [1] 1
zaokrouhli(1.5)
## [1] 2
zaokrouhli(2.5)
## [1] 3
zaokrouhli(-0.5)
## [1] -1
zaokrouhli(-1.5)
## [1] -2
zaokrouhli(1.4)
## [1] 1
zaokrouhli(-1.4)
## [1] -1

Pozn. trunc odřízne desetinnou část, sign vrátí -1, 0, 1 podle znaménka čísla. Další užitečné funkce: ceiling zaokrouhlí vždy na vyšší číslo, floor na nižší.

ceiling(2.5)
## [1] 3
ceiling(2.1)
## [1] 3
ceiling(-2.9)
## [1] -2

Úlohy

  1. Vytvořte funkci, která vypočítá DPH ze zadané částky.
  2. Vytvořte funkci, která zjistí, zda jsou dvě čísla dělitelná beze zbytku.
  3. Vytvořte vlastní funkci, která pomocí for cyklu vypočte faktoriál přirozeného čísla. Pozn. faktoriál 5 = 1 * 2 * 3 * 4 * 5 = 120.
  4. Vytvořte skript, který vypisuje čísla řady 1, 2, 4, 7, 11, 16 atd.
    1. Celkem 20 čísel.
    2. Postupně všechna čísla, která nepřekročí hodnotu 100.

© 25. 11. 2015 Tomáš Bořil, borilt@gmail.com