11.07.2015 Views

Funkce

Funkce

Funkce

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Haskell●●●moderní jazyk (vznik 90.léta) využívajícísoučasný pokrok v oblasti funkcionálníhoprogramováníčistě funkcionálníRysy, které nezapadají do funkcionálníhomodelu (např. sekvenční vstup/výstup) jsoupodporovány pomocí univerzálního (akomplexního) mechanismu tzv. monád a jsoustriktně odděleny od zbytku jazyka.užíván je především pro akademické avýzkumné účely


Základní rysy (oproti Scheme)●běžnější syntaxe, napodobující matematický zápis (rolihraje i odsazení textu, tzv. layout systém)●statický typový systém = typová kontrola již připřekladu. Každý symbol má již v době v době překladupřidělen datový typ (ten je automaticky odvozen, lze hovšak explicitně zpřesnit)●automatické lenivé vyhodnocování: Haskell nenístriktní jazyk a vyhodnocení provádí, až když jepotřeba. Výhody: zjednodušený zápis, vestavěnápodpora neohraničených struktur, někdy i vyššíefektivita. Nevýhody: paměťová náročnost (přísliby),pomalost ve většině algoritmů


Implementace●●●●pro Haskell existuje jen relativně malémnožství překladačů.odkazy na ně lze nalézt nawww.haskell.orgHugs: interpret — vhodný jako vývojovýnástroj (i pro začátečníky). Běh aplikací jevšak extrémně pomalý. Budeme používat.GHC: překladač — vhodný pro produkčníprostředí (relativně rychlý kód)


Operátory a funkce●funkce naopak mají alfanumerické identifikátory apoužívají se prefixově. Pokud jsou však binární, lzepro ně používat i infixový zápis, v němž však musíbýt ve zpětných apostrofech.mod 5 2 (prefixový zápis), 5 `mod` 2 (infixový)●operátory obsahují pouze speciální znaky. Binárníoperátory se používají jako infixové. Pokud jsou všakpoužity samostatně (např. v podobě holé funkce),nebo prefixově (ala Scheme), píší se v závorkách:2 + 3 (infixový zápis), (+) 2 3 (prefixový zápis)


Priorita●●●●při použití operátorů je zohledněna priorita a asociativita(za elegantnější zápisy se bohužel platí větší složitostí).Priority jsou v zásadě stejné jako u dalších jazyků(především u numerických a relačních operátorů).Podobné je i využití závorek. Trochu problémů však můžečinit použití závorek ve volání (prefixových) funkcí:parametry funkcí se neuzavírají do závorek. Závorkyse však mohou vyskytovat jako součást datovýchkonstruktorů (n-tic), a především jsou mnohdy nutné prozajištění správného pořadí vyhodnocení:volání funkce má vyšší prioritu než jakýkoliv operátor


Priorita IIsin 1 + 3 = (sin 1) + 3-- volání funkce má vyšší prioritu než sčítánísin (1 + 3) = sin 4cos (sin 1)-- cos sin 1 typová chyba ∼ (cos sin) 1mod 5 2 + 5= (mod 5 2) + 5 -- doporučený zápis (no není to C)mod 5 (2+5) * 3= (mod 5 (2+5)) * 3 (opět přehlednější)mod(5 2) resp. mod(5,2)-- typová chyba, parametrem nemůže být n-tice


Elementární datové typy●●●●repertoár základních číselných typůInt (32 resp. 64 bitové číslo)Integer (číslo s neomezeným rozsahem)Doubleznakový typ : Char s běžnými literály např. 'a'typ logických hodnot: Bool (hodnoty True, False)n-tice (tuple) = n-tice typovaných hodnot (početa typ hodnot je pevně dán)typ: (Integer, Char), ukázka hodnoty: (58, 'A')typ: (Bool, Bool), ukázka hodnoty (True, False)


