Úvod do jazyka Haskell

Ciele

  1. Naučiť sa používať interaktívny interpretátor GHCi.
  2. Naučiť sa definovať vlastné funkcie a používať ich.
  3. Oboznámiť sa s koncepciou typov v jazyku Haskell.

Postup

Krok 1: GHCi

Haskell je kompilovaný jazyk, ale existuje aj interaktívny interpretátor GHCi, ktorý umožňuje rýchlo skúšať zabudované aj vlastné funkcie. Po spustení GHCi je možné zadávať výrazy a sledovať ich výsledky.

Haskell podporuje bežné infixné operátory, napríklad:

1 + 2
2 * (5^2 - 4)
(4 <= 2) && (4 /= 2)
"hello" == "hello"

Aplikácia funkcie sa zapisuje bez zátvoriek okolo argumentov. Argumenty sú oddelené len medzerou. Napríklad:

max 4 2
sin (pi / 2)
sin pi / 2 == (sin pi) / 2

Úloha 1.1

Napíšte výraz, ktorý vypočíta dĺžku prepony pravouhlého trojuholníka s odvesnami dĺžky 3 a 4. Použite pritom funkciu sqrt.

Krok 2: Vlastné funkcie

Vlastné funkcie sa píšu v súbore s príponou .hs. Definícia funkcie má tvar názovFunkcie parametre = výraz, napríklad:

add x y = x + y

Úloha 2.1

Vytvorte si súbor cvicenie.hs a zapíšte doň definíciu funkcie add. Načítajte tento súbor do interaktívneho interpretátora pomocou príkazu :load cesta/k/súboru.hs alebo pomocou tlačidla na otvorenie súboru vo WinGHCi. Skúste použiť funkciu add v interpretátore.

Úloha 2.2

Definujte funkciu hypotenuse, ktorá vypočíta dĺžku prepony pravouhlého trojuholníka na základe dĺžok odvesien.

Poznámka

Po zmene súboru je možné ho znovu načítať pomocou príkazu :r alebo :reload.

Podmienené vykonávanie je možné dosiahnuť pomocou tzv. strážcov (guards):

factorial n
    | n == 1 = 1
    | n >= 1 = n * factorial (n-1)

Poznámka

Telo funkcie musí byť odsadené keďže Haskell, podobne ako Python, používa odsadzovanie ako časť syntaxe.

Úloha 2.3

Definujte funkciu middleNumber, ktorá vráti stredné z troch zadaných čísel:

middleNumber :: Integer -> Integer -> Integer -> Integer

Príklady použitia:

middleNumber 5 3 8 == 5
middleNumber 1 1 1 == 1

Krok 3: Typy

Haskell má statickú typovú kontrolu, ale vo väčšine prípadov nie je nutné uvádzať typy explicitne. Prekladač dokáže automaticky odvodiť typy hodnôt a funkcií. Odvodený typ je možné pozrieť v GHCi pomocou špeciálneho príkazu :t, napríklad:

:t 'a'
:t 5
:t not
:t sqrt
:t max

Úloha 3.1

Skontrolujte typy vašich funkcií hypotenuse a factorial.

Typ funkcie môžeme deklarovať ak explicitne, napríklad:

add :: Integer -> Integer -> Integer
add x y = x + y

Úloha 3.2

Definujte typ funkcie factorial ako Int -> Int a použite ju na výpočet faktoriálu čísla 21. Zmeňte typ na Integer -> Integer a vyskúšajte to znovu.

Poznámka

Na konverziu medzi typmi Int a Integer je možné použiť funkcie fromInteger a toInteger.

Krok 4: Viac funkcií

Na priblížny výpočet odmocniny je možné použiť Newtonovú metódu. Jej podstata spočíva v postupnom spresňovaní aproximácie. Ak y je odhad hodnoty druhej odmocniny čísla x, tak lepší odhad dokážeme vypočítať ako priemer čísel y a x/y.

Takže v každom kroku výpočtu je možné overiť, či odhad je už dostatočne presný, a ak nie je, vypočítať presnejší. Na overenie nám stačí vypočítať mocninu odhadu y a porovnať ju s odmocňovaným číslom x. Ich rozdiel musí byť menší ako nejaká stanovená hranica, napríklad:

$$ \left| y^2 - x \right| < 0,001 $$

Úloha 4.1

Definujte funkciu newtonSqrt pre výpočet druhej odmocniny pomocou Newtonovej metódy. Definujte si tiež pomocné funkcie

  • sqrtIter pre rekurzívne spresňovanie aproximácie,
  • improve pre výpočet lepšej aproximácie,
  • goodEnough pre jej overenie.

Poznámka

Na výpočet absolútnej hodnoty čísla môžete použiť štandardnú funkciu abs.

Krok 5: Domáca úloha

Úloha 5.1

Definujte funkciu getValue, ktorej prvým argumentom je zoznam tovarov. Tie sú reprezentované dvojicou. Prvý prvok dvojice je názov výrobcu a druhý hodnota konkrétneho tovaru. Jednotlivý výrobca môže byť v zozname viackrát. Vašou úlohou je vypočítať výslednú sumu za všetky tovary od výrobcu, ktorý je určený druhým parametrom.

products :: [(String, Int)]
products = [("avia",1000), ("trabant",500), ("avia",400), ("tatra",600),
            ("trabant",200), ("avia",700)]
getValue :: [(String, Int)] -> String -> Int
getValue products "trabant" == 700

Poznámka

Vo funkcii použite funkcie vyššieho rádu ako sú map a filter. Okrem toho môžete použiť funkciu sum, ktorá vypočíta súčet prvkov zoznamu čísel.

Úloha 5.2

Definujte funkciu replace, ktorá nahradí slova v texte podľa zadaného zoznamu náhrad. Funkcia má nahradzovať len celé slová, nie ich časti. Môžete pritom použiť funkcie words a unwords.

replace :: [(String, String)] -> String -> String

replacements = [("fox", "cat"), ("he", "she"), ("dog", "crocodile")]
sentence = "The quick brown fox jumps over the lazy dog"
replace replacements sentence == "The quick brown cat jumps over the lazy crocodile"

Zdroje