5.5 Jäsenninkombinaattoreista

Samankaltaiset tiedostot
5.3 Laskimen muunnelmia 5.3. LASKIMEN MUUNNELMIA 57

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Jäsennys. TIEA341 Funktio ohjelmointi 1 Syksy 2005

tään painetussa ja käsin kirjoitetussa materiaalissa usein pienillä kreikkalaisilla

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Monadeja siellä, monadeja täällä... monadeja kaikkialla? TIES341 Funktio ohjelmointi 2 Kevät 2006

Laajennetaan vielä Ydin-Haskellia ymmärtämään vakiomäärittelyt. Määrittely on muotoa

Tyyppiluokat II konstruktoriluokat, funktionaaliset riippuvuudet. TIES341 Funktio-ohjelmointi 2 Kevät 2006

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Tyyppejä ja vähän muutakin. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Esimerkki: Laskin (alkua) TIEA341 Funktio ohjelmointi 1 Syksy 2005

Taas laskin. TIES341 Funktio ohjelmointi 2 Kevät 2006

Uusi näkökulma. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Luku 3. Listankäsittelyä. 3.1 Listat

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Kertausta 1. kurssikokeeseen

Demo 7 ( ) Antti-Juhani Kaijanaho. 9. joulukuuta 2005

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Säännölliset kielet. Sisällys. Säännölliset kielet. Säännölliset operaattorit. Säännölliset kielet

Ydin-Haskell Tiivismoniste

TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho. 19. tammikuuta 2012

TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho. 31. maaliskuuta 2011

ICS-C2000 Tietojenkäsittelyteoria. Tähän mennessä: säännölliset kielet. Säännöllisten kielten pumppauslemma M :=

TIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 10. kesäkuuta 2013

ICS-C2000 Tietojenkäsittelyteoria

Geneeriset tyypit. TIES542 Ohjelmointikielten periaatteet, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos

ICS-C2000 Tietojenkäsittelyteoria Kevät 2016

Haskell ohjelmointikielen tyyppijärjestelmä

jäsennyksestä TIEA241 Automaatit ja kieliopit, syksy 2016 Antti-Juhani Kaijanaho 29. syyskuuta 2016 TIETOTEKNIIKAN LAITOS Kontekstittomien kielioppien

Luku 5. Monadit. 5.1 Siirrännän ongelma

Abstraktit tietotyypit. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Säännöllisten kielten sulkeumaominaisuudet

jäsentäminen TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho 26. marraskuuta 2015 TIETOTEKNIIKAN LAITOS

Äärellisten automaattien ja säännöllisten kielten ekvivalenssi

uv n, v 1, ja uv i w A kaikilla

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Tämän vuoksi kannattaa ottaa käytännöksi aina kirjoittaa uuden funktion tyyppi näkyviin, ennen kuin alkaa sen määritemää kirjoittamaan.

Attribuuttikieliopit

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 16. helmikuuta 2012

8. Kieliopit ja kielet

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 26. tammikuuta 2012

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

TIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 29. toukokuuta 2013

Rajoittamattomat kieliopit (Unrestricted Grammars)

(0 1) 010(0 1) Koska kieli on yksinkertainen, muodostetaan sen tunnistava epädeterministinen q 0 q 1 q 2 q3

Laskennan mallit (syksy 2010) Harjoitus 8, ratkaisuja

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 2. helmikuuta 2012

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

Funktionimien kuormitus. TIES341 Funktio ohjelmointi 2 Kevät 2006

Pinoautomaatit. TIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 6. kesäkuuta 2013 TIETOTEKNIIKAN LAITOS. Pinoautomaatit.

Ohjelmoinnin peruskurssien laaja oppimäärä

Jäsennysalgoritmeja. TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 29. syyskuuta 2009 TIETOTEKNIIKAN LAITOS. Jäsennysalgoritmeja

TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho. 29. huhtikuuta 2011

Vasen johto S AB ab ab esittää jäsennyspuun kasvattamista vasemmalta alkaen:

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 16. marraskuuta 2015

on rekursiivisesti numeroituva, mutta ei rekursiivinen.

11.4. Context-free kielet 1 / 17

TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho. 31. maaliskuuta 2011

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 19. syyskuuta 2016

TIEA341 Funktio-ohjelmointi 1, kevät 2008

DFA:n käyttäytyminen ja säännölliset kielet

1. Universaaleja laskennan malleja

Rajoittamattomat kieliopit

Yhteydettömät kieliopit [Sipser luku 2.1]