Case controlled language●●jazyk Haskell je nejen case-senzitivní (tj. rozeznává videntifikátorech malá a velká písmena), ale je i casecontrolled(tj. velikost písmen má syntaktickou roli)následující pravidla jsou proto syntaktická(nikoliv jen uzuální)– identifikátory funkcí začínají malým písmenem(včetně identifikátorů proměnných = nulárníchfunkcí)– identifikátory typů začínají velkým písmenem (platípro základní i uživatelské typy)– konstruktory nových hodnot začínají velkýmpísmenem (včetně nulárních konstant např. True)


Seznamy●●seznam musí být v Haskellu homogenní (=položkystejného typu), velikost však není omezena (jevšak přirozeně nemodifikovatelný)identifikátor typu: [a], kde a je konkrétní typ[Integer] , příklad hodnoty [2, 5, 8][(Double,Double)] = pole dvojic reálných číselpříklad hodnoty: [(5.2, 3), (-5, 2,6)][[Bool]] = seznam seznamů log. hodnotpříklad hodnoty: [[True, True], [False], []][Char] = seznam znaků = řetězec, alias Stringpříklad hodnot: ['I', 'o'] = "Io" (zkrácený zápis)


Seznamové funkce●seznam je v Haskellu vytvářen vícenásobnouaplikací binární operace (:) [připoj prvek zleva]na seznam, počínaje prázdným seznamem[2, 5] = 2 : 5 : [] = 2 : (5 : [])●●konstrukce je obdobná Scheme, je však omezena jenna konstrukci seznamů (nikoliv tečka-dvojic). Operace(:) je obdobou funkce cons. Je však omezena je naseznamy (druhý operand musí být seznam) a je typověkontrolovaná.většina lispovských algoritmů pro práci se seznamy lzepoužít i pro Haskell (jen syntaxe je jiná)


<strong>Funkce</strong>●●funkce je v Haskellu ještě centrálnější pojem nežve Scheme (a je samozřejmě first-class)definice funkce v Haskellu je intuitivní:log2 x = log x / log 2 -- log je lnhypot a b = sqrt (x^2 + y^2)volání (aplikace funkce):log2 5log2 xvýraz (podvýraz)pozičně odpovídápravé straně definice tzv. vzoru [pattern]⇒ dojde k (lokálnímu) navázání hodnoty 5 na symbol xa vyhodnocení pravé strany definice


Unifikace vzorů (pattern matching)●mechanismus unifikace vzorů (= testování shody mezivoláním a pravou stranou definice funkce a případnénavázání) není omezeno jen na poziční shoduproměnných (dále jednoduchý vzor).hodnotový vzor (příklad operace AND):True && False = FalseTrue && True = TrueFalse && True = FalseFalse && False = False●zde se při volání testuje postupně hodnotová shodamezi skutečnými parametry a jednotlivými definicemi(předpisy) funkce, po nalezení shody je vrácenapříslušná levá strana (nedochází zde k vázaní)


Unifikace vzorů II●a hodnotový vzor lze kombinovatTrue && True = Truex && y = Falsedruhý předpis se uplatí jen v případě, že první není splněn(na pořadí záleží!). Vázané proměnné x, y se přivýpočtu neuplatní, lze je nahradit anonymním vzorem(označena podtržítkem)_ && _ = Falsekaždý výskyt podtržítka symbolizuje jinou (= obecněnezávislou) hodnotu (k vázání nedojde)Běžnou proměnnou lze ve vzoru použít jen jednou!(nelze tedy např. psát x == x = True v definici shodnosti)


Unifikace vzorů III●strukturální vzor– při strukturální vzor je vyžadována shoda ve struktuřesložené hodnoty (obecně v tzv. konstruktoru)– navíc ji lze použít pro navázaní jednotlivých částísložené hodnoty na symboly (tj. lze provést dekompoziciobjektu)length [] = 0 -- hodnotová shoda (má přednost)length (x:xs) = 1 + length xsvzor (x:xs) se shoduje jen s objekty konstruovanýmipomocí operace (:), což jsou jen neprázdné seznamy.Na symbol x se váže první prvek seznamu (head, car), nasymbol xs [iksis] pak zbytkový seznam (tail, cdr)


