Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005
Tällä luennolla Algebralliset tietotyypit Hahmonsovitus (pattern matching) Primitiivirekursio Esimerkkinä binäärinen hakupuu
Muistattehan... Listatyyppi [a] [1,2,3,4] 1 : (2 : (3 : 4 : [])) eli 1 : 2 : 3 : 4 : [] Hahmonsovitus case foo of [] > Tyhjä _:_ > Ei tyhjä Nämä ovat erikoistapauksia
Algebralliset tietotyypit Haskellin tyyppijärjestelmän kulmakivi Teoriassa kaikki edellisellä luennolla esitetyt perustyypit ovat myös algebrallisia tyyppejä Kaksi keskeistä käsitettä: tyyppikoostin datakoostin
Algebrallisia tietotyyppejä: Bool tyyppikoostin datakoostimet data Bool = True False deriving (Read, Show, Eq, Ord, Enum, Bounded) avainsana tyyppiluokat, joihin tyyppi Bool kuuluu
Algebrallisia tietotyyppejä: Maybe tyyppikoostin datakoostimet tyyppimuuttuja data Maybe a = Nothing Just a deriving (Read, Show, Eq, Ord) avainsana tyyppiluokat, joihin tyyppi Maybe a kuuluu
Tyyppikoostin Tyypin nimi Kirjoitetaan isolla kirjaimella: Bool, Maybe Voi ottaa tyyppiparametreja (esim. Maybe a) tällöin kyseessä on geneerinen tyyppi tyyppimuuttuja voidaan korvata tyypillä: Maybe String (a = String) Maybe (Maybe Int) (a = Maybe Int)...
Datakoostin Esim. True, False, Just 42, Nothing Kirjoitetaan isolla kirjaimella Jos ei ota parametreja, on tyyppinsä vakio True, False, Nothing Jos ottaa parametreja, on funktio, joka luo tyyppinsä arvoja Just :: a > Maybe a
data... Algebrallisen tyypin määrittely on sen kuvaus, minkälaisia ko. tyypin lausekkeet ovat, kun niistä on ensin poistettu kaikki (ei koostin )funktiot Maybe Bool tyypin kaikki arvot ovat Nothing Just False Just True
Operatiivinen malli Koodissa Just 42 Muistissa Just 42 Nothing Nothing Ei koko totuus, mutta hyvä osatotuus
Listatyyppi ei ole erikoistapaus... paitsi syntaksiltaan data List a = Cons a (List a) Nil List Bool tyypin arvoja: Nil Cons False Nil Cons True Nil Cons False (Cons False Nil) Cons False (Cons True Nil)...
Listatyyppi ei ole erikoistapaus... paitsi syntaksiltaan data List a = Cons a (List a) Nil List Bool tyypin arvoja: Nil Cons False Nil Cons True Nil Cons False (Cons False Nil) Cons False (Cons True Nil)... data [a] = a : [a] [] (ei laillista Haskellia, mutta idea on oikein)
Listan operatiivinen malli [1,2,3,4] eli 1 : (2 : (3 : (4 : []))) (:) 1 (:) 2 (:) 3 (:) 4 []
Binäärinen hakupuu data BinTree k d = BTEmpty BTNode (BinTree k d) k d (BinTree k d) BTNode (BTNode BTEmpty Kissa 1 BTEmpty) Koira 42 (BTNode BTEmpty Tiikeri 33 BTEmpty) BTNode Koira 42 BTNode Kissa 1 BTNode Tiikeri 33 BTEmpty BTEmpty BTEmpty BTEmpty
Hahmonsovitus 1 Hahmonsovitus on se tapa, jolla pohjimmiltaan päästään käsiksi algebrallisen tietotyypin parametrisoidun koostimen luoman arvon sisälle Hahmonsovitus tapahtuu case lausekkeessa case lauseke of hahmo ehto > lauseke ehto > lauseke... where määrittely... hahmo ehto > lauseke...
Hahmonsovitus 2 case lauseke of hahmo ehto > lauseke ehto > lauseke... where määrittely... hahmo ehto > lauseke... case avainsanaa seuraava lauseke on se, jota ollaan nyt tutkimassa ehdot ovat tavallisia Bool tyyppisiä lausekkeita
Hahmonsovitus 3 case lauseke of hahmo ehto > lauseke ehto > lauseke... where määrittely... hahmo ehto > lauseke... nuolta seuraava lauseke on casen tulos, jos sitä vastaava hahmo sopii lausekeeseen ja sitä vastaava ehto on tosi hahmoja ja ehtoja tarkistetaan ylhäältä alaspäin
Hahmoja ovat... (1) muuttujat muuttuja sopii mihin tahansa ko. muuttujan näkyvyysalueeseen kuuluvat hahmoon liittyvät ehdot ja where osuus ehtoihin liittyvät lausekkeet avainsana _ (alaviiva) kuten muuttuja, mutta ei voi käyttää muuttujana ehdoissa tai lausekkeissa
Hahmoja ovat... (2) Datakoostimen käyttö parametrien paikalla hahmoja hahmo sopii, jos tutkittava arvo on luotu ko. datakoostimella ja parametrihahmot sopivat esim. Just x yleensä laitettava sulkeisiin Ns. @ hahmo: muuttuja@hahmo muuttuja saa arvokseen hahmoon sovitettavan arvon lisäksi ko. arvoa sovitetaan hahmoon
Hahmoja ovat... (3) Laiska hahmo: ~hahmo sovitus jätetään myöhemmälle, oletetaan että onnistuu jos hahmossa käytettyjä muuttujia yrittää käyttää, eikä hahmo sovikaan, on tuloksena virhe (vrt. nollalla jako) harvoin tarvitaan Numero ym. vakiot sopii, jos tutkittava arvo on sama kuin ko. vakion arvo
Hahmonsovitus 4 case Left (Just 42) of Right _ > oikea arvo Left Nothing > vasen ei mikään Left (Just x) x < 0 > vasen nega otherwise > vasen pos Mikä on tuon lausekkeen tulos?
Kielioppimakeinen f hahmo1 hahmo2 testi1 = lauseke1 testi2 = lauseke2 where määritelmä f hahmo3 hahmo4 = lauseke3 on suunnilleen sama kuin f x y = case (x,y) of (hahmo1, hahmo2) testi1 > lauseke1 testi2 > lauseke2 where määritelmä (hahmo3, hahmo4) > lauseke3
Primitiivirekursio Useimmat algebrallisia tietotyyppejä koskevat operaatiot ovat primitiivirekursiivisia Primitiivirekursiivisen funktion kaava: f :: DataType > joku tyyppi f CA = jokin vakio f CB = jokin vakio f (CC x) =... f x... f (CD x y) =... f x... f y... tässä DataType on määritelty näin: data DataType = CA CB CC joku CD toi nen
Muistathan: binäärinen hakupuu data BinTree k d = BTEmpty BTNode (BinTree k d) k d (BinTree k d) BTNode (BTNode BTEmpty Kissa 1 BTEmpty) Koira 42 (BTNode BTEmpty Tiikeri 33 BTEmpty) BTNode Koira 42 BTNode Kissa 1 BTNode Tiikeri 33 BTEmpty BTEmpty BTEmpty BTEmpty
Haku binäärisestä puusta data BinTree k d = BTEmpty BTNode (BinTree k d) k d (BinTree k d) lookup :: Ord k => k > BinTree k d > Maybe d lookup key tree = loop tree where loop BTEmpty = Nothing loop (BTNode left key' datum right) key < key' = loop left key > key' = loop right otherwise = Just datum loop on primitiivirekursiivinen
Haku binääripuusta: Esimerkki (1) lookup :: Ord k => k > BinTree k d > Maybe d lookup key tree = loop tree where loop BTEmpty = Nothing loop (BTNode left key' datum right) key < key' = loop left key > key' = loop right otherwise = Just datum lookup Tiikeri (BTNode (BTNode BTEmpty Kissa 1 BTEmpty) Koira 42 (BTNode BTEmpty Tiikeri 33 BTEmpty))
Haku binääripuusta: Esimerkki (2) lookup :: Ord k => k > BinTree k d > Maybe d lookup key tree = loop tree where loop BTEmpty = Nothing loop (BTNode left key' datum right) key < key' = loop left key > key' = loop right otherwise = Just datum let key = Tiikeri in loop (BTNode (BTNode BTEmpty Kissa 1 BTEmpty) Koira 42 (BTNode BTEmpty Tiikeri 33 BTEmpty))
Haku binääripuusta: Esimerkki (3) lookup :: Ord k => k > BinTree k d > Maybe d lookup key tree = loop tree where loop BTEmpty = Nothing loop (BTNode left key' datum right) key' < key = loop left key' > key = loop right otherwise = Just datum let key = Tiikeri in case BTNode (BTNode BTEmpty Kissa 1 BTEmpty) Koira 42 (BTNode BTEmpty Tiikeri 33 BTEmpty) of BTEmpty > Nothing BTNode left key' datum right key < key' = loop left key > key' = loop right otherwise = Just datum
Haku binääripuusta: Esimerkki (4) lookup :: Ord k => k > BinTree k d > Maybe d lookup key tree = loop tree where loop BTEmpty = Nothing loop (BTNode left key' datum right) key' < key = loop left key' > key = loop right otherwise = Just datum let key = Tiikeri in case BTNode (BTNode BTEmpty Kissa 1 BTEmpty) Koira 42 (BTNode BTEmpty Tiikeri 33 BTEmpty) of BTEmpty > Nothing BTNode left key' datum right Tiikeri < key' = loop left Tiikeri > key' = loop right otherwise = Just datum
Haku binääripuusta: Esimerkki (5) lookup :: Ord k => k > BinTree k d > Maybe d lookup key tree = loop tree where loop BTEmpty = Nothing loop (BTNode left key' datum right) key' < key = loop left key' > key = loop right otherwise = Just datum let key = Tiikeri in loop (BTNode BTEmpty Tiikeri 33 BTEmpty)
Haku binääripuusta: Esimerkki (6) lookup :: Ord k => k > BinTree k d > Maybe d lookup key tree = loop tree where loop BTEmpty = Nothing loop (BTNode left key' datum right) key' < key = loop left key' > key = loop right otherwise = Just datum let key = Tiikeri in case BTNode BTEmpty Tiikeri 33 BTEmpty of BTEmpty > Nothing BTNode left key' datum right key' < key = loop left key' > key = loop right otherwise = Just datum
Haku binääripuusta: Esimerkki (7) lookup :: Ord k => k > BinTree k d > Maybe d lookup key tree = loop tree where loop BTEmpty = Nothing loop (BTNode left key' datum right) key' < key = loop left key' > key = loop right otherwise = Just datum case BTNode BTEmpty Tiikeri 33 BTEmpty of BTEmpty > Nothing BTNode left key' datum right key' < Tiikeri = loop left key' > Tiikeri = loop right otherwise = Just datum ==> Tulos: Just 33
Primitiivirekursiosta vielä (1) Määritellään, että lausekkeen paino on siinä esiintyvien konstruktoreiden lukumäärä kunhan ensin on avattu kaikki määritelmät, niin että lausekkeessa ei esiinny määriteltyjä funktioita tai vakioita Yksiparametrinen, paloittain määritelty rekursiivinen funktio on primitiivirekursiivinen, jos funktion parametrin paino on kussakin palassa suurempi kuin ko. palan määrittelevässä lausekkessa esiintyvien rekursiivisten kutsujen argumenttien yhteenlaskettu paino
Primitiivirekursiosta vielä (2) lookup :: Ord k => k > BinTree k d > Maybe d lookup key tree = loop tree where loop BTEmpty = Nothing loop (BTNode left key' datum right) key' < key = loop left key' > key = loop right otherwise = Just datum Parametrin paino on 1 Argumentin paino on 0 ==> loop on primitiivirekursiivinen
Primitiivirekursiosta vielä (3) Primitiivirekursiivinen laskenta päättyy aina On olemassa ongelmia, joita ei voi ratkaista primitiivirekursiolla silloin käytetään yleistä rekursiota Mutta: on hyvä aina yrittää tehdä ensin primitiivirekursiivinen ratkaisu ennen kuin yrittää yleistä rekursiota Primitiivirekursiota sanotaan myös rakennerekursioksi (structural recursion)