Luku 4. Tietorakenteet funktio-ohjelmoinnissa. 4.1 Äärelliset kuvaukset

815338A Ohjelmointikielten periaatteet Harjoitus 7 Vastaukset

Automaatit. Muodolliset kielet

jäsentämisestä TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho 27. marraskuuta 2015 TIETOTEKNIIKAN LAITOS

Yllä osoitettiin, että säännöllisten kielten joukko on suljettu yhdisteen

TIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 26. kesäkuuta 2013

Säännöllisen kielen tunnistavat Turingin koneet

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 9. lokakuuta 2016

follow(a) first(α j ) x

2.4 Normaalimuoto, pohja ja laskentajärjestys 2.4. NORMAALIMUOTO, POHJA JA LASKENTAJÄRJESTYS 13

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 12. kesäkuuta 2013

Vaihtoehtoinen tapa määritellä funktioita f : N R on

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 3. joulukuuta 2015

Rekursio. Funktio f : N R määritellään yleensä antamalla lauseke funktion arvolle f (n). Vaihtoehtoinen tapa määritellä funktioita f : N R on

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 12. lokakuuta 2016

TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho. 16. toukokuuta 2011

Metodien tekeminen Javalla

Laiska laskenta, korekursio ja äärettömyys. TIEA341 Funktio ohjelmointi Syksy 2005

815338A Ohjelmointikielten periaatteet

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Yhteydettömän kieliopin jäsennysongelma

7.5 Monadimuuntimet. Vaikka monadinen tyypitus on monoliittista, niin monadit voi suunnitella ja toteuttaa hierarkisesti.

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Muodolliset kieliopit

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 5. marraskuuta 2015

Jos sekaannuksen vaaraa ei ole, samastamme säännöllisen lausekkeen ja sen esittämän kielen (eli kirjoitamme R vaikka tarkoitammekin L(R)).

FORMAALI SYSTEEMI (in Nutshell): aakkosto: alkeismerkkien joukko kieliopin määräämä syntaksi: sallittujen merkkijonojen rakenne, formaali kuvaus

TIES542 kevät 2009 Rekursiiviset tyypit

Chomskyn hierarkia. tyyppi 0 on juuri esitelty (ja esitellään kohta lisää) tyypit 2 ja 3 kurssilla Ohjelmoinnin ja laskennan perusmallit

Ohjelmoinnin peruskurssien laaja oppimäärä

Tarkastelemme ensin konkreettista esimerkkiä ja johdamme sitten yleisen säännön, joilla voidaan tietyissä tapauksissa todeta kielen ei-säännöllisyys.

abba 1. Perusrakenteita

Yksinkertaiset tyypit

Mitä funktionaalinen ohjelmointi on

Johdatus lukuteoriaan Harjoitus 11 syksy 2008 Eemeli Blåsten. Ratkaisuehdotelma

Transkriptio:

5.5. JÄSENNINKOMBINAATTOREISTA 67 type Env α = FiniteMap String α data EnvT m α = MkE (Env Integer m (Env Integer, α)) instance Transformer EnvT where promote mp = MkE $ λenv mp λr return $(env, r) instance Monad m Monad (EnvT m) where return = promote return (MkE p) f = MkE $ λenv p env λ(env, pr) case f pr of (MkE q) q env fail = promote fail instance Monad m EnvMonad (EnvT m) where begin (MkE p) = MkE $ λenv p env λ(_, r) return (env, r) bind v n = MkE $ λenv return (addtofm env v n, ()) getvalue v (MkE f) = MkE $ λenv case lookupfm env v of Just n return (env, n) Nothing f env Kuva 5.2: Ympäristömonadimuunnin 5.5 Jäsenninkombinaattoreista Tarkastellaanpa 6 nyt ongelman toista puolta. Miten jäsentää merkkijono niin, että saamme ulos sitä vastaavan tyyppiä Expr olevan abstraktin syntaksipuun? Tutkitaanpa ensin yleistä jäsennysongelmaa: tunnistetaan merkkijono ja kootaan sen perusteella semanttinen arvo. Jäsennin on siis oikeastaan funktio String α. Toisaalta joillakin merkkijonoilla voi olla useita semanttisia arvoja (jos kielen syntaksi on moniselitteinen), joten voisi olla parempi käyttää funktiotyyppiä String [α]. Aiemmin luennoilla on puhuttu kombinatorisesta ohjelmoinnista, jossa ohjelmia koostetaan toisista ohjelmista. Tämä oli monadisen lähestymistavan ydin. Nokkelina saattaisimme päätyä ajatukseen, että ehkä jäsentimetkin kannattaisi rakentaa kombinatorisesti: ainakin säännöllisten kielten tapauksessa tämä olisi mahdollista. Säännölliset kielethän voidaan kuvata säännöllisillä lausekkeilla: Määritelmä 5.5.1 Säännölliset lausekkeet ovat seuraavanlaisia: 6. Kirjoittaja pahoittelee tämän osan karuutta. Ehkä ensi vuonna sitten :)