Unifikace vzorů IV●všechny typy vzorů lze libovolně kombinovat(x:[])= bude se shodovat s jednoprvkovým sznamem (do xse naváže tento jediný prvek)[x]= totéž (jen stručněji)(0, second, _) = bude se shodovat s trojicí (tuple), v nížprvní položka je rovna 0 (druhá položka je navíc pro dalšípoužití navázána na symbol second)((Complex re im) : others) = shoda s (neprázdným)seznamem komplexních číslem (Complex je jejichkonstruktor), reálná část prvního prvku je navázána na re,imaginární na img (zbytek seznamu na others)(c@(Complex re im) : others) = totéž + vazba celéhoprvného prvku (kompl. čísla) na symbol c (tzv. as-pattern).


Unifikace vzorů v jiných jazycích●●unifikace (strukturálních) vzorů je relativněexotickým mechanismem. Existuje jen vněkterých dalších funkcionálně zaměřenýchjazycích (např. ML, Caml, Mathematica).ještě obecnější unifikaci nabízí logický jazykProlog (unifikace vzorů je podmnožinouprologovské unifikace)


Strážné podmínky●hodnotové vzory umožní testovat (a takodlišovat) jen jednoduchou shodu (rovnost).U složitějších podmínek je nutno použít tzv.strážné podmínky [guards]signum x | x < 0 = -1| x == 0 = 0| otherwise = 1filtr _ [] = []filtr predikat (x:xs)| predikat x = x : filtr xs| otherwise = filtr xs


Klauzule Where●pro celý předpis lze zavést (společné) lokálníproměnné pomocí klauzule wherequadratic 0 0 c = []quadratic 0 b c = [-c / b]quadratic a b c| d < 0 = []| d == 0 = [-b / da]| otherwise = [-b + d / da, -b - d / da]where d = sqrt(b^2 - 4*a*c)da = 2 * a


Typování funkcí●●●funkce stejně jako jiné hodnoty mají staticky určený typ.vyvozený datový typ (infer data type)tento datový typ je vydedukován překladačem přímoz definice funkce. Je to nejobecnější přípustný typ.Lze získat dotazem :t identifikátor-funkceexplicitní (uživatelský) datový typdeklarován uživatelem. Může být roven vyvozenémudatovému typu nebo být striktnější (tj. explicitní typ můžebýt podmnožinou vyvozeného). V opačném případě je připřekladu hlášena chyba.Doporučuji typ explicitně uvádět (pro účely ladění,dokumentace, kontraktu).


<strong>Funkce</strong> jednoho parametru●funkce signum, explicitní signatura (uváděna předdefinicí funkce)signum :: Integer -> Integeridentifikátor funkce typ parametru typ návratové hodnotyjsou však možné i další typy: Int -> Int (striktnější),Double -> Doublevyvozený (a nejobecnější) typ je však:signum :: (Num a, Ord a, Num b) => a -> blibovolný typ v typové tříděNum(ber) a Ord(erable),označený dále jako alibovolný (obecně jiný)číselný (Num) typoznačený jako btransformacehodnoty typu a na b


Polymorfismus●●●pro typový systém Haskellu je typická podporapolymorfismu, tj. schopnost funkce pracovat sargumenty různých typů. Polymorfismus je statický (tj.podporovaný a kontrolovaný jíž během překladu)polymorfismus se navenek projevuje využitímtypových proměnných ve specifikacích typů (označujílibovolný typ z určité množiny typů). Typové proměnnézačínají malým písmenem, a volba identifikátoru nemážádný sémantický význam (nelze však za stejnouproměnnou substituovat dva různé typy)pro typové proměnné se běžně používají obecnéidentifikátory a, b, c, ... Někdy jsou však vhodnějšíidentifikátory popisné (např. number, apod)


Typové třídy●●●množiny typů bývají běžně omezené pomocíspecifikace typových tříd, které definují požadovanérozhraní hodnot jednotlivých typů.typové třídy nejsou OOP třídami (jejich instancemi jsoutypy ne hodnoty). Odpovídají spíše javovským a C#rozhraním (interface). Např. typová třída Ord odpovídározhraní IComparable. Nejsou ale jmennými prostory.některé funkce lze použít nad libovolným typem:identita :: a -> apodobně lze mnohé seznamové funkce použít proseznamy s libovolnými typy položek:length :: [a] -> Int


