Inštalácia
%TODO
Hello World
ghci> print "Hello World"
- reťazce sú v úvodzovkách
print
je funkcia s jedným parametrom- okolo parametrov funkcie sa nepíšu zátvorky
Zoznamy
- základná dátová štruktúra
- prvky musia byť rovnakých typov
Zoznamové literály
Vytvorte zoznam troch čísiel
ghci> [1, 2, 3]
Pokúste sa vytvoriť zoznam čísla a mena
ghci> [1, "John"]
Chyba:
No instance for (Num [Char]) arising from the literal `1'
Possible fix: add an instance declaration for (Num [Char])
In the expression: 1
In the expression: [1, "John"]
In an equation for `it': it = [1, "John"]
- Zoznamy musia mať rovnaké prvky.
- Ak chceme zoznam nerovnakých prvkov, môžeme využiť tice (tuples), o nich ale neskôr.
Funkcie
Vypočítajte súčet čísiel od 1 po 10
ghci> sum [1..10]
55
sum
je zabudovaná funkcia- v skutočnosti každá funkcia patrí do nejakého modulu
- automaticky je dostupný modul
Prelude
Vypočítajte súčet čísiel od 1 po 10 [Gauß style]
ghci> (10 * (10 + 1)) / 2
Vytvorte funkciu pre súčet čísiel od 1 po n
ghci> let sucet_po n = n * (n + 1) / 2
- v tejto deklarácii nepoužívame dátové typy
- Haskell ich odvodí
- je však dobrou konvenciou deklarovať typy explicitne
- funkcia pripomína matematický zápis
- pokiaľ sme v REPL editore, potrebujeme dať pred funkciu
let
funkciu voláme:
ghci> sucet_po 10
Vytvorte funkciu pre faktoriál
ghci> let faktorial n = product [1..n]
- využili sme matematickú definíciu 1 x 2 x 3 x … x n
- vieme zapísať aj podľa matematického rekurzívneho predpisu, ale to nie je potrebné, ani efektívne
Vytvorte funkciu pre sínus
V externom súbore:
faktorial n = product [1..n]
sinus x n = sum $ [ ((-1)**i / faktorial(2*i + 1)) * (x ** (2*i + 1)) | i <- [0..n] ]
A potom:
ghci> sinus (3.14 / 2) 10
0.9999996829318347
Vypočítajte korene kvadratickej rovnice
kvadr_rovnica_diskr a b c = b**2 - (4 * a * c)
kvadr_rovnica_x_1 a b c = ((-b) + sqrt d) / (2 * a) where d = kvadr_rovnica_diskr a b c
kvadr_rovnica_x_2 a b c = ((-b) - sqrt d) / (2 * a) where d = kvadr_rovnica_diskr a b c
kvadr_rovnica a b c = (kvadr_rovnica_x_1 a b c, kvadr_rovnica_x_2 a b c)
Vypočítajte rozmery ISO papiera (od A0 po A11)
ghci> let iso (x, y) = (y / 2, x)
ghci> take 11 $ iterate iso (841, 1189)
Alebo:
ghci> map (\x -> (x, x * sqrt 2)) $ take 11 $ iterate iso 841
Nájdite permutácie daného zoznamu
import Data.List
-- [] -> [[]]
-- [1] -> [[1]]
-- [1,2] -> [[1,2],[2,1]]
permutacie [] = [[]]
permutacie [x] = [[x]]
permutacie zoznam = map (\prvok -> [prvok] ++ delete prvok zoznam) zoznam
- druhý riadok je zbytočný, je kvôli prehľadnosti
- pozor na hraničný prípad, musíme obsiahnuť prázdny zoznam v zozname
Nájdite podmnožiny daného zoznamu
podmnoziny [] = [[]]
podmnoziny [x] = [[x]]
podmnoziny (x:xs) = podmnoziny xs ++ [ x : zvysok | zvysok <- powerset xs ]
Vygenerujte binárne postupnosti maximálnej zadanej dĺžky
nj 1 = ["0", "1"]
-- nj 2 = ["00", "01", "10", "11", "0", "1"]
nj n = (map (\x -> "0" ++ x ) $ nj (n-1) ) ++ (map (\x -> "1" ++ x ) $ nj (n-1))
Alebo:
nj 1 = ["0", "1"]
-- nj 2 = ["00", "01", "10", "11", "0", "1"]
nj n = (map (\x -> '0' : x ) $ nj (n-1) ) ++ (map (\x -> '1' : x ) $ nj (n-1))
Po eta-redukcii:
nj 1 = ["0", "1"]
-- nj 2 = ["00", "01", "10", "11", "0", "1"]
nj n = (map ('0':) $ nj (n-1) ) ++ (map ('1':) $ nj (n-1))
S where
:
nj 1 = ["0", "1"]
nj n = (map ('0':) kratsi ) ++ (map ('1':) kratsi) where kratsi = nj (n-1)
S číslami:
nj 1 = [[0], [1]]
nj n = (map (0:) kratsi ) ++ (map (1:) kratsi) where kratsi = nj (n-1)
Vymažte n-tý znak z reťazca
deleteAt index str = left ++ tail right
where (left, right) = splitAt index str
Na vstupe je zoznam čísiel. Vytvorte nový zoznam, kde každý prvok bude obsahovať reťazec s príslušným počtom hviezdičiek.
[2, 3, 2, 1, 5] ==> ["**","***","**","*","*****"]
Využijeme:
prvok prevedieme na reťazec hviezdičiek cez vlastnú funkciu
ghci> let toStars n = replicate n '*'
replicate
je zabudovaná funkcia zPrelude
mapovanie cez funkciu
map
ghci> map toStars [2, 3, 2, 1, 5]
Alternatívne [cez lambdu]
map (\n -> replicate n '*') [2, 3, 2, 1, 5]
Alternatívne [cez flip
]
Všimnime si, že replicate
berie najprv číslo a potom reťazec. Ak by sme vedeli vymeniť argumenty, mohli by sme využiť curryfikáciu / eta-redukciu
ghci> let replicate' list n = replicate n list
ghci> map (replicate' '*') [2, 3, 2, 1, 5]
Na takúto výmenu argumentov slúži funkcia flip
, ktorá vezme binárnu funkciu a vráti funkciu so vzájomne vymenenými argumentami:
ghci> map (flip replicate '*') [2, 3, 2, 1, 5]
Získať
i
-ty prvok zo zoznamu
Toto robí funkcia !!
. Vlastné riešenie:
list_get [x] index
| index == 0 = x
| otherwise = error "Index out of range"
list_get (x:xs) index
| index == 0 = x
| otherwise = list_get xs (index - 1)
- ak máme jednoprvkový zoznam, vrátime jeho nultý prvok. (Pre ostatné indexy vyhodíme chybu.)
- ak máme viacprvkový zoznam, napr.
AHOJ
- ak chceme nultý index, vrátime hlavu (
A
) - ak chceme väčší index, napr. 2 (chceme získať
O
), zoberieme chvost zoznamu (HOJ
) a hľadáme v ňom index o jedna menší (teda 1)
- ak chceme nultý index, vrátime hlavu (
Nahraďte i-tý prvok
list_replace [x] index newElement
| index == 0 = [newElement]
| otherwise = error "Index out of range"
list_replace (x:xs) i newElement
| i < 0 = error "Index must be >0"
| i == 0 = newElement : xs
| otherwise = x : list_replace xs (i-1) newElement
- ak máme jednoprvkový zoznam, nahradíme jeho nultý prvok. (Pre ostatné indexy vyhodíme chybu.)
- ak máme viacprvkový zoznam, napr.
AHOJ
- ak chceme nultý index, nahradíme hlavu novým prvkom a nalepíme na ňu pôvodný chvost
- ak chceme väčší index, napr. 2 (nahradiť znak
O
), zoberieme chvost zoznamu (HOJ
) a nahradíme v ňom prvok na indexe o jedna menšom (teda 1). Pred to všetko predlepíme nezmenenú pôvodnú hlavu.
Rozsahy (ranges)
Vytvorte zoznam čísiel od 1 po 10.
[1..10]
Výsledok:
[1,2,3,4,5,6,7,8,9,10]
Vytvorte zoznam párnych od 1 po 10.
[2,4..10]
- Vieme deklarovať krok (step).
Spočítajte
Dátové typy
Bedáky s typmi
Toto funguje:
ghci> sqrt 25
5.0
Lebo typy:
ghci> :t 25
25 :: Num a => a
- Konštanta
25
sa správa ako polymorfný literál - dá sa použiť v úlohe rozličných typov čísiel
napríklad:
ghci> :t sqrt sqrt :: Floating a => a -> a
napríklad pri
sqrt
sa vyhlási konštanta25
zaFloating
, teda rodinu čísiel s desatinnou čiarkou
Referencie
- http://pnyf.inf.elte.hu:8000/Expressions_en.xml#polymorphic-literals-and-constants
- http://www.haskell.org/pipermail/beginners/2010-July/004542.html
- https://stackoverflow.com/questions/19926992/haskell-ghci-why-type-of-1-is-num
Nefungujúca vec
Toto nefunguje:
ghci> let n = 25
n :: Integer
ghci> sqrt n
No instance for (Floating Integer) arising from a use of `sqrt'
Possible fix: add an instance declaration for (Floating Integer)
In the expression: sqrt n
In an equation for `it': it = sqrt n
Je to kvôli odvodzovaniu typov:
ghci> :t sqrt
sqrt :: Floating a => a -> a
Funkcia sqrt
berie číslo z rodiny Floating
a vracia číslo z rodiny Floating
. Dátový typ Integer
(celé číslo) však nepatrí do rodiny čísiel s plávajúcou čiarkou (logicky, nie? a keď nie, tak viď obrázok na http://bit.ly/1gbrnOm) a preto nebudú sedieť dátové typy, odkiaľ prýšti hláška “neexistuje inštancia pre celé číslo s desatinnou čiarkou”.
Alebo tiež
ghci> let f :: Int -> Int; f x = 3.14 * x
No instance for (Fractional Int) arising from the literal `3.14'
Possible fix: add an instance declaration for (Fractional Int)
In the first argument of `(*)', namely `3.14'
In the expression: 3.14 * x
In an equation for `f': f x = 3.14 * x
- toto je blbosť už z matematického zápisu: celé číslo vynásobené desatinným nikdy nedá celé číslo
- v Haskelli:
x
musí byťInt
(celé číslo)- ale neexistuje definované násobenie medzi číslom s pevnou desatinnou čiarkou (
Fractional
) a celým číslom (Int
), preto nesedia typy
Ak opravíme výsledok na:
let f :: Int -> Double; f x = 3.14 * x
Couldn't match expected type `Double' with actual type `Int'
In the second argument of `(*)', namely `x'
In the expression: 3.14 * x
In an equation for `f': f x = 3.14 * x
Funkcia má rozpor: x
je deklarované ako Int
, ale po násobení s konštantou 3.14
, ktorá je z rodiny Fractional
, nevznikne automaticky Double
.
Môžeme to opraviť na:
ghci> let f :: Int -> Double; f x = 3.14 * fromIntegral x
A potom zavolať:
ghci> f 3.14
No instance for (Fractional Int) arising from the literal `3.14'
Possible fix: add an instance declaration for (Fractional Int)
In the first argument of `f', namely `3.14'
In the expression: f 3.14
In an equation for `it': it = f 3.14
Funkcia f
nepodporuje celé čísla: na vstupe čaká Int
, ale dostala 3.14
z rodiny Fractional
, čo nesedí.
Riešenie:
let f :: Double -> Double; f x = 3.14 * x
Iná nefungujúca vec
Vypočítajte priemer známok
Prvý pokus:
ghci> let znamky = [1, 1, 2, 2, 2, 2, 4, 3]
ghci> sum znamky / length znamky
Ale:
Couldn't match expected type `Integer' with actual type `Int'
In the return type of a call of `length'
In the second argument of `(/)', namely `length znamky'
In the expression: sum znamky / length znamky
Lebo:
ghci> :t sum znamky
sum znamky :: Integer
A:
ghci> :t length
length :: [a] -> Int
Inak povedané, chceme deliť Integer
typom Int
, ale to sú nezávislé a odlišné dátové typy.
Skúsme previesť druhý parameter na Integer
. Funkcia fromIntegral
prevedie číslo na “taký dátový typ, ako treba”.
ghci> :t fromIntegral
fromIntegral :: (Integral a, Num b) => a -> b
Skúsme:
sum znamky / fromIntegral (length znamky)
No instance for (Fractional Integer) arising from a use of `/'
Possible fix: add an instance declaration for (Fractional Integer)
In the expression: sum znamky / fromIntegral (length znamky)
In an equation for `it':
it = sum znamky / fromIntegral (length znamky)
Ďalší problém! Operátor delenia /
nefunguje na celé čísla!
Riešenie:
fromIntegral(sum znamky) / (fromIntegral(length znamky))
Skúsme to cez funkciu:
ghci> let priemer znamky = fromIntegral(sum znamky) / fromIntegral(length znamky)
priemer :: (Fractional a, Integral a1) => [a1] -> a
Funkcia má deklaráciu:
- na vstupe zoznam, ktorého prvky sú z rodiny
Integral
- na výstupe hodnota z rodiny
Fractional
Inak povedané, berie celé čísla a vracia číslo v pevnej desatinnej čiarke.
Potom ju radostne voláme:
ghci> priemer znamky
2.125
Ak by sme funkciu deklarovali ako:
ghci> let priemer' znamky = sum znamky / fromIntegral(length znamky)
priemer' :: Fractional a => [a] -> a
Vidíme, že Haskell odvodí deklaráciu “na vstupe je zoznam desatinných čísiel a na výstupe je jedno desatinné číslo”.
Samozrejme, potom toto nefunguje:
ghci> priemer' znamky
No instance for (Fractional Integer) arising from a use of priemer'
Possible fix: add an instance declaration for (Fractional Integer)
In the expression: priemer' znamky
In an equation for `it': it = priemer' znamky
Premenná znamky
je totiž zoznam Integer
ov a nesedia typy.
Vyriešiť to môžeme konverziou zoznamu:
ghci> priemer' $ map (\x -> fromIntegral x) znamky
Alebo redeklaráciu premennej:
ghci> let znamky = [1, 1, 2, 2, 2, 2, 4, 3] :: [Double]
znamky :: [Double]
Bedáky s násobením
ghci> 5 * 5.0
25.0
it :: Double
Ale:
ghci> let pat = 5
pat :: Integer
A:
ghci> let patCelychNula = 5.0
patCelychNula :: Double
A potom:
ghci> pat * patCelychNula
<interactive>:61:7:
Couldn't match expected type `Integer' with actual type `Double'
In the second argument of `(*)', namely `patCelychNula'
In the expression: pat * patCelychNula
In an equation for `it': it = pat * patCelychNula
Chceme násobiť Integer
číslom typu Double
, čo nie je definované. Lebo:
ghci> :t (*)
(*) :: Num a => a -> a -> a
Čísla musia byť rovnakých typov.
Haskell totiž nemá implicitné typové konverzie (coercion) pre čísla. Na rozdiel od Javy, kde 5 * 5.0
(teda int
krát double
) funguje vďaka koercii int
u na double
, musíme prevod urobiť explicitne.
Napríklad:
ghci> fromIntegral(pat) * patCelychNula
25.0
it :: Double
Zdroj: http://www.haskell.org/haskellwiki/Converting_numbers
Veľké programy
-- prog-with-main.hs
main = print "Hello"
Spustíme:
d:\work\haskell>runhaskell prog-with-main
Program využíva I/O akcie (čo sú monády, teda výpočty pozostávajúce z krokov, ktoré majú vedľajšie efekty, t. j. menia stav vonkajšieho sveta).
Zdroje
- https://www.fpcomplete.com/school/starting-with-haskell/basics-of-haskell/the-tao-of-monad
Vypíšte 10krát “Budem si robit DU”
-- domace-ulohy.hs
main = do
mapM (\n -> print "Budem si robit D. U. z Haskellu") [1..10]
- mapovaním vykonáme nad každým prvkom zoznamu funkciu “vytlač”
- funkcia “vytlač” nie je bežná rýdza funkcia, ale I/O akcia
- na mapovanie monád, resp. I/O akcií musíme použiť špeciálnu funkciu
mapM
(M
ako monáda).
Výsledok:
d:\work\haskell>runhaskell prog-with-main
"Budem si robit D. U. z Haskellu"
"Budem si robit D. U. z Haskellu"
"Budem si robit D. U. z Haskellu"
"Budem si robit D. U. z Haskellu"
"Budem si robit D. U. z Haskellu"
"Budem si robit D. U. z Haskellu"
"Budem si robit D. U. z Haskellu"
"Budem si robit D. U. z Haskellu"
"Budem si robit D. U. z Haskellu"
"Budem si robit D. U. z Haskellu"
[(),(),(),(),(),(),(),(),(),()]
GHCI vždy vyhodnotí celú I/O akciu main
(lebo aj vnútro v do
bloku je zložená I/O akcia). Ak chceme ignorovať výsledok, použime radšej mapM_
:
-- domace-ulohy.hs
main = do
mapM_ (\n -> print "Budem si robit D. U. z Haskellu") [1..10]