68 LUKU 5. MONADIT 1. Symboli ε muodostaa yksinään säännöllisen lausekkeen. 2. Mikä tahansa aakkoston Σ merkki on säännöllinen lauseke. 3. Jos r on säännöllinen lauseke, niin r on säännöllinen lauseke (Kleenen tähti). 4. Jos r on säännöllinen lauseke, niin r + on säännöllinen lauseke. 5. Jos r ja s ovat säännöllisiä lausekkeita, niin rs on säännöllinen lauseke (r:n ja s:n katenointi). 6. Jos r ja s ovat säännöllisiä lausekkeita, niin r s on säännöllinen lauseke. Aakkoston Σ säännöllisten lausekkeiden joukkoa merkitään RE(Σ). Säännöllisen lausekkeen merkitys määritellään funktion L : RE(Σ) P(Σ ) avulla 7 : L ε = ε L c = c L rs = { vw v L r w L s } L r s = L r L s L r = L ε rr L r + = L rr missä c Σ Huomaa, että Kleenen tähden merkitys määritellään rekursiivisesti. Aivan formaalisti tämän tekeminen vaatisi kiintopisteoperaattoria, mikä ei valitettavasti kuulu tämän kurssin esitietoihin. Voisimme asettaa seuraavanlaiset Haskell-määritelmät: type Parser α = String [α] eps :: α Parser α eps a s = [a] char :: Char Parser Char char c [] = [] char c (x : _) c == x = [c] otherwise = [] 7. Sulut on tavanomainen tapa erottaa tarkasteltava kieli (tässä säännölliset lausekkeet) metakielestä (tässä merkkijonoteoria). Sulkujen sisällä oleva tavara on tarkasteltavaa kieltä, sulkujen ulkopuolella on metakieltä.

5.5. JÄSENNINKOMBINAATTOREISTA 69 ja niin edelleen. Ongelmaksi muodostuu katenoinnin määritteleminen. Tähän mennessä käyttämämme jäsentimen tyyppi ei mitenkään paljasta, mitä jäsennin on syötteestä syönyt. Siksi lienee parasta määritellä vielä kerran uudelleen: type Parser α = String [(α, String)] eps :: α Parser α eps a s = [(a, s)] char :: Char Parser Char char c [] = [] char c (x : xs) c == x = [(c, xs)] otherwise = [] Mutta lienee fiksumpaa tehdä jäsentimestä abstrakti tyyppi: newtype Parser α = MkP(String [(α, String)]) Ei ehkä niin yllättävästi näin määritelty jäsennintyyppi on monadi: instance Monad Parser where return x = MkP $ λs [(x, s)] (MkP p) f = MkP $ λs concatmap g $ p s where g (rv, rest) = case f rv of MkP p p rest fail _ = mzero Se kuuluu myös tyyppiluokkaan MonadPlus, joka on monadi, jolla on lisäksi yhteenlaskuoperaattori ja nolla : instance MonadPlus Parser where mzero = MkP $ λ_ [] (MkP p) mplus (MkP q) = MkP $ λs p s ++ q s Ajatus on se, että return x vastaa säännöllistä lauseketta ε, jonka semanttinen arvo on x ja p λx q on p:n ja q:n katenointi niin, että x:ään tulee p:n semanttinen arvo ja q tuottaa omansa lisäksi koko lausekkeen yhteisen semanttisen arvon. Kuvissa 5.3 ja 5.4 on tämän ajatuksen pohjalta määritelty jäsenninkombinaattorimoduli. Tämä moduli riittääkin edellä esitetyn laskimen jäsentimen rakentamiseen (ks. kuva 5.5). Kannattaa huomata, että vaikka edellä keskityttiinkin vain säännöllisten lausekkeiden kuvaamiseen, Haskellin rekursiiviset määritelmät antavat