<strong>Funkce</strong> s více parametry● funkce s více parametry jsou interpretovány (aformálně označovány) jako funkce s jednímparametrem pomocí curryinguindex :: Int -> ([a] -> a) -- indexace (n-tý prvek)díky prioritě lze zkrátit na index :: Int -> [a] -> afunkce přijímá číslo (index) a vrací novou funkci(v níž je již fixován index). Tato funkce má opětjeden parametr (seznam) a vrací položku s danýmfixním indexem.Currying není jen formální, funkci lze skutečně volatjen s jedním parametrem (výsledkem je ale funkce!)head = index 1 (funkce head je rovna funkci indexpro fixní index 1)


Binární operátory●binární operátory jsou jen speciálním případem funkcís více parametry (včetně plné podpory curryingu)(/) :: Double -> (Double -> Double)obecný typ je: (Fractional a) => a -> a -> aBinární operátor lze volat jen s jedním parametrem(operandem) lze si navíc vybrat, který operand budevynechán (= zůstane volný)map (/2) [6, 8] = [3.0, 4.0] -- mapována funkce polovina (x/2)map (10/) [2, 5] = [5.0, 2.0] -- mapována funkce 10/x( a -> a -> afiltr (


Funkcionály (a jejich typy)●mapmap :: (a -> b) -> [a] -> [b]●zipWith (pouhé zip, spojuje prvky do n-tice)zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]●foldlfoldl :: (a -> b -> a) -> a -> [b] -> a●foldrfoldr :: (a -> b -> b) -> b -> [a] -> b


Lambda výraz●lambda výraz existuje i v Haskellu, je všakméně používána (není nutná při definici, ačasto ani pro vytváření anonymních funkcí, kdelze často použít curryingu)● \parametry -> tělo funkce (znak \ je podobný λ)●příklady:\x y -> sqrt(x + y)foldl (\s,i -> i:s) [] ≈ reverse


Generátory funkcí●●stejně jako ve Scheme lze vytvořit funkce, kterévytvářejí a vracejí nově vytvořené funkce (resp.uzávěry)pro vytvoření uzávěru lze použít λ-výraz, alečastěji se využívá definice funkce v sekci where.dekorator :: [a] -> ([a] -> [a])dekorator dekor = fwhere f s = dekor ++ s ++ dekorlépe ale přes skládání funkcí (operátor .)dekorator dekor = (dekor ++) . (++ dekor)


Generátory funkcí II●●generátory funkcí mohou být i sofistikovanějšígenerování funkce pro n-násobnou aplikaci zadané funkce(jako operátor .*)(.*) :: Integer -> (a->a) -> (a->a)n .* f = nf nwhere nf 1 = fnf i = f . (nf (i-1))příklad volání: (3 .* (+1)) 3●inverzní funkce (pomocí nestd. hledání kořene) [omezení!]problémy s nejednoznačností operace minusinv f = gwhere g y = findRoot ((+ (-y)) . f)


Seznamová komprehenze●seznamová komprehenze je syntaktickázkratka pro vytváření seznamů, pomocí operacífiltr a map.●základní tvar: [f x | x


Seznamová komprehenze II●zdroje komprehenze nejsou omezeny na jedinýseznam. Pokud je uvedeno více zdrojů pak jemapování (a filtr) volán na kartézský součinzdrojů.[(x,y) | x


Neohraničené seznamy●díky lazy charakteru jazyka (všude se používajíimplicitně přísliby) je vytváření neohraničených seznamůa práce s nimi snadná.nrange n = n : integers (n + 1)lze psát i jako [n..]integers = nrange 1evenIntegers = [x | x


Neohraničené seznamy II●neohraničené seznamy ohraničených (konečných)položektwinpair:: [Integer] -> [[Integer]]twinpair (x1:x2:xs) | x2 - x1 == 2 = [x1,x2] : rest| otherwise = restwhere rest = twinpair (x2:xs) --neefetivnítwinprimes = twinpair primes → [[3,5],[5,7],[11,13],[17,19], ...●neohraničené seznamy neohraničených seznamů(obecněji struktur)id = [(replicate i 0) ++ [1] ++ repeat 0 | i


Neohraničené seznamy III●●●seznamy neohraničené ve více dimenzích vyžadujíopatrnost při použití (v ohraničeném čase)výpis výřezu: take 10 (map (take 10) id)relativně snadno se však pomocí funkcionálu zipWithkombinují s ohraničenými seznamy (zipWith iterujevždy jen před kratší seznam)hp matrix = zipWith (zipWith (*))[(replicate i 0) ++ [1] ++ repeat 1 | i


Neohraničené seznamy v C#●●neohraničené seznamy lze využívat i v Linq (C#)(v podobě nekonečných generátorů). Jen je třeba něcodoprogramovat (Linq standardně nenabízí žádný)výběr náhodné podmnožiny ze seznamu za použitíproudu (=nekonečného generátoru) náhodných čísel(zde je zdrojem pole jmen)jmena.Zip(RandomBools(), (j,s) => new {Jmeno = j, Stesti = s}).Where(d => d.Stesti).Select(d => d.Jmeno)


Algebraické datové typy I●●●Haskell nabízí i prostředky pro tvorbu uživatelskýchdatových typů: algebraické typyalgebraický datový typ lze obecně charakterizovat jakosjednocení uspořádaných dvojic (K,M), kde K jejedinečný identifikátor (označovaný jako konstruktor)a M je prvek z kartézského součinu libovolného počtutypů (včetně nulového, kdy M=Ø)speciálním případem algebraických typů jsou výčtovétypy (pro něž právě M=Ø)●data Result = Success | Failuredefinice nového typu je sjednocení (|) dvou konstruktorů(identifikátorů)


Algebraické typy II●●Konstruktorů výčtového typu lze využít v běžnémvýrazu pro konstrukci hodnot nového typu (jejichrepresentace je irelevantní, hodnota získané pomocíjednotlivých konstruktorů jsou různé). Konstruktory sechovají jako nulární funkce.Lze je dále použít ve vzoru (pattern), kdy si vynucujíshodu se stejným konstruktorem (bez vazby)●resultToBool :: Result -> BoolresultToBool Success = TrueresultToBool Failure = Falsevýčtového typu jsou i standardní typy jako Bool(zřejmé), Int (dlouhý výčet, nestandardní konstruktor).


Algebraické typy III●●jednoduché kartézské typy (s jediným konstruktorem)příklad definice: data PointT = Point Double Double– množina hodnot MH(PointF) ={(Point, p), p je prvkem MH(Double) x MH(Double)}– množinu lze interpretovat jako body ve 2D prostoru– hodnoty lze vytvářet zápisem Point x y (kde x,y jsou výrazy typuDouble), z důvodů priority však běžně v podobě (Point x y),identifikátor konstruktoru zde označuje binární funkci– zápis v podobě (Point x y) lze použít i jako vzor (zde jsou všakx,y podvzory tj. volné symboly resp. anonymní vzor)– konstruktor může mít stejné jméno jako typ (nutno však vždyrozlišovat !!), což se hojně používá:data Point = Point Double Double


Algebraické typy IV●příklad použití typu kartézského typu Point:vzdalenost :: Point -> Point -> Doublevzdalenost (Point x1 y1) (Point x2 y2) = sqrt((x1-x2)^2+ (y1-y2)^2)x_coord :: Point -> Doublex_coord (Point x _) = x -- jednoduchý getterzipWith (Point) [2.0, 3.0] [1.0, 5,0] -- Point je zde bin. fce(Point 2.0 6) -- nelze zatím zobrazitvzdalenost (Point 2.0 3.0) (Point 4.0 -3.6)


Algebraické typy V●příklady dalších jednoduchých kartézských typůdata Node = Node String ≈ uzel grafu s řetězcovým id.data Edge = Node :-> Node ≈ hrana, operátorová formakonstruktoru (musí začínat znakem ':')data State = State String ≈ stav DFAdate InputSymbol = S0 | S1 ≈ vstupní symbol DFA (výčet,konstruktory musí být jedinečné v rámci modulu)data DFA = DFA [State] State [State ->Char->State] [State]≈ determ. konečný automat = seznam stavů, poč. stav,seznam přechodových funkcí, seznam koncových stavů(množina vstup. symbolů je dána výčtem InputSymbol)


Algebraické typy VI●obecné algebraické typy se definují jako sjednoceníněkolika n-árních konstruktorůdata Source = Url String | Book String String | Unknownukázky hodnot typu SourceUrl "http://haskell.org", Url "http://plt-scheme.org"Book "J.R.R.Tolkien" "Pán prstenů", Unknowndůležité: množiny hodnot generované jednotlivýmikonstruktory jsou disjunktní tj. například nulárníkonstruktor nemůže symbolizovat vybraný prvekz množiny jiného konstruktoru.data Point = Point Double, Double | Origin -- origin ≠ Point 0.0 0.0


Binární strom●●algebraické typy mohou být i rekurzivními typickýmpříkladem jsou stromybinární strom nesoucí celá čísla●data Tree = Node Integer Tree Tree | Emptypro testovací účely je dobré, pokud je strom převoditelnýna řetězec (a tudíž jej lze vypsat). To lze nejjednodušejizajistit doplnění následující klauzule k definici typu:deriving Showtyp se tak automaticky zařadí do třídy Show(=převoditelné na řetězce) a je automaticky vytvořenaelementární metoda show (zobrazuje strukturukonstruktorů)


Binární strom II●konstrukce stromu(Node 5 (Node 3 (Node 2 (Node 1 Empty Empty) Empty)(Node 4 Empty Empty)) (Node 8 Empty Empty))5ukázka elementárních funkcí23 84EE1 E E EtreeLength :: Tree -> IntegerE EtreeLength Empty = 0treeLength (Node _ left right) = 1 + treeLength left+ treeLength righttreeEq :: Tree -> Tree -> BooltreeEq Empty Empty = TruetreeEq (Node v1 l1 r1) (Node v2 l2 r2) = v1 == v2 &&(l1 `treeEq` l2) && (r1 `treeEq` r2)treeEq _ _ = False


Binární stromy III●binární stromy celých čísel mohou být vytvářeny aspravovány jako uspořádanéinsert :: Integer -> Tree -> Tree -- vložení do uspoř. stromuinsert n Empty = Node n Empty Emptyinsert n (Node m left right) | n < m = Node m (insert n left) right| otherwise = Node m left (insert n right)tuto funkci lze použít pro generování usp. stromů ze seznamlistToTree = foldr insert Emptyopačný směr je komplikovanější, elementární rekurzivníimplementace je snadná ale neefektivní:treeToList1 :: Tree -> [Integer]treeToList1 Empty = []treeToList1 (Node n left right) = treeToList left ++ [n] ++treeToList right


Binární stromy IV●●neefektivita spočívá v častém vytváření pomocných polí(při spojování seznamů) a ani spojování není přílišefektivní (složitost O(n)!), kde n je velikost prvníhoseznamu). Situace je podobná spojování řetězců pomocíoperátoru '+' v C# nebo Javě.existuje však efektivnější implementace, kdy se využívájen (rychlé) operace cons (:). Nejdříve pomocná funkce.treeToList' :: Tree -> [Integer] -> [Integer]treeToList' Empty = id -- id je identitatreeToList' (Node n left right) = ((treeToList' left) . (n:) .(treeToList' right))treeToList :: Tree -> [Integer]treeToList = (flip treeToList') [] -- flip obrací pořadí parametrůa odměna nakonec: tbsort = treeToList . listToTree


Generické typy●●●●kromě základních algebraických typů podporujeHaskell i generické (polymorfní) typygenerické typy jsou parametrizovány typovýmiparametry (překladač pak může automaticky vytvářettypy substituováním typových parametrů)mezi generické typy patří i haskellovské seznamy(typové označení [a], kde a je typový paremetrproměnná)vhodným kandidátem je i náš typ binárních stromů(kde generický parametr určuje typ položek)data Tree a = Node a (Tree a) (Tree a) | Empty


Generické typy II●●●●takto definovaný typ specifikuje, že položky mohou býtlibovolného typu (formálně označeného a)to je však dostatečné pouze pro funkce, kterénepracují přímo s položkami, ale je se strukturouseznamu: např. treeLength.u funkce treeEq se však položky porovnávají což,neumožňují všechny typy (např. nelze testovatshodu funkcí). U funkcí pracující s uspořádanýmstromem, musí být navíc položky porovnatelné, tj.musí pro ně existovat uspořádání).řešením je omezit generický typ na typy určité třídynapř. Eq (equality, shoda), resp. Ord (Ordering,uspořádání)


Generické typy III●požadován relace rovnosti položek:data Eq a => Tree a = Node a (Tree a) (Tree a) | Empty●požadována relace uspořání mezi položkami (a tím irovnosti, třída Ord a třídu Eq implikuje resp. jepodtřídou)data Ord a => Tree a = Node a (Tree a) (Tree a) | EmptySpecifikace omezení generických parametrů (omezenégenerické typy) jsou povinné (jinak se nepřeložínapř. porovnání dvou položek)Omezené generické typy se vyskytují i v některých modernějšíchOOP jazycích (počínaje jazykem Eiffel, později i v Javě a C#)Srovnej např v C#: class Tree where T : IComparable


Třídy typů●●●třídy typů (type classes) jsou důležitou součastítypového systému Haskellu.třída typů soustřeďuje typy, které sdílí jistoupodmnožinu operací-funkcí (tj. typy lze je označitjako instance jistého abstraktního typu). Proto sepodobá abstrakci interface jazyka Java (a C#). Typovétřídy jsou však plně statický mechanismus (kontrolujíse za překladu nikoliv až za běhu programu).mezi základní třídy typů patří: Eq, Ord, Show (již jsmena ně narazili), Enum (ordinální typy, s operacemi succa pred), Read (existuje parser pro převod z řetězcovérepresentace), Num (všechna čísla)


Instance třídy typů I●●●každý uživatelský typ se může stát instancí libovolnéhopočtu tříd typůtypy nejsou jmennými prostory pro funkce a tak je jezařazení typu do třídy typů jediným prostředkem jaksdílet identifikátory funkcí (např. operace + jedefinována pouze pro typy třídy Num)typ který je instancí třídy, podporuje všechny funkce(operace) dané třídy, pro některé funkce jsou ve tříděimplicitní implementace (s využitím ostatních funkcítřídy). Tyto funkce proto nemusí být daným typemimplementovány (ale samozřejmě mohou).


Instance třídy typů II●●●typová třída Eq definuje dvě operace (metody):class Eq a where(==), (/=) :: a -> a -> Boolx /= y = not (x == y)x == y = not (x /= y)obě operace mají implicitní implementace, ty jsou všakvzájemně závislé a tak je v instančním typu nutno definovatalespoň jednu.odvozená typová třída Ord dědí operace ze třídy Eq apřídává další (definovat je nutno compare nebo Ord a wherecompare :: a -> a -> Ordering --výčet EQ.LT,GT(=) :: a -> a -> Boolmax, min :: a -> a -> a


Instance třídy typů III●předpokládejme následující typ pro rozšířená přirozenáčísla (N + nekonečno)data ExtInt = ExtInt Integer | Infinity●pro ně definujeme (mimo jiné) operace pro testovánírovnosti a porovnání (nelze pro ně použít prozatím běžnéidentifikátory == a compare)eIntEq :: ExtInt -> ExtInt -> Bool(ExtInt n) `eIntEq` (ExtInt m) = m == nInfinity `eIntEq` Infinity = error "undefined"_ `eIntEq` _ = FalseeIntCompare :: ExtInt -> ExtInt -> Ordering(ExtInt n) `eIntCompare` (ExtInt m) = compare n mInfinity `eIntCompare` Infinity = error "undefined"Infinity `eIntCompare` (ExtInt _) = GT(ExtInt _) `eIntCompare` Infinity = LT


Instance třídy typů IV●identifikátory eIntEq a eIntCompare jsou dostinepohodlné.Zařazením třídy ExtInt do tříd Eq a Ord však získámemožnost použití standardních operátorů (a navíc idalších operátorů resp. funkcí, např. max, /=, >,apod.)instance Eq ExtInt wherex == y = x `eIntEq` yinstance Ord ExtInt wherecompare x y = x `eIntCompare` ytyp ExtInt by bylo lze zařadit i do dalších tříd např. Num(větší počet nutných operací), Show (lze automatickypomocí deriving), Read, apod.


Instancování polymorfních typů●●členy tříd mohou být i polymorfní typy (resp. přesnějivšechny jejich specializace)instanciace je stejná jako u nepolymorfních typůinstance (Eq a) => Eq (Tree a) where(==) = treeEq●Haskell však podporuje i třídy, jejichž operace pracujínad několika polymorfními typy se stejnou aritou(tj. počtem generických parametrů)class Functor f wherefmap:: (a -> b) -> f a -> f bf symbolizuje generický typ aplikovaný na typovýparametr (vytváří se tím vlastně metatypový systém)


Monády I●jako monády jsou označovány algebraické konstrukcekonstrukce tvořené polymorfním typem M a dvojicífunkcí nad tímto typem:●class Monad m wherereturn :: a -> m a(>>=) :: m a -> (a -> m b) -> m b -- tzv. bindnavíc tyto funkce musí splňovat jisté podmínky (viz např.http://en.wikibooks.org/wiki/Haskell/Understanding_monads).Existuje větší polymorfních typů polymorfních typů pro něžlze definovat monády, od jednoduchých (např. seznam), pomírně složité (např. s -> (a,s)), až po velmi složité.


Monády II●●●hlavní výhodou monád, je že umožňuje uvnitřfunkcionálního jazyka vybudovat mechanismuspostupného vyhodnocování a změny stavů (a tonejen lineární)monády tak nabízejí uvnitř Haskellu přesně definovanéa ohraničené minijazyky s vlastním mechanismemtoku dat.nejdůležitější (a standardně podporovanou) je tzv.IO monáda, nabízející prostředí sekvenčním IOprogramům. Navíc jazyk nabízí i speciální syntaxi,která její využití podporuje (není nutné znát abstraktnípopis monády jen několik mechanických pravidel)


IO monáda●jednou ze základních I/O funkcí je getChargetChar :: IO Char●funkce vrací typ IO Char, což je typ Char uzavřený doobálky monády. V této obálce je použitelný pouzev kontextu daného minijazyka.● následující fragment funkce je tedy syntakticky (isémanticky) nepřípustny:getLine | znak == '\n' = znak : []| otherwise = znak : getLinewhere znak = getChar●syntakticky (formálně) je problém jednoduchý, znaknení typu Char (ale IO Char)


IO monáda II●●●sémantickou (skutečnou) příčinou je nejistota v jakémpořadí se volají jednotlivá getChar (od konce, odpočátku náhodně)Haskell nezaručuje žádné pořadí, neboť pro čistéfunkce (= bez postranního efektu) je pořadí aktivaceirelevantní (funkce getChar by byla konstantou)funkce pracující s IO monádami proto musí být použitypouze v kontextu metod return a především bind ,resp. v prostředích, které je využívají. Nejpohodlnějšíje prostředí do, které simuluje imperativní zápis (nevšak zcela dokonale)


Prostředí do●program pro čtení řádky v prostředí do má téměřimperativní tvar (užívá však rekurze)●getLine :: IO StringgetLine = do c


Práce se soubory●v I/O subsystému je zahrnuta i jednoduchá práce sesoubory (otevřený soubor je v Haskellu označován jakohandler)main = do fromHandle

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!