70 LUKU 5. MONADIT module ParComb where import Char (isspace, isalpha, isdigit) import Monad (MonadPlus, mzero, mplus) newtype Parser a = MkP (String -> [(a, String)]) runparser :: Parser a -> String -> [(a, String)] runparser (MkP f) = f instance Monad Parser where return x = MkP $ \ s -> [(x, s)] (MkP p) >>= f = MkP $ \ s -> concatmap g $ p s where g (rv, rest) = case f rv of MkP p -> p rest fail _ = mzero instance MonadPlus Parser where mzero = MkP $ \ _ -> [] (MkP p) mplus (MkP q) = MkP $ \ s -> p s ++ q s eof :: a -> Parser a eof rv = MkP f where f [] = [(rv, [])] f (_:_) = [] eps :: a -> Parser a eps a = MkP $ \ s -> [(a, s)] maximal :: Parser [a] -> Parser [a] maximal (MkP p) = MkP $ \ s -> findmax (\ (a,s) -> length a) $ p s where findmax :: (a -> Int) -> [a] -> [a] findmax m l = let f (_, r) [] = r f p@(lr, r) (x:xs) lx > lr = f (lx, [x]) xs otherwise = f p xs where lx = m x in f (-1, []) l Kuva 5.3: Moduli ParComb alkaa

5.5. JÄSENNINKOMBINAATTOREISTA 71 item :: Parser Char item = MkP f where f [] = [] f (x:xs) = [(x, xs)] sat :: (Char -> Bool) -> Parser Char sat p = item >>= filt where filt c p c = return c otherwise = mzero char :: Char -> Parser Char char c = sat (==c) wchar :: Char -> Parser () wchar c = wsp >> char c >> return () string :: String -> Parser String string rv@([]) = return rv string rv@(x:xs) = char x >> string xs >> return rv wsp :: Parser () wsp = kstar (sat isspace) >> return () wstring :: String -> Parser String wstring s = wsp >> string s -- Kleene star kstar :: Parser a -> Parser [a] kstar p = eps [] mplus (p >>= \ x -> kstar p >>= \ xs -> return $ x:xs) -- Kleene plus kplus :: Parser a -> Parser [a] kplus p = p >>= \x -> kstar p >>= \xs -> return $ x:xs ident :: Parser String ident = maximal $ kplus (sat isalpha) wident :: Parser String wident = wsp >> ident int :: Parser Integer int = (maximal $ kplus (sat isdigit)) >>= return. (read :: String -> Integer) wint :: Parser Integer wint = wsp >> int Kuva 5.4: Moduli ParComb päättyy

72 LUKU 5. MONADIT meille ilmaiseksi kyvyn kuvata kontekstittomia kielioppeja. Ainoa rajoite on se, että tämä tekniikka ei kestä vasenrekursiivisia kielioppeja, joten vasen rekursio on ensin kieliopista poistettava. Kuvaa 5.5 vastaava konkreetti kielioppi on seuraavanlainen: Let-expression = "let", Identifier, "=", Expression, "in", Expression Expression Expression = Term, Rest of expression Rest of expression = (* nothing *) "+", Term, Rest of expression -", Term, Rest of expression Term = Factor, Rest of term Rest of term = (* nothing *) "*", Factor, Rest of term "/", Factor, Rest of term Factor =Identifier Integer literal "(", Let expression, ")" Kannattaa huomata, että tässä esitetty toteutus jäsenninkombinaattoreille on hyvin voimallinen mutta tehoton. Ks. esimerkiksi sivulle http://www.cs.uu.nl/ daan/parsec.html, jossa esitellään tehokas versio samasta ideasta.

5.5. JÄSENNINKOMBINAATTOREISTA 73 module Parser (parse) where import Expr import List (nub) import ParComb import Monad (mplus) parse :: String -> [Expr] parse s = nub $ map fst $ runparser (lexpr >>= eof) s lexpr :: Parser Expr lexpr = letex mplus expr letex :: Parser Expr letex = do wstring "let" x <- wident wchar = e <- expr wstring "in" e <- lexpr return $ Let x e e expr :: Parser Expr expr = term >>= expr expr :: Expr -> Parser Expr expr t = mplus (eps t) $ do op <- (wchar + >> return (:+)) mplus (wchar - >> return (:-)) t <- term expr (op t t ) term :: Parser Expr term = factor >>= term term :: Expr -> Parser Expr term f = mplus (eps f) $ do op <- (wchar * >> return (:*)) mplus (wchar / >> return (:/)) f <- factor term (op f f ) factor :: Parser Expr factor = (wident >>= return. Var) mplus (wint >>= return. Const) mplus (wchar ( >> lexpr >>= \r -> wchar ) >> return r) Kuva 5.5: Moduli Parser