7.5 Monadimuuntimet. Vaikka monadinen tyypitus on monoliittista, niin monadit voi suunnitella ja toteuttaa hierarkisesti.
|
|
- Kirsti Uotila
- 7 vuotta sitten
- Katselukertoja:
Transkriptio
1 7.5 Monadimuuntimet Vaikka monadinen tyypitus on monoliittista, niin monadit voi suunnitella ja toteuttaa hierarkisesti. Sitä varten ghc tarjoaa monadimuuntimet (monadic transformers) (O Sullivan et al., 2008, luku 18). Jos M ja N ovat monadeja, niin N (M a) on sellaisen monadin tyyppi, jossa ulompi monadi N tuottaa vastauksen tyyppiä M a jossa sisempi monadi M esittää sellaisen laskennan, jonka vastaus on a. Silloin tämä ulompi tyyppi N onkin sellainen, joka ottaa sisäänsä toisen monadin M antaa tuloksenaan uuden monadin, jossa sisään otettua monadia M on täydennetty uusilla ominaisuuksilla. Eri monadimuuntimet N voivat siten esittää eri täydennystapoja, ja haluttu monadi koota niitä pinoamalla B (...(O (N (M a)))...). Kun laskennassa halutaan käyttää jotakin sisämonadin operaatiota niin sen käyttö tulos <- operaatio kirjoitetaankin muodossa tulos <- lift $ operaatio jossa tämän monadimuuntimen t metodi lift :: (Monad m, MonadTrans t) => m a -> t m a nostaa tämän operaation sisämonadista m=m ulkomonadin operaatioksi. ulkomonadin operaatiota niin se on käytössä suoraan ilman nostamista. Siten monadimuuntimen t käyttö koostuu sellaisen tyypin kirjoittamisesta, jossa uloimpana on t ja sisällä sisämonadin tyyppi sisämonadista tarvittavien operaatioiden nostamisesta. ghc tarjoaa Control.Monad-kirjastoissaan monia tällaisia muuntimia. Esimerkkinä täydennetään monadia ST virheenkäsittelymekanismilla, jonka tarjoaa monadimuunnin ErrorT kirjastosta Control.Monad.Error. Ulkomonadimme tyyppi on muotoa ErrorT virheilmoitustyyppi sisämonadi vastaustyyppi Me käytämme virheilmoitustyyppinämme Stringiä. Meidän sisämonadimme on ST s jossa s on se eksistentiaalikvantifioitava tyyppiparametri. 122
2 Meidän tyyppimme omat parametrit ovat tämä s ja sama vastaustyyppi. newref1 on esimerkkinä sisämonadimme ST s operaation nostamisesta ulkomonadiimme. throwerror taas on esimerkki uudesta ominaisuudesta jonka tämä muunnin lisäsi sisämonadiimme. import Control.Monad.Error import Control.Monad.ST import Data.STRef type ErrorST s a = ErrorT String (ST s) a newref1 :: a -> ErrorST s (STRef s a) newref1 = lift. newstref suojattu1 :: Double -> Either String Bool suojattu1 x = runst $ runerrort $ do y <- newref1 True if x<0 then throwerror "Ei negatiivisia!" else return $ x>0 Tämän ulkomonadimme suoritusfunktio on runerrorst :: ErrorST s a -> Either String a runerrorst = runst. runerrort kuten suojattu1 osoittaa. Sen tulos on siis joko Left virheilmoitusteksti tai Right vastaus. Tällaisen suoritusfunktion rakenne on vastaava tyyppi toisinpäin: Tyypin N (M a) suoritusfunktio on muotoa runm (runn...) koska N-laskenta tuottaa M-laskennan joka tuottaa vastauksen. Tällaisen suoritusfunktion kirjoittaminen on hyvää ohjelmointityyliä. Valitettavasti runst:n tyypin eksistenssikvantifiointi aiheuttaa sen, ettei Haskell pystykään päättelemään tätä tyyppiä omin voimin. Tämän ongelman voisi kiertää kirjoittamalla operaattorin (.) tälle käyttötilanteelle käsin sen tämänhetkisen tyypin (kvanttoreineen) mutta sivuutamme sen nyt. 123
3 Tämä monadimuunnin ErrorT laajentaakin aiempaa monadia Maybe :: * -> * joka toteutti yksinkertaiset poikkeukset. Mayben virhetilanne Nothing ei sisällä lisäinformaatiota poikkeuksen syystä. Monadi Either e :: * -> * sisältää tämän lisäinformaation tyyppiä e. Sen jäsenyys voisi siis alkaa instance Monad (Either e) where (Right x) >>= k = k x j@(left _) >>= _ = j return = Right Mayben jäsenyyden pohjalta. Voimme vielä parantaa tyyppiturvallisuutta määrittelemällä kokonaan uuden tyypin joka käyttää monadimuunninta vain sisäisesti. Voimme tehdä sen newtypenä, koska tyypillämme on vain yksi kenttä, eli se muuntimella luomamme monadi. Meidän on liitettävä oma tyyppimme tyyppiluokan Monad jäseneksi. Meidän on liitettävä oma tyyppimme myös tyyppiluokan MonadError jäseneksi, jotta sillä olisi ne muuntimen lisäämät ominaisuudet kuten throwerror. ghc ilmoittaa mitkä instancet vielä tarvitaan. Nämä puuttuvat instancet saadaan puolestaan vaivatta deriving-mekanismilla ja kielilaajennuksella GeneralizedNewtypeDeriving joka monipuolisti sitä newtypeille. {-# LANGUAGE GeneralizedNewtypeDeriving #-} import Control.Monad.Error import Control.Monad.ST import Data.STRef newtype ErrST s a = ErrST{ errst :: ErrorT String (ST s) a } deriving(monad,monaderror String) runerrst = runerrort. errst newref2 :: a -> ErrST s (STRef s a) newref2 = ErrST. lift. newstref suojattu2 x = runst $ runerrst $ 124
4 do y <- newref2 True if x<0 then throwerror "Ei negatiivisia!" else return $ x>0 Haskell Platformin kirjasto Control.Monad.State tarjoaa erityisen kätevän monadimuuntimen StateT. Se lisää monadiseen koodiin taustaparametrin joka kulkee automaattisesti monadisen suorituksen rinnalla ilman lisäohjelmointia. Ohjelmoija voi siis käsitellä tätä taustaparametria monadisessa koodissaan silloin kun hän itse haluaa, eikä hänen tarvitse muulloin ottaa sitä erikseen huomioon. Sen monadisia operaatioita ovat esimerkiksi get joka lukee taustaparametrin nykyisen arvon put joka korvaa sen nykyisen arvon uudella Sen run-funktioita ovat esimerkiksi runstatet tämä alkuarvo joka suorittaa tämän laskennan tällä taustaparametrin alkuarvolla ja tuottaa parin (vastaus,loppuarvo) evalstatet tämä alkuarvo jonka tulos onkin pelkkä vastaus execstatet tämä alkuarvo jonka tulos onkin pelkkä loppuarvo. Samassa kirjastossa on myös monadi State joka tarjoaa samat palvelut. Se on määritelty lyhenteenä type State s = StateT s Identity eli tämän monadimuuntimen soveltamisena monadiin... Identity joka on siinä mielessä tarpeeton monadi, että se ei sisällä mitään omaa laskentaa. Identity on kuitenkin monadimuuntimissa hyödyllinen monadi, koska tällä tavalla saadaan myös monadi M monadimuuntimen MT sivutuotteena. 7.6 Monadinen jäsennys Jäsennys (parsing) on syötteenä saadun merkkijonon muuntamista sitä vastaavaksi jäsennyspuuksi (tai muuksi semanttiseksi rakenteeksi ) annetun kieliopin mukaisesti. Se on epädeterminististä laskentaa, koska kielioppi voi olla moniselitteinen eli sallia samalle merkkijonolle monta eri jäsennystä ja jäsennyspuuta jäsennettäessä merkkijonoa alusta eteenpäin voi paikallisesti olla useita eri lupaavia tapoja jatkaa jäsennystä, vaikka yksiselitteinen kielioppi sulkisikin lopulta pois kaikki muut kuin yhden niistä. 125
5 Aiemmin näimme, että tällaisen epädeterministisen laskennan eri vaihtoehdot voi esittää Haskell-listana, jonka jokainen alkio on yhden epädeterminisen laskennan välitilanne. Jäsennyksessä tällainen välitilanne on pari (taimi,loppuosa) jossa taimi on se jäsennyspuun osa, joka on koottu jo käsitellystä merkkijonon alkuosasta loppuosa merkkijonosta, joka on vielä jäsentämättä. Lopputilanne on silloin pari (jäsennyspuu,""). Dr. Seussin the Cat in the Hat sanoisi tämän: A parser for Things is a function from Strings to lists of pairs of Things and Strings. Haskellilla sama tyyppi ilmaistaan type Parser t = String -> [(t,string)]. Epädeterminisyyden lisäksi jäsennys on monadista toisestakin syystä: Jäsennyksessä on luonteva peräkkäisjärjestys jäsennä ensin merkkijonon alkuosa tällä tavalla ja jatka sitten jäljelle jääneen loppuosan jäsentämistä tuolla tavalla. Tätä lähestymistapaa jäsennykseen kutsutaan monadisiksi jäsenninkombinaattoreiksi (monadic parser combinators) koska jokainen yksittäinen jäsennin on monadinen mutkikkaammat jäsentimet kootaan yhdistelemällä yksinkertaisempia jäsentimiä toisiinsa. Voisimmekin ryhtyä itse kehittämään tätä jäsennystapaa kirjoittamalla määritelmät newtype Parser t =... instance Monad Parser where... mutta kun mukaan lisätään vielä jäsennysvirheiden käsittely Eitherilla kuten edellä, niin on vaivattomampaa ryhtyä käyttämään valmista työkalua. Haskell Platform sisältää raskaan sarjan jäsennystehtäviin erilliset työkalut Happy joka on Haskell-vastine UNIXin yacc- ja GNUn bison-työkalulle. Ne ovat metakääntäjiä (metacompiler) jotka tekevät annetulle kontekstittomalle (LA)LR-kieliopille kokoavan (bottom-up) jäsentäjän. Esimerkiksi ghc itse on toteutettu sen avulla. Alex joka on Haskell-vastine UNIXin lex- ja GNUn flex-työkalulle. Raskaan sarjan jäsentäminen jaetaan kahteen vaiheeseen: 126
6 selaukseen jossa syötemerkkijono kootaan suuremmiksi yksiköiksi, kuten ohjelmointikielissä varatuiksi sanoiksi, muuttujannimiksi,... Tässä vaiheessa poistetaan myös sellaiset osat joiden ei haluta näkyvän enää jäsennysvaiheessa, eli välilyönnit, rivinvaihdot, kommentit,... jäsennykseen joka jäsentää näin saadun suurempien yksikköjen listan. Nämä työkalut tuottavat selaajan, kun annetaan jäsennettävän kielen perusalkioiden kuvaus säännöllisinä lausekkeina. Niiden sijaan tutustummme keskiraskaan sarjan jäsennystehtäville sopivaan Haskell Platformin jäsenninkombinaattorikirjastoon Text.Parsec. (O Sullivan et al., 2008, luku 16) Jäsenninkombinaattorit ovat rekursiivisesti etenevää (recursive descent) jäsennystä. Rekursiivisesti etenevä jäsennys on osittavaa (top-down) ja se soveltuu LLkieliopeille. Emme kytke Parseciin erillistä selaajaa, vaan varaudumme itse kieliopissamme välilyönteihin yms. Haskell Platfrom sisältää myös kirjaston Data.Attoparsec joka käyttää samaa lähestymistapaa jäsennykseen tuottaa tehokkaampia jäsentäjiä vaatii enemmän ohjelmointia kuin Parsec. Muistutetaan mieliin kontekstittoman kieliopin idea: Se koostuu säännöistä muotoa välikesymboli vaihtoehto vaihtoehto vaihtoehto. vaihtoehto joka sanoo, että tällä välikesymbolilla on (vain) nämä vaihtoehdot. Jokainen vaihtoehto on puolestaan (äärellinen) jono muotoa symboli symboli symboli... symboli jonka jokainen symboli on joko välike- tai päätesymboli. Jokin näistä välikesymboleista on erityinen alkusymboli. Sitä on tapana merkitä S. Tällaisen kieliopin välikesymbolin A tuottamat johdokset määritellään induktiivisesti (eli kielioppisääntöjen pienimpänä kiintopisteenä): A itse on johdos. 127
7 Jos symbolijono muotoa αbγ on välikesymbolin A tuottama johdos, ja kieliopissa on sääntö muotoa B... β... niin myös symbolijono αβγ on välikesymbolin A tuottama johdos. Välikesymbolin A tuottama kieli koostuu niistä päätesymbolijonoista jotka ovat välikesymbolin A tuottamia johdoksia. Tällaisen kieliopin tuottama kieli on sen alkusymbolin S tuottama kieli. Tällaisen kieliopin tunnistusongelma on Tässä on päätesymbolijono δ. Kuuluuko se kielioppisi tuottamaan kieleen? jäsennysongelma on Tässä on päätesymbolijono δ. Voitko jäljittää sen tavan, miten alkusymbolisi S tuotti tämän johdoksen δ? Tämä tapa voidaan esittää jäsennyspuuna. Nämä jäsennyspuut voidaan esittää luontevasti algebrallisilla tietotyypeillä. Useimmiten haluammekin muuntaa syötemerkkijonon δ sitä vastaavaksi algebrallisen tietotyypin arvoksi. Toisin sanoen, haluammekin toteuttaa saman, jonka metodi read :: (Read a) => String -> a tekee, mutta omalla (eikä Haskellin) kieliopilla algebrallisen tietotyypin a arvojen tekstiesitykselle. Rekursiivisesti etenevän jäsentämisen perusidea on: Jokaiselle välike- ja päätesymbolille ohjelmoidaan sen oma rekursiivinen aliohjelma. Välikesymbolin A aliohjelman tehtävänä on lukea jäljellä olevan syötteen alusta jokin sen tuottama päätesymboleista koostuva johdos. Päätesymbolin aliohjelman tehtävänä on lukea jäljellä olevan syötteen alusta sen tekstiesitys. Nämä aliohjelmat kutsuvat rekursiivisesti toisiaan siten kuin kielioppi esittää. Tavallisesti sen ohjelmointi ennustavana (predictive) vaatii, että kielioppi täyttää LL(k)-ehdon, jonka mukaan tällainen välikesymbolin A aliohjelma voi valita sen säännössä muotoa A α 1 α 2 α 3... α p juuri oikean vaihtoehdon α i tutkimalla vain seuraavaa k syötemerkkiä tavallisesti tämä kurkistus (lookahead) k = 1. Haskellin epädeterminismi- eli listamonadi syötemerkkijonon käsittely laiskana merkkilistana kuten getcontents do-syntaksi 128
8 osoittavat toisen tavan valita vaihtoehto α i kuin LL(k): Seurataan epädeterministisesti eri vaihtoehtoja α 1, α 2, α 3,...,α p kunnes joku niistä onnistuu. Tämä epädeterminismi ja siihen liittyvä syötemerkkijono käsittely piilotetaan jäsennysmonadin sisälle. Silloin jokainen vaihtoehto α j voidaan kirjoittaa jäsennysmonadisena do-lausekkeena ja koko välikesymbolin A aliohjelma voidaan kirjoittaa yhdistämällä ne toisiinsa sopivalla jäsenninkombinaattorilla joka toteuttaa pystyviivalla esitetyn epädeterministisen valintaoperaation tai. Esimerkkinä olkoon tavalliset aritmeettiset lausekkeet S SAB B B BCD D D (S) e x A + - C * / jossa e on liukulukuvakio x on muuttujannimi. Niiden rakennetta ei tavallisesti kuvata kieliopissa, koska säännölliset lausekkeet riittäisivät siihen ne käsitellään tavallisesti selaajassa eikä jäsentäjässä jäsennetään ne Haskellin selaajalla, jonka Parsec tarjoaa valmiina. Kirjoitamme systemaattisesti jokaiselle välikesymbolille X jäsennysfunktion nimeltä parsex. sellaisen kutsut muodossa x <- parsex jossa x on sen palauttama semanttinen tulos. välikesymbolin X vaihtoehdot muodossa parsex = do a <- parsea b <- parseb c <- parsec return $ sem a b c... X ABC... semantiikkafunktiot jotka kokoavat jäsennyspuun rekursiokutsujen antamista alipuista. Emme vielä tässä vaiheessa rakenna jäsennyspuita, vaan palautamme aina arvon (). 129
9 import Text.Parsec -- Tässä otetaan käyttöön Parsecin Haskell-selaaja. import qualified Text.Parsec.Token as Token import Text.Parsec.Language(haskellDef) lexer = Token.makeTokenParser haskelldef -- "A parser for things..." type Parser thing = Parsec String () thing -- Tässä on jäsennyksen pääohjelma. Se kutsuu alkusymbolin -- jäsennysfunktiota. (Tyhjää) merkkijonoparametria -- käytetään virheilmoituksiin. jasenna = parse (do s <- parses eof -- Jäsentää syötteen lopun. return s) "" parses :: Parser () parses = do s <- parses a <- parsea b <- parseb return () do b <- parseb return () parsea = do char + return () do char - return () parseb = do b <- parseb c <- parsec d <- parsed return () do d <- parsed return () parsec = do char * return () do char / 130
10 return () parsed = do char ( s <- parses char ) return () do e <- Token.float lexer return () do x <- Token.identifier lexer return () Kielioppeihin liitetään kolmenlaisia attribuutteja (attribute) ja niiden muodostussääntöjä. Syntesoidut (synthesized) attribuutit muodostetaan jäsennyspuussa alhaalta ylöspäin lehdistä juureen S päin. Monadisessa jäsennyksessä nämä ovat niitä, jotka palautetaan jäsennysfunktioista parsex returnilla rekursiokutsuista. Vielä nyt ne ovat esimerkissämme() mutta täydennämme ne myöhemmin. Perityt (inherited) attribuutit muodostetaan jäsennyspuussa ylhäältä alaspäin juuresta S lehtiin päin. Monadisessa jäsennyksessä nämä esitetään jäsennysfunktioiden parsex parametreina. Emme tarvitse niitä esimerkissämme. Muut attribuutit ovat taustainformaatiota. Parsecissa ne voidaan esittää kuten monadissa Control.Monad.State. Emme tarvitse niitä esimerkissämme, joten määrittelemme niille tyypin () omassa Parserissamme. Tämä koodi kääntyy mutta jumittuu ikuiseen silmukkaan: parses (ja parseb) kutsuu itseään rekursiivisesti lukematta syötettä. Tämä vasen (left) rekursio on ongelmallista rekursiivisesti enenevälle jäsennykselle. Sitä tarvitaan vasemmalle assosioiville operaattoreille kuten - (ja / ) jolla x y z on (x y) z eikä x (y z). Funktion parses pitäisi arvata sokkona epädeterministisesti oikein, montako vähennyslaskua - syötteessä on tulossa eikä sen listatoteutus siihen pysty, koska se on syvyyssuuntaista etsintää. Tämä ongelma ratkaistaan tavallisesti muuttamalla kielioppia niin, että vasen rekursio poistuu. Parsecissa tämä kieliopin muuttaminen on ohjelmoitu monadisena kontrollirakenteena 131
11 chainl1 :: Parser a -> Parser (a -> a -> a) -> Parser a jolla ketju BABABA... B voidaan kirjoittaa chainl1 parseb parsea joka välttää jumittumisen. import Text.Parsec import Text.Parsec.Combinator -- Tässä otetaan käyttöön Parsecin Haskell-selaaja. import qualified Text.Parsec.Token as Token import Text.Parsec.Language(haskellDef) lexer = Token.makeTokenParser haskelldef -- "A parser for things..." type Parser thing = Parsec String () thing -- Tässä on jäsennyksen pääohjelma. Se kutsuu alkusymbolin -- jäsennysfunktiota. (Tyhjää) merkkijonoparametria -- käytetään virheilmoituksissa. jasenna = parse (do s <- parses eof return s) "" parses :: Parser () parses = chainl1 parseb parsea parsea = do char + return $ \ () () -> () do char - return $ \ () () -> () parseb = chainl1 parsed parsec parsec = do char * return $ \ () () -> () do char / return $ \ () () -> () parsed = Token.whiteSpace lexer >> 132
12 (do s <- Token.parens lexer parses return () (try $ do e <- Token.float lexer return ()) do e <- Token.natural lexer return () do x <- Token.identifier lexer return ()) Lisätään samalla välilyöntien käsittely: Poistetaan välilyönnit yms. Token.whiteSpace ennen välikesymbolia D sen jäsennysfunktiossa parsed. Sen Token-funktiot poistavat ne sen jälkeen. Tämän vuoksi siirrytään käsittelemään myös (S) Token-funktiolla. Token.float jäsentää Haskell-liukulukuvakion, jossa siis pitää olla desimaalipiste. Sallitaan myös (etumerkitön) kokonaislukuvakio Token.natural jotta sitä ei tarvita. Parsecin ei olekaan sama kuin kielioppien. Sen sijaan eka toka tarkoittaakin, että tokaa kokeillaan vain jos eka epäonnistui lukematta yhtään syötettä. Tässä siis yritetään ensin lukea aito liukulukuvakio, mutta jos se epäonnistuu (desimaalipisteen puuttumisen vuoksi) niin sitten yritetään jäsentää samat numeromerkit kokonaislukuvakiona. Jos tarvitaan oikeaa (kuten nyt) niin (try eka) toka muuntaa ekan sellaiseksi jäsentimeksi, joka epäonnistuessaan palauttaa lukemansa syötteen takaisin tokan luettavaksi. Näin tämä Parsecin try sallii LL( )-jäsennyksen, eli sellaisen rekursiivisen jäsennyksen, jossa ei olekaan kiinteää kurkistusylärajaa k. Tämä ero johtuu siitä, että luonnollinen kieli on moniselitteistä (eli saman syötteen voi jäsentää monella eri tavalla) ja mallintaa sitä formaalisti määritellyt kielet (kuten ohjelmointikielet) suunnitellaan yksiselitteisiksi. Tämä ero heijastuu jäsennysvirheiden käsittelyyn: luonnollisessa kielessä pitää silloin kokeilla jotakin toista jäsennystapaa ja tekee niin formaalisti määritellyssä kielessä pitääkin pysähtyä virheilmoitukseen ja tekee niin ohittamalla ne toiset jäsennystavat, kuten monadit Maybe ja Either tekivät. 133
13 Näin välttää turhan ja pahimmassa tapauksessa eksponentiaalisen (!) toisten jäsennystapojen kokeilun. Siis (try eka) toka sanoo, että jos ekassa tulee jäsennysvirhe, niin yritä tokaa vaikka tavallinen reaktio olisikin pysäyttää jäsennys siihen virheeseen. Nyt kun syntaksi on kunnossa siirrymme semantiikkaan. Jäsennyspuun tyyppi voidaan määritellä systemaattisesti määrittelemällä jokaiselle välikesymbolille X oma datatyyppinsä PuuX välikesymbolin X vaihtoehdot muodossa eli X ABC DEF... PuuX = KonstruktoriX1 PuuA PuuB PuuC KonstruktoriX2 PuuD PuuE PuuF... jokaiselle sen vaihtoehdolle oma konstruktori jokaisen konstruktorin kentät samassa järjestyksessä kuin sen vaihtoehdossa symbolit. Tämän taustalla on tietenkin se, että sekä kieliopin että data-määrittelyn on tai vaihdoehdon symboleilla että konstruktorin kentillä on keskinäinen järjestys. Näin sammme systemaattisesti tarkan data-määrittelyn alkuperäisen kielioppimme jäsennyspuille. Koska poistimme siitä vasemman rekursion monadisella kontrollirakenteella chainl1, jäsentimemme käyttääkin implisiittisesti siten muutettua kielioppia, vaikka emme konstruoineetkaan sitä eksplisiittisesti. Siten meidän on muokattava kontrollirakenteen chainl1 käyttöparametreja siten, että ne luovat alkuperäisen kielioppimme mukaisen jäsennyspuun. Se ei kuitenkaan ole työlästä algbrallisilla tietotyypeillä. Tämä on kuitenkin vähemmän mutkikasta kuin muodostaa muutettu kielioppi eksplisiittisesti ja tuottaa sen mukaisia jäsennyspuita, koska haluttu semantiikka on tavallisesti suunniteltu alkuperäisen kieliopin mukaisesti. Tietojenkäsittely- ja formaalissa kielitieteessä erotellaan konkreettinen syntaksi joka kuvaa merkkijonojen rakenteen. abstrakti syntaksi joka kuvaa sen semanttisen puurakenteen, jota merkkijono esittää. Abstrakti syntaksi voidaan nähdä tämän kaltaisena data-määritelmänä jossa ei ole jäänteitä konkreettisesta syntaksista. Jäsennyspuissa on vielä sen jäänteitä. 134
14 import Text.Parsec import Text.Parsec.Combinator -- Tässä otetaan käyttöön Parsecin Haskell-selaaja. import qualified Text.Parsec.Token as Token import Text.Parsec.Language(haskellDef) lexer = Token.makeTokenParser haskelldef -- "A parser for things..." type Parser thing = Parsec String () thing -- Jäsennyspuutyypit. data PuuS = VaihtoehtoS1 PuuS PuuA PuuB VaihtoehtoS2 PuuB deriving (Show) data PuuA = VaihtoehtoA1 VaihtoehtoA2 deriving (Show) data PuuB = VaihtoehtoB1 PuuB PuuC PuuD VaihtoehtoB2 PuuD deriving (Show) data PuuC = VaihtoehtoC1 VaihtoehtoC2 deriving (Show) data PuuD = VaihtoehtoD1 PuuS VaihtoehtoD2 Double VaihtoehtoD3 String deriving (Show) -- Tässä on jäsennyksen pääohjelma. Se kutsuu alkusymbolin -- jäsennysfunktiota. (Tyhjää) merkkijonoparametria -- käytetään virheilmoituksissa. jasenna = parse (do s <- parses eof return s) "" parses :: Parser PuuS parses = chainl1 (fmap VaihtoehtoS2 parseb) parsea parsea :: Parser (PuuS -> PuuS -> PuuS) parsea = do char + return $ \ vasen (VaihtoehtoS2 oikea) -> VaihtoehtoS1 vasen VaihtoehtoA1 oikea do char - return $ \ vasen (VaihtoehtoS2 oikea) -> 135
15 VaihtoehtoS1 vasen VaihtoehtoA2 oikea parseb :: Parser (PuuB) parseb = chainl1 (fmap VaihtoehtoB2 parsed) parsec parsec :: Parser (PuuB -> PuuB -> PuuB) parsec = do char * return $ \ vasen (VaihtoehtoB2 oikea) -> VaihtoehtoB1 vasen VaihtoehtoC1 oikea do char / return $ \ vasen (VaihtoehtoB2 oikea) -> VaihtoehtoB1 vasen VaihtoehtoC2 oikea parsed :: Parser PuuD parsed = Token.whiteSpace lexer >> (do s <- Token.parens lexer parses return $ VaihtoehtoD1 s (try $ do e <- Token.float lexer return $ VaihtoehtoD2 e) do e <- Token.natural lexer return $ VaihtoehtoD2 $ frominteger e do x <- Token.identifier lexer return $ VaihtoehtoD3 x) Semanttiset rakenteet eivät aina ole jäsennyspuita tai abstraktia syntaksia. Semanttinen rakenne valitaan halutun käyttötarkoituksen mukaan. Valitaan käyttötarkoitukseksemme laatia semanttisena rakenteena merkkijonosta funktio tyyppiä joka [(String,Double)] -> Either String Double ottaa sisään syötelistan pareja (x,d) jossa x on muuttujannimi ja d sitä vastaava liukulukuarvo antaa vastauksenaan merkkijonon kuvaaman lausekkeen arvon kun sen jokaiselle muuttujalle x annetaan sitä vastaava arvo d tai virheilmoituksen jos syötelistasta puuttuikin jokin sellainen muuttujannimi x joka esiintyy lausekkeessa. Nyt meillä on kaksi monadia sisäkkäin: Ulompana on Parsecin jäsennysmonadi. Sisempänä on tämä semanttinen funktio, joka on Either String -virheenkäsittelymonadissa. 136
16 import Text.Parsec import Text.Parsec.Combinator import Control.Monad.Error -- Tässä otetaan käyttöön Parsecin Haskell-selaaja. import qualified Text.Parsec.Token as Token import Text.Parsec.Language(haskellDef) lexer = Token.makeTokenParser haskelldef -- "A parser for things..." type Parser thing = Parsec String () thing -- Käännöksen tulostyyppi. type LookupFn = [(String,Double)] -> Either String Double -- Tässä on jäsennyksen pääohjelma. Se kutsuu alkusymbolin -- jäsennysfunktiota. (Tyhjää) merkkijonoparametria -- käytetään virheilmoituksissa. jasenna = parse (do s <- parses eof return s) "" parses :: Parser LookupFn parses = chainl1 parseb parsea parsea :: Parser (LookupFn -> LookupFn -> LookupFn) parsea = do char + return $ binop (+) do char - return $ binop (-) binop :: (Double -> Double -> Double) -> LookupFn -> LookupFn -> LookupFn binop op p q xs = do p <- p xs q <- q xs return $ p op q parseb :: Parser LookupFn parseb = chainl1 parsed parsec parsec :: Parser (LookupFn -> LookupFn -> LookupFn) 137
17 parsec = do char * return $ binop (*) do char / return $ binop (/) parsed :: Parser LookupFn parsed = Token.whiteSpace lexer >> (Token.parens lexer parses (try $ do e <- Token.float lexer return $ const $ return e) do e <- Token.natural lexer return $ const $ return $ frominteger e do x <- Token.identifier lexer return $ maybe (throwerror $ "Muuttujalta " ++ x ++ " puuttuu arvo!") return. lookup x) kaanna :: (Monad m) => String -> m LookupFn kaanna = either (error. show) return. jasenna Parsec on esimerkki sovelluskohtaisesta kielestä (Domain Specific Language, DSL). DSL-ideassa lähestytään yleistä ohjelmointiongelmaa niin, että 1 ensin suunnitellaan ja toteutetaan juuri tällaisiin ohjelmointiongelmiin soveltuva pieni ohjelmointikieli 2 sitten käytetään tätä pientä kieltä kun ratkotaan tämän ohjelmointiongelman tapauksia. Parsecissa yleinen ohjelmointiongelma on jäsennys. 1 Parsecissa on rekursiivisesti etenevän jäsentämisen primitiivit sekä välineet niiden yhdistelemiseksi toisiinsa. 2 Näillä Parsecin primitiiveillä on vaivattomampaa kirjoittaa annetulle kieliopille rekursiivisesti etenevä jäsentäjä kuin perus-haskellilla. Haskell on hyvä alusta DSL-ohjelmoinnille: Toteutettava DSL voi lainata Haskellin funktiokäsitteen itselleen, koska laiskuutensa nojalla Haskell vain välittää parametreja funktioiden välillä laskematta niitä DSL voi itse päättää milloin se ne laskee. Laiskuutensa vuoksi Haskellilla on myös vaivatonta määritellä uusia DSLkontrollirakenteita kuten Parsecin. Muissa ohjelmointikielissä käytettäisiin makroja tms. 138
18 DSL vodaan määritellä monadisena, jolloin sen peräkkäissuoritus voidaan määritellä sen metodina (>>=) kirjoittaa do-syntaksilla. Esimerkiksi Parsec määritteli oman peräkkäissuorituksensa tarkoittavan että jäsennä syötettä eteenpäin. 8 Abstraktit tietotyypit Haskellissa on yksinkertainen modulijärjestelmä, joka on tarkoitettu abstraktien (abstract) tietotyyppien määrittelemiseen. Yleisesti tietotyyppi on abstrakti, jos sen toteutus on piilotettu sitä käyttävältä ohjelmakoodilta, jotta koodin täytyy käyttää tätä tyyppiä vain siten kuin sen ohjelmoija on tarkoittanut. Algebrallisilla tietotyypeillä se tarkoittaa tietotyyppiä, jonka konstruktorit on piilotettu sitä käyttävältä ohjelmakoodilta, koska silloin koodi ei voi luoda eikä tutkia sen arvoja omin päin vaan ainoastaan siten kuin sen ohjelmoija on tarkoittanut. Luvussa 6.2 kuvatut eksistentiaaliset tyypit ovat tällaisia abstrakteja tyyppejä, koska niiden arvoa pystyi käsittelemään vain tyyppiluokkana annetun rajapinnan kautta (jos sellainen annettiin). Ohjelmointikielten teoriassa modulit voidaankin nähdä tapana määritellä eksistentiaalisia tyyppejä. Funktionaalisessa ML-kieliperheessä kuten Standard ML (Milner et al., 1997, luvut 3 ja 5) ja (O)Caml on kehitetty paljon Haskellia monipuolisempia modulijärjestelmiä. Tosin niissä ei vuorostaan ole tyyppiluokkia. Haskellin moduleilla ilmaistaan, mitkä ohjelman tässä lähdekooditiedostossa määritellyistä funktioista, tyypeistä ja tyyppiluokista ovat paikallisia eli käytettävissä vain tässä samassa lähdekooditiedostossa julkisia eli käytettävissä kaikissa muissakin lähdekooditiedostoissa, jotka ottavat ne käyttöön importilla. Jos Haskell-lähdekooditiedosto on oma modulinsa, niin silloin sen rakenne on: module Hierarkinen.Modulin.Nimi viennit where tuonnit määritelmät ghci olettaa näin otsikoidun hierarkisen modulin löytyvän tiedostosta nimeltä Linuxissa Hierarkinen/Modulin/Nimi.hs Windowsissa hierarkinen\modulin\nimi.hs jonka polku alkaa 139
19 nykyisestä työhakemistosta josta ohjelmoija voi siis aloittaa tälle nyt tekeillä olevalle ohjelmalleen sen oman moduli- ja tiedostohierarkian asennuksen yhteydessä määritellystä pakkaushakemistosta josta alkaen se löytää vakiokirjastonsa kuten Data.Word jne. muista hakemistoista joita voi antaa optioina joko komentorivillä tai lähdekooditiedoston erikoiskommentissa {-# OPTIONS_GHC... #-} Ainakin Linuxissa näitä(kin) optioita voi asettaa myös 8.1 Tuonnit työhakemistokohtaisessa alustustiedostossa./.ghci tai käyttäjäkohtaisessa alustustiedostossa /.ghci. Modulin tuonnit koostuvat tuontilauseista jollaisen perusmuoto on import [qualified] Toinen.Hierarkinen [as Lyhenne] johon tutustuimmekin jo luvussa 4.6. Jos haluaakin tuoda modulin tekemistä määritelmistä vain osan, niin ne voi antaa monikkona (eli suluissa (...) pilkuin, eroteltuna) koko importin lopussa. Tai jos haluaakin tuoda modulin tekemistä määritelmistä muut kuin tässä monikossa mainitut, niin tämän monikon eteen voi kirjoittaa hiding. 8.2 Viennit Modulin viennit on sekin monikko, jossa kerrotaan mitä määritelmiään tämä moduli julkistaa ja miten. Jos tämä monikko viennit puuttuu, niin tämä moduli julkistaa kaikki määritelmänsä (paitsi importit). Yksinkertaisin julkistettava määritelmä on jonkin muuttujan nimi. Toinen julkistettava määritelmä on module Toinen.Hierarkinen jossa tässä samassa modulissa on myös import Toinen.Hierarkinen ja joka myös julkistaa sen modulin Toinen.Hierarkinen jonka se itse on ottanut käyttöön. Esimerkiksi moduli Data.Array julkistaa myös modulin Data.Ix, koska jos jokin moduli käyttää taulukoita, niin se käyttänee myös taulukkoindeksien operaatioita. Tällöin taulukkoja käyttävä modulissa riittää pelkkä import Data.Array 140
20 Kolmas julkistettava määritelmä on type-synonyymi. Se julkistetaan kirjoittamalla sen nimi monikkoon viennit. Neljäs julkistettava määritelmä on tyyppiluokka. Se voidaan julkistetaan kolmella eri tavalla: Pelkkä TyyppiLuokanNimi julkaisee sen siten, että muut modulit voivat käyttää sitä rajoitteissaan (...) => mutta eivät voi liittää omia tyyppejään sen uusiksi jäseniksi koska tämä moduli ei julkistanutkaan sen metodeja. Sen perään voi kirjoittaa monikkona niiden sen metodien nimet, jotka tämä moduli haluaa julkistaa. Silloin muut modulit voivat liittää omia tyyppejään sen jäseniksi määrittelemällä nämä metodit niiden instanceissa. Tämän monikon erikoismuotona on(..) joka tarkoittaa kaikki sen metodit. Viides julkistettava määritelmä on data-tyyppi. Sekin voidaan julkistaa vastaavasti kolmella eri tavalla: Pelkkä TyypinNimi julkistaa sen kokonaan abstraktina tyyppinä, jota muut modulit voivat käyttää omissa tyypeissään ::... mutta eivät voi käyttää koodissaan hahmojen tai lausekkeiden kautta koska tämä moduli ei julkistanutkaan sen konstruktoreja. Sen perään voi kirjoittaa monikkona niiden sen konstruktoreiden ja kenttien nimet, jotka tämä moduli haluaa julkistaa. Silloin muut modulit voivat käyttää niitä koodissaan, jolloin TyypinNimi onkin vain osittain abstrakti. Tämän monikon erikoismuotona on (..) joka tarkoittaa kaikki sen konstruktorit ja kentännimet. Silloin myös TyypinNimi on muissakin moduleissa täysin vapaasti käytettävissä samoin kuin niiden määrittelemät tyypit. Modulissa voi olla määritelmä muuttuja :: Tyyppi jossa moduli julkaisee tämän muuttujan mutta ei julkaise jotakin sen Tyyppiin sisältyvää itse määrittelemäänsä tyyppiä tai tyyppiluokkaa. Koska muissa moduleissa on voitava käyttää tätä julkaistua muuttujaa, niin myös tämä julkistamaton tyyppi tai tyyppiluokat julkistetaan, mutta vain tyypinpäättelyä varten, eli sellaisena etteivät muut modulit voikaan käyttää sitä omissa tyypeissään. Esimerkkinä tehdään luvun pinosta abstrakti tietotyyppi. 141
21 module Stack(Stack,emptyStack,push,pop) where newtype Stack t = IntoStack { fromstack :: [t] } deriving (Show) emptystack :: Stack t emptystack = IntoStack [] push :: t -> Stack t -> Stack t push x = IntoStack. (x :). fromstack pop :: u -> (t -> Stack t -> u) -> Stack t -> u pop e _ (IntoStack []) = e pop _ f (IntoStack (x:xs)) = f x (IntoStack xs) 8.3 Päämoduli Koko ohjelman päämoduli on poikkeus: Sen rakenne on module Main(main) where... main :: IO () main =... Sen tiedostonimi saa olla mikä tahansa ohjelma.hs. Sen kääntäminen komennolla ghc ohjelma.hs tuottaa siitä suorituskelpoisen ohjelmatiedoston nimeltään Linuxissa ohjelma Windowsissa ohjelma.exe samaan hakemistoon. Tämä main on standardoitu nimi Haskellin pääohjelmalle, joka suoritetaan ajamalla tämä ohjelmatiedosto. Pääohjelmaa ei tarvitse kirjoittaa omaksi modulikseen Main. Riittää määritellä tämä main :: IO () lähdekooditiedostossa ohjelma.hs. 142
22 9 Curryn ja Howardin vastaavuudesta Curryn ja Howardin vastaavuus (the Curry-Howard correspondence) (Sørensen and Urzyczyn, 2006) on symmetria formaalin loogisen todistusteorian ja funktionaalisen ohjelmoinnin välillä. Tietojenkäsittelyteoriassa se on inspiroinut esimerkiksi ohjelmointikielten tyyppiteoriaa ohjelmien verifiointia formaalia ohjelmankehitystä. Kyseessä on jälleen sama loogikko Haskell B. Curry sekä hänen oppilaansa William A. Howard. Tässä on sen perusintuitio. Otamme käyttöön merkintöjä todistusteoriasta: Merkintä nämä = tuo luetaan, että näistä oletuksista voidaan todistaa tuo johtopäätös. Nämä oletukset ja johtopäätökset ovat nyt muotoa eli tämä λ-lauseke on tuota tyyppiä. Esitämme merkinnällä sääntöjä tämän = käytölle. Perussääntö on lauseke :: tyyppi jos olemme osoittaneet kaikki nämä niin voimme osoittaa myös tämän e :: t,... = e :: t eli jos jokin kuuluu jo oletuksiin, niin se voidaan triviaalisti todistaa niistä. Käytämme yksinkertaista tyypitettyä λ-laskentaa, jonka tyyppirakenne on: Tulotyyppi (t,u) jolla on konstruktori (x,y) sekä kirjastofunktiot fst ja snd. Funktiotyyppi t -> u. Summatyyppi Either t u jolla on konstruktorit Left ja Right sekä kirjastofunktio either. Käytämme näitä kirjastofunktioita case-hahmonsovituksen sijasta. Kirjoitamme merkinnöillämme tämän kielen tyypityssääntöjä: 143
23 Tulotyypin konstruktorin tyypityssääntö on Γ = x :: t Γ = y :: u Γ = (x,y) :: (t,u) (17) ja sen kirjastofunktioiden tyypityssäännöt ovat Γ = z :: (t,u) Γ = fst z :: t ja Γ = z :: (t,u) Γ = snd z :: u. (18) Funktiotyypin konstruktorin λ tyypityssääntö on x :: t, Γ = e :: u Γ = λx.e :: t -> u (19) ja funktionkutsun tyypityssääntö on Γ = f :: t -> u Γ = f a :: u. Γ = a :: t (20) Summatyypin konstruktoreiden tyypityssäännöt ovat Γ = z :: t Γ = Left z :: Either t u ja Γ = z :: u Γ = Right z :: Either t u sekä sen kirjastofunktion tyypityssääntö on Γ = f :: t -> v Γ = g :: u -> v Γ = either f g :: Either t u -> v. (21) (22) (23) Ohjelmointikielten tyyppijärjestelmien teoriassa käytetään tällaisia sääntöjä, koska niillä voidaan spesifioida, mitä tyyppijärjestelmän toteutusten pitää päätellä ohjelmakoodista niitä tutkimalla loogisessa todistusteoriassa kehitetyin välinein voidaan analysoida tyyppijärjestelmän ominaisuuksia. Unohdetaan hetkeksi näistä säännöistä kaikki osat lauseke ::. Tulotyypin konstruktorin tyypityssääntö (17) pelkistyy muotoon = t = u = (t,u) ja sen kirjastofunktioiden tyypityssäännöt (18) muotoihin = (t,u) = t ja = (t,u) = u. Mutta nämähän ovat samat kuin konjunktion t u käyttösäännöt lauselogiikan todistuksissa! 144
24 Curryn ja Howardin vastaavuuden ensimmäinen osa onkin että lauselogiikan kaavat ovat λ-laskennan tyyppejä ( formulæ as types ) tässä mielessä. Ohjelmoinnissa tyyppi (t,u) onkin sellainen, jonka arvoissa on 1. kenttä tyyppiä t ja 2. kenttä tyyppiä u. Tästä syystä Haskell käyttää monikoita esimerkiksi rajoitteissaan(...)=>. Funktiotyypin tyypityssääntö (19) pelkistyy muotoon t, Γ = u Γ = t -> u ja tyypityssääntö (20) pelkistyy muotoon (24) Γ = t -> u Γ = u Γ = t jotka ovat puolestaan implikaation t u käyttösäännöt. Erityisesti sääntö (25) on päättelyaskel Modus Ponens. Summatyypin konstruktoreiden tyypityssäännöt (21) ja (22) pelkistyvät muotoihin = t = u ja = Either t u = Either t u ja sen kirjastofunktion tyypityssääntö (23) pelkistyy muotoon = t -> v = u -> v = Either t u -> v (25) (26) jotka ovat puolestaan disjunktion t u käyttösäännöt. Säännön (24) nojalla sääntö (26) kertoo, miten t u toimii ollessaan oletuksena. Ohjelmoinnissa tyyppi Either t u onkin sellainen, jonka arvossa on joko kenttä tyyppiä t tai kenttä tyyppiä u, ja tiedämme kumpi niistä. Tämä on kuitenkin konstruktiivinen eli intuitionistinen logiikka, koska t u voidaan osoittaa vain jos joko t voidaan osoittaa tai u voidaan osoittaa. Tällaisessa logiikassa esimerkiksi väitettä Marsissa joko on elämää tai siellä ei ole elämää ei voikaan (vielä) osoittaa, koska emme ole osoittaneet kumpaakaan näistä kahdesta mahdollisuudesta. Tutummassa klassisessa logiikassa ei sanotakaan voidaan osoittaa vaan on totta. Sellaisessa logiikassa Mars-esimerkkiväitteemme on totta, koska joko Marsissa on elämää tai siellä ei ole elämää on totta, vaikka emme tiedäkään (vielä) kumpi niistä on totta. Palautetaan sitten sääntöihin niistä unohdetut osat lauseke ::. Ne voidaan nähdä esityksinä vastaavan todistuksen rakenteelle, koska lausekkeen uloimmainen konstruktori tai funktionkutsu identifioi sen säännön, jota käytetään seuraavaksi. Curryn ja Howardin vastaavuuden toinen osa onkin, että lauselogiikan todistukset ovat λ-laskennan termejä eli laajemmin tulkittuna funktionaalisia ohjelmia ( proofs as programs ) tässä mielessä. 145
25 Tässä valossa tyypitetty β-reduktioaskel (β) eli funktionaalisen ohjelman suorituksen perusaskel (λx.f :: t -> u) (g :: t) f[x g] :: u on todistuksen sieventämistä poistamalla siitä jokin Modus Ponens -päättelyaskel (25) siten, että väitteen t todistus g sijoitetaan kaikkialle missä oletusta x käytetään. Voidaan määritellä kolme ongelmaa: Tyypintarkistus: Annetaan lauseke ja tyyppi, ja pitää selvittää päteekö niille lauseke :: tyyppi vaiko ei. Tämä on se ongelma, jota kaikki staattisesti tyypitetyt ohjelmointikielet ratkovat käännösaikana. Tutkimuksessa pyritään kehittämään sellaisia ilmaisuvoimaisia tyyppijärjestelmiä, jotka rajoittaisivat mahdollisimman vähän ohjelmoijan ilmaisunvapautta. Tyypinpäättely: Annetaan vain lauseke, ja pitää löytää sellainen tyyppi jolla lauseke :: tyyppi pätee (tai osoittaa, ettei sellaista tyyppiä ole). Tämä on se ongelma, jota tyypinpäättelevät ohjelmointikielet kuten Haskell ratkovat käännösaikana. Tutkimuksessa pyritään kehittämään sellaisia päättelymekanismeja, joilla tällaiset ilmaisuvoimaisemmat tyyppijärjestelmät olisivat ohjelmoijalle näkymättömiä. Tyyppiohjattu ohjelmointi: Annetaan vain tyyppi, ja pitää löytää sellainen lauseke jolla lauseke :: tyyppi pätee (tai osoittaa, ettei sellaista lauseketta ole). Curryn ja Howardin vastaavuuden näkökulmasta tämä ongelma on: Tässä on spesifikaatio logiikalla ilmaistuna. Kehitä sen mukainen ohjelma! Käytimme kurssilla perusversiota tästä lähestymistavasta, kun lähdimme ohjelmoimaan funktiota 1 kirjoittamalla ensin sille tyypin 2 sitten valitsemalla jonkin parametrin ja haarautumalla koodissa sen tyypin konstruktoreiden mukaan. Kysymystä Kuuluuko tähän tyyppiin yhtään lauseketta? kutsutaan sen asutusongelmaksi (type inhabitation problem). Ohjelmointinäkökulmasta se on Onko tämä spesifikaatio mahdollinen vai mahdoton toteuttaa? On kehitetty logiikkoja, joiden tietokonetoteutukset pystyvät generoimaan todistuksesta sitä vastaavan funktionaalisen ohjelmakoodin. Silloin ohjelmoijan työ muuttuu koodin kirjoittamisesta spesifikaation ristiriidattomuuden osoittamiseksi konstruktiivisesti, jotta todistuksesta voidaan tuottaa sitä vastaava ohjelmakoodi. Nämä välineet eivät vielä ole nousseet tutkimuslaboratorioista ohjelmistoteollisuuteen. Ehkä joskus tulevaisuudessa? Ehkä sellaisissa ohjelmointiongelmissa, joissa ohjelman oikeellisuus on kriittistä ja sen testaaminen vaikeaa? Jos näin käy, niin sen mukana funktionaalisen ohjelmoinnin rooli korostuu. 146
Jäsennys. TIEA341 Funktio ohjelmointi 1 Syksy 2005
Jäsennys TIEA341 Funktio ohjelmointi 1 Syksy 2005 Muistutus: Laskutehtävä ja tulos data Laskutehtava = Luku Double Yhteen Laskutehtava Laskutehtava Vahennys Laskutehtava Laskutehtava Tulo Laskutehtava
LisätiedotTIEA341 Funktio-ohjelmointi 1, kevät 2008
TIEA341 Funktio-ohjelmointi 1, kevät 2008 Luento 9 Kombinaattoreista Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 21. tammikuuta 2008 Currying Haskell-funktio ottaa aina vain yhden
Lisätiedot5.5 Jäsenninkombinaattoreista
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
LisätiedotAlgebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005
Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005 Tällä luennolla Algebralliset tietotyypit Hahmonsovitus (pattern matching) Primitiivirekursio Esimerkkinä binäärinen hakupuu Muistattehan...
Lisätiedot815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset
815338A Ohjelmointikielten periaatteet 2015-2016. Harjoitus 2 vastaukset Harjoituksen aiheena on BNF-merkinnän käyttö ja yhteys rekursiivisesti etenevään jäsentäjään. Tehtävä 1. Mitkä ilmaukset seuraava
Lisätiedottään painetussa ja käsin kirjoitetussa materiaalissa usein pienillä kreikkalaisilla
2.5. YDIN-HASKELL 19 tään painetussa ja käsin kirjoitetussa materiaalissa usein pienillä kreikkalaisilla kirjaimilla. Jos Γ ja ovat tyyppilausekkeita, niin Γ on tyyppilauseke. Nuoli kirjoitetaan koneella
LisätiedotMonadeja siellä, monadeja täällä... monadeja kaikkialla? TIES341 Funktio ohjelmointi 2 Kevät 2006
Monadeja siellä, monadeja täällä... monadeja kaikkialla? TIES341 Funktio ohjelmointi 2 Kevät 2006 Materiaalia Paras verkkomatsku: http://www.nomaware.com/monads/html/ Komentoanalogiasta vielä Monadityypin
LisätiedotJäsennysalgoritmeja. TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 29. syyskuuta 2009 TIETOTEKNIIKAN LAITOS. Jäsennysalgoritmeja
TIE448 Kääntäjätekniikka, syksy 2009 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 29. syyskuuta 2009 Sisällys Sisällys Seuraava deadline Vaihe B tiistai 6.10. klo 10 selaaja ja jäsentäjä toimivat Kääntäjän
LisätiedotTIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 30. marraskuuta 2015
TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 30. marraskuuta 2015 Sisällys t Väitöstilaisuus 4.12.2015 kello 12 vanhassa juhlasalissa S212 saa tulla 2 demoruksia
LisätiedotLaajennetaan vielä Ydin-Haskellia ymmärtämään vakiomäärittelyt. Määrittely on muotoa
2.6. TIETOKONE LASKIMENA 23 Edellä esitetty Ydin-Haskell on hyvin lähellä sitä kieltä, jota GHCi (Glasgow Haskell Compiler, Interactive) sekä muut Haskell-järjestelmät suostuvat ymmärtämään. Esimerkiksi:
LisätiedotTyyppejä ja vähän muutakin. TIEA341 Funktio ohjelmointi 1 Syksy 2005
Tyyppejä ja vähän muutakin TIEA341 Funktio ohjelmointi 1 Syksy 2005 Viime luennolla... Haskellin alkeita pääasiassa Hello World!... ja muita tutunoloisia ohjelmia Haskellilla Haskellin voima on kuitenkin
Lisätiedot5.2.5 Konstruktoriluokat Edellisessä esimerkissä määrittelimme oman tyyppiluokan Isqrt jonka jäsenet olivat tyyppejä (kuten Int, Integer, Word,...).
5.2.5 Konstruktoriluokat Edellisessä esimerkissä määrittelimme oman tyyppiluokan Isqrt jonka jäsenet olivat tyyppejä (kuten Int, Integer, Word,...). Voimme määritellä tyyppiluokkia myös tyyppikonstruktoreille
LisätiedotEsimerkki: Laskin (alkua) TIEA341 Funktio ohjelmointi 1 Syksy 2005
Esimerkki: Laskin (alkua) TIEA341 Funktio ohjelmointi 1 Syksy 2005 Esimerkki: Laskin Liukulukulaskentaa Yhteen, vähennys, kerto ja jakolaskut Syötteenä laskutehtävä, tulosteena tulos tai virheilmoitus
Lisätiedotfollow(a) first(α j ) x
Tästä ensimmäisestä LL(1)-ehdosta (14) seuraa erityisesti, että korkeintaan yksi välikkeen A säännöistä voi tuottaa tyhjän merkkijonon ε eli tehdä välikkeestä A tyhjentyvän (eli nollautuvan). Toinen osa
Lisätiedot815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset
815338A Ohjelmointikielten periaatteet 2015-2016. Harjoitus 6 Vastaukset Harjoituksen aiheena on funktionaalinen ohjelmointi Scheme- ja Haskell-kielillä. Voit suorittaa ohjelmat osoitteessa https://ideone.com/
LisätiedotVasen johto S AB ab ab esittää jäsennyspuun kasvattamista vasemmalta alkaen:
Vasen johto S AB ab ab esittää jäsennyspuun kasvattamista vasemmalta alkaen: S A S B Samaan jäsennyspuuhun päästään myös johdolla S AB Ab ab: S A S B Yhteen jäsennyspuuhun liittyy aina tasan yksi vasen
LisätiedotTIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 3. lokakuuta 2016
TIEA241 Automaatit ja kieliopit, syksy 2016 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 3. lokakuuta 2016 Sisällys n tunnistin Jay : An Efficient Context-Free Parsing Algorithm. Communications of the
LisätiedotTIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 10. kesäkuuta 2013
TIEA241 Automaatit ja kieliopit, kesä 2013 etenevä Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 10. kesäkuuta 2013 Sisällys etenevä etenevä Chomskyn hierarkia (ja muutakin) kieli säännöllinen LL(k) LR(1)
Lisätiedotjäsentäminen TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho 26. marraskuuta 2015 TIETOTEKNIIKAN LAITOS
TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 26. marraskuuta 2015 Sisällys Tunnistamis- ja jäsennysongelma Olkoon G = (N, Σ, P, S) kontekstiton kielioppi ja
Lisätiedotjäsentämisestä TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho 27. marraskuuta 2015 TIETOTEKNIIKAN LAITOS
TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 27. marraskuuta 2015 Sisällys Rekursiivisesti etenevä engl. recursive descent parsing Tehdään kustakin välikesymbolista
LisätiedotAttribuuttikieliopit
TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 3. toukokuuta 2011 Sisällys t Chomskyn hierarkia kieli säännöllinen kontekstiton kontekstinen rekursiivisesti
LisätiedotTIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho. 29. huhtikuuta 2011
TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 29. huhtikuuta 2011 Sisällys Chomskyn hierarkia kieli säännöllinen kontekstiton kontekstinen rekursiivisesti
Lisätiedotjäsennyksestä TIEA241 Automaatit ja kieliopit, syksy 2016 Antti-Juhani Kaijanaho 29. syyskuuta 2016 TIETOTEKNIIKAN LAITOS Kontekstittomien kielioppien
TIEA241 Automaatit ja kieliopit, syksy 2016 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 29. syyskuuta 2016 Sisällys Harjoitustehtävätilastoa Tilanne 29.9.2016 klo 8:41 (lähes kaikki kommentoitu) passed
LisätiedotICS-C2000 Tietojenkäsittelyteoria Kevät 2016
ICS-C2000 Tietojenkäsittelyteoria Kevät 2016 Kierros 6, 22. 26. helmikuuta Huom: arviointiviikolla 15. 19.2. ei ole laskuharjoituksia! Demonstraatiotehtävien ratkaisut D1: (a) Osoita, että seuraava yhteydetön
LisätiedotYhteydettömän kieliopin jäsennysongelma
Yhteydettömän kieliopin jäsennysongelma Yhteydettömän kieliopin jäsennysongelmalla tarkoitetaan laskentaongelmaa Annettu: yhteydetön kielioppi G, merkkijono w Kysymys: päteekö w L(G). Ongelma voidaan periaatteessa
LisätiedotTIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 12. kesäkuuta 2013
TIEA241 Automaatit ja kieliopit, kevät 2012 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 12. kesäkuuta 2013 Sisällys t Chomskyn hierarkia (ja vähän muutakin) kieli säännöllinen LL(k) LR(1) kontekstiton
Lisätiedot815338A Ohjelmointikielten periaatteet 2014-2015. Harjoitus 7 Vastaukset
815338A Ohjelmointikielten periaatteet 2014-2015. Harjoitus 7 Vastaukset Harjoituksen aiheena on funktionaalinen ohjelmointi Scheme- ja Haskell-kielillä. Voit suorittaa ohjelmat osoitteessa https://ideone.com/
LisätiedotOhjelmoinnin perusteet Y Python
Ohjelmoinnin perusteet Y Python T-106.1208 2.3.2009 T-106.1208 Ohjelmoinnin perusteet Y 2.3.2009 1 / 28 Puhelinluettelo, koodi def lue_puhelinnumerot(): print "Anna lisattavat nimet ja numerot." print
LisätiedotGeneeriset tyypit. TIES542 Ohjelmointikielten periaatteet, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos
Geneeriset tyypit TIES542 Ohjelmointikielten periaatteet, kevät 2007 Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 6. maaliskuuta 2007 Kysymys Mitä yhteistä on seuraavilla funktioilla?
LisätiedotTestaa: Vertaa pinon merkkijono syötteeseen merkki kerrallaan. Jos löytyy ero, hylkää. Jos pino tyhjenee samaan aikaan, kun syöte loppuu, niin
Yhteydettömien kielioppien ja pinoautomaattien yhteys [Sipser s. 117 124] Todistamme, että yhteydettömien kielioppien tuottamat kielet ovat tasan samat kuin ne, jotka voidaan tunnistaa pinoautomaatilla.
LisätiedotHaskell ohjelmointikielen tyyppijärjestelmä
Haskell ohjelmointikielen tyyppijärjestelmä Sakari Jokinen Helsinki 19. huhtikuuta 2004 Ohjelmointikielten perusteet - seminaarityö HELSINGIN YLIOPISTO Tietojenkäsittelytieteen laitos 1 Johdanto 1 Tyyppien
LisätiedotLuku 3. Listankäsittelyä. 3.1 Listat
Luku 3 Listankäsittelyä Funktio-ohjelmoinnin tärkein yksittäinen tietorakenne on lista. Listankäsittely on paitsi käytännöllisesti oleellinen aihe, se myös valaisee funktio-ohjelmoinnin ideaa. 3.1 Listat
LisätiedotIDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit
IDL - proseduurit 25. huhtikuuta 2017 Viimeksi käsiteltiin IDL:n interaktiivista käyttöä, mutta tämä on hyvin kömpelöä monimutkaisempia asioita tehtäessä. IDL:llä on mahdollista tehdä ns. proseduuri-tiedostoja,
Lisätiedot8.5 Takarekursiosta. Sanoimme luvun 8.3 foldl -esimerkissämme että
85 Takarekursiosta Sanoimme luvun 83 foldl -esimerkissämme että foldl :: (a -> b -> a) -> a -> [b] -> a foldl f z [] = z foldl f z (x:xs) = foldl f (f z x) xs olisi pelkkä silmukka Tämä johtuu siitä, että
LisätiedotATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014
18. syyskuuta 2014 IDL - proseduurit Viimeksi käsiteltiin IDL:n interaktiivista käyttöä, mutta tämä on hyvin kömpelöä monimutkaisempia asioita tehtäessä. IDL:llä on mahdollista tehdä ns. proseduuri-tiedostoja,
LisätiedotLaskennan mallit (syksy 2010) Harjoitus 8, ratkaisuja
582206 Laskennan mallit (syksy 2010) Harjoitus 8, ratkaisuja 1. Tarkastellaan yhteydetöntä kielioppia S SAB ε A aa a B bb ε Esitä merkkijonolle aa kaksi erilaista jäsennyspuuta ja kummallekin siitä vastaava
LisätiedotLisää pysähtymisaiheisia ongelmia
Lisää pysähtymisaiheisia ongelmia Lause: Pysähtymättömyysongelma H missä H = { w111x w validi koodi, M w ei pysähdy syötteellä x } ei ole rekursiivisesti lueteltava. Todistus: Pysähtymisongelman komplementti
LisätiedotITKP102 Ohjelmointi 1 (6 op)
ITKP102 Ohjelmointi 1 (6 op) Tentaattori: Antti-Jussi Lakanen 7. huhtikuuta 2017 Vastaa kaikkiin tehtäviin. Tee jokainen tehtävä erilliselle konseptiarkille. Kirjoittamasi luokat, funktiot ja aliohjelmat
LisätiedotTIEA341 Funktio-ohjelmointi 1, kevät 2008
TIEA34 Funktio-ohjelmointi, kevät 2008 Luento 3 Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 2. tammikuuta 2008 Ydin-Haskell: Syntaksi Lausekkeita (e) ovat: nimettömät funktiot: \x
LisätiedotYhteydettömät kieliopit [Sipser luku 2.1]
Yhteydettömät kieliopit [ipser luku 2.1] Johdantoesimerkkinä tarkastelemme kieltä L = { a n b m a n n > 0, m > 0 }, joka on yhteydetön (mutta ei säännöllinen). Vastaavan kieliopin ytimenä on säännöt eli
Lisätiedot6 Algebralliset tietotyypit
Nyt voitaisiin kirjoittaa instance Functor Set where type Inv Set e = (Ord e) fmap = map jossa metodin tyyppi onkin nyt fmap :: (Ord a,ord b) => (a -> b) -> Set a -> Set b joka onkin nyt samaa tyyppiä
LisätiedotTIEA341 Funktio-ohjelmointi 1, kevät 2008
TIEA341 Funktio-ohjelmointi 1, kevät 2008 Luento 14: Monadit Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 21. tammikuuta 2008 Tyyppien tyypit eli luonteet engl. kind tyyppinimet, kuten
LisätiedotTIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 16. helmikuuta 2012
TIEA241 Automaatit ja kieliopit, kevät 2012 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 16. helmikuuta 2012 Sisällys t Sisällys t Chomskyn hierarkia kieli säännöllinen kontekstiton kontekstinen rekursiivisesti
LisätiedotTyyppiluokat II konstruktoriluokat, funktionaaliset riippuvuudet. TIES341 Funktio-ohjelmointi 2 Kevät 2006
Tyyppiluokat II konstruktoriluokat, funktionaaliset riippuvuudet TIES341 Funktio-ohjelmointi 2 Kevät 2006 Alkuperäislähteitä Philip Wadler & Stephen Blott: How to make ad-hoc polymorphism less ad-hoc,
LisätiedotDemo 7 ( ) Antti-Juhani Kaijanaho. 9. joulukuuta 2005
Demo 7 (14.12.2005) Antti-Juhani Kaijanaho 9. joulukuuta 2005 Liitteenä muutama esimerkki Ydin-Haskell-laskuista. Seuraavassa on enemmän kuin 12 nimellistä tehtävää; ylimääräiset ovat bonustehtäviä, joilla
Lisätiedot11.4. Context-free kielet 1 / 17
11.4. Context-free kielet 1 / 17 Määritelmä Tyypin 2 kielioppi (lauseyhteysvapaa, context free): jos jokainenp :n sääntö on muotoa A w, missäa V \V T jaw V. Context-free kielet ja kieliopit ovat tärkeitä
LisätiedotTIEA341 Funktio-ohjelmointi 1, kevät 2008
TIEA341 Funktio-ohjelmointi 1, kevät 2008 Luento 5 Ympärysmitta. Puut. Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 21. tammikuuta 2008 CASE: YMPÄRYSMITTA Lasketaan kuvioiden ympärysmittoja
LisätiedotSe mistä tilasta aloitetaan, merkitään tyhjästä tulevalla nuolella. Yllä olevassa esimerkissä aloitustila on A.
Tehtävä. Tämä tehtävä on aineistotehtävä, jossa esitetään ensin tehtävän teoria. Sen jälkeen esitetään neljä kysymystä, joissa tätä teoriaa pitää soveltaa. Mitään aikaisempaa tehtävän aihepiirin tuntemusta
LisätiedotAbstraktit tietotyypit. TIEA341 Funktio ohjelmointi 1 Syksy 2005
Abstraktit tietotyypit TIEA341 Funktio ohjelmointi 1 Syksy 2005 Data abstraktio Abstraktio on ohjelmoinnin tärkein väline Data abstraktio abstrahoi dataa Abstrakti tietotyyppi Koostuu kolmesta asiasta:
LisätiedotOngelma(t): Miten jollakin korkeamman tason ohjelmointikielellä esitetty algoritmi saadaan suoritettua mikro-ohjelmoitavalla tietokoneella ja siinä
Ongelma(t): Miten jollakin korkeamman tason ohjelmointikielellä esitetty algoritmi saadaan suoritettua mikro-ohjelmoitavalla tietokoneella ja siinä olevilla komponenteilla? Voisiko jollakin ohjelmointikielellä
LisätiedotVaihtoehtoinen tapa määritellä funktioita f : N R on
Rekursio Funktio f : N R määritellään yleensä antamalla lauseke funktion arvolle f (n). Vaihtoehtoinen tapa määritellä funktioita f : N R on käyttää rekursiota: 1 (Alkuarvot) Ilmoitetaan funktion arvot
Lisätiedot5.3 Laskimen muunnelmia 5.3. LASKIMEN MUUNNELMIA 57
5.3. LASKIMEN MUUNNELMIA 57 Samaan sarjaan kuuluu seuraavakin funktio, jonka määritelmä esittelee muutenkin hyödyllisen tavan kirjoittaa ohjelmia: getline :: IO String getline = getchar λc case c of \n
Lisätiedoton rekursiivisesti numeroituva, mutta ei rekursiivinen.
6.5 Turingin koneiden pysähtymisongelma Lause 6.9 Kieli H = { M pysähtyy syötteellä w} on rekursiivisesti numeroituva, mutta ei rekursiivinen. Todistus. Todetaan ensin, että kieli H on rekursiivisesti
LisätiedotLuonnollisen päättelyn luotettavuus
Luonnollisen päättelyn luotettavuus Luotettavuuden todistamiseksi määrittelemme täsmällisesti, milloin merkkijono on deduktio. Tässä ei ole sisällytetty päättelysääntöihin iteraatiosääntöä, koska sitä
LisätiedotRekursio. Funktio f : N R määritellään yleensä antamalla lauseke funktion arvolle f (n). Vaihtoehtoinen tapa määritellä funktioita f : N R on
Rekursio Funktio f : N R määritellään yleensä antamalla lauseke funktion arvolle f (n). Vaihtoehtoinen tapa määritellä funktioita f : N R on käyttää rekursiota: Rekursio Funktio f : N R määritellään yleensä
LisätiedotTIES542 kevät 2009 Tyyppijärjestelmän laajennoksia
TIES542 kevät 2009 Tyyppijärjestelmän laajennoksia Antti-Juhani Kaijanaho 16. helmikuuta 2009 Tyypitetyt ohjelmointikielet sisältävät paljon muitakin konstruktioita kuin yksinkertaisesti tyypitetyn lambda-kielen,
LisätiedotSisällys. 12. Näppäimistöltä lukeminen. Yleistä. Yleistä 12.1 12.2 12.3 12.4
Sisällys 12. Näppäimistöltä lukeminen Arvojen lukeminen näppäimistöltä yleisesti. Arvojen lukeminen näppäimistöltä Java-kielessä.. Luetun arvon tarkistaminen. Tietovirrat ja ohjausmerkit. Scanner-luokka.
LisätiedotYdin-Haskell Tiivismoniste
Ydin-Haskell Tiivismoniste Antti-Juhani Kaijanaho 8. joulukuuta 2005 1 Abstrakti syntaksi Päätesymbolit: Muuttujat a, b, c,..., x, y, z,... Tyyppimuuttujat α, β, γ,... Koostimet (data- ja tyyppi-) C, D,...,
LisätiedotM =(K, Σ, Γ,, s, F ) Σ ={a, b} Γ ={c, d} = {( (s, a, e), (s, cd) ), ( (s, e, e), (f, e) ), (f, e, d), (f, e)
Tik-79.148 Kevät 2001 Tietojenkäsittelyteorian perusteet Laskuharjoitus 7 Demonstraatiotehtävien ratkaisut 1. Pinoautomaatti M = K Σ Γ s F missä K Σ s ja F on määritelty samalla tavalla kuin tilakoneellekin.
LisätiedotTIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho. 19. tammikuuta 2012
TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 19. tammikuuta 2012 Sisällys Sisällys Muistathan A B -konstruktion 0 k 1 i 2 s 3 s 4 a 5 0 k 1 o 2 i 3 r 4
Lisätiedot811120P Diskreetit rakenteet
811120P Diskreetit rakenteet 2016-2017 ari.vesanen (at) oulu.fi 5. Rekursio ja induktio Rekursio tarkoittaa jonkin asian määrittelyä itseensä viittaamalla Tietojenkäsittelyssä algoritmin määrittely niin,
LisätiedotOhjelmoinnin perusteet Y Python
Ohjelmoinnin perusteet Y Python T-106.1208 28.2.2011 T-106.1208 Ohjelmoinnin perusteet Y 28.2.2011 1 / 46 Ohjelmointiprojektin vaiheet 1. Määrittely 2. Ohjelman suunnittelu (ohjelman rakenne ja ohjelman
LisätiedotAlgoritmit 1. Luento 3 Ti Timo Männikkö
Algoritmit 1 Luento 3 Ti 17.1.2017 Timo Männikkö Luento 3 Algoritmin analysointi Rekursio Lomituslajittelu Aikavaativuus Tietorakenteet Pino Algoritmit 1 Kevät 2017 Luento 3 Ti 17.1.2017 2/27 Algoritmien
LisätiedotOperaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta
C++ - perusteet Java-osaajille luento 5/7: operaattoreiden ylikuormitus, oliotaulukko, parametrien oletusarvot, komentoriviparametrit, constant, inline, Operaattoreiden ylikuormitus Operaattoreiden kuormitus
LisätiedotZeon PDF Driver Trial
Matlab-harjoitus 2: Kuvaajien piirto, skriptit ja funktiot. Matlabohjelmoinnin perusteita Numeerinen integrointi trapezoidaalimenetelmällä voidaan tehdä komennolla trapz. Esimerkki: Vaimenevan eksponentiaalin
LisätiedotELM GROUP 04. Teemu Laakso Henrik Talarmo
ELM GROUP 04 Teemu Laakso Henrik Talarmo 23. marraskuuta 2017 Sisältö 1 Johdanto 1 2 Ominaisuuksia 2 2.1 Muuttujat ja tietorakenteet...................... 2 2.2 Funktiot................................
LisätiedotOhjelmoinnin perusteet Y Python
Ohjelmoinnin perusteet Y Python T-106.1208 1.3.2010 T-106.1208 Ohjelmoinnin perusteet Y 1.3.2010 1 / 36 Monikko Monikko (engl. tuple) muistuttaa listaa, mutta monikon sisältöä ei voi muuttaa sen jälkeen,
LisätiedotOhjelmoinnin perusteet Y Python
Ohjelmoinnin perusteet Y Python T-106.1208 2.3.2011 T-106.1208 Ohjelmoinnin perusteet Y 2.3.2011 1 / 39 Kertausta: tiedoston avaaminen Kun ohjelma haluaa lukea tai kirjoittaa tekstitiedostoon, on ohjelmalle
LisätiedotITKP102 Ohjelmointi 1 (6 op)
ITKP102 Ohjelmointi 1 (6 op) Tentaattori: Antti-Jussi Lakanen 22. huhtikuuta 2016 Vastaa kaikkiin tehtäviin. Tee jokainen tehtävä erilliselle konseptiarkille! Kirjoittamasi luokat, funktiot ja aliohjelmat
LisätiedotICS-C2000 Tietojenkäsittelyteoria Kevät 2016
ICS-C2000 Tietojenkäsittelyteoria Kevät 206 Kierros 0, 2. 24. maaliskuuta Huom! Perjantaina 25. maaliskuuta ei ole laskareita (pitkäperjantai), käykää vapaasti valitsemassanne ryhmässä aiemmin viikolla.
LisätiedotLuento 5. Timo Savola. 28. huhtikuuta 2006
UNIX-käyttöjärjestelmä Luento 5 Timo Savola 28. huhtikuuta 2006 Osa I Shell-ohjelmointi Ehtolause Lausekkeet suoritetaan jos ehtolausekkeen paluuarvo on 0 if ehtolauseke then lauseke
LisätiedotTIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 8. syyskuuta 2016
TIEA241 Automaatit ja kieliopit, syksy 2016 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 8. syyskuuta 2016 Sisällys a https://tim.jyu.fi/view/kurssit/tie/ tiea241/2016/videoiden%20hakemisto Matemaattisen
Lisätiedotja λ 2 = 2x 1r 0 x 2 + 2x 1r 0 x 2
Johdatus diskreettiin matematiikkaan Harjoitus 4, 7.10.2015 1. Olkoot c 0, c 1 R siten, että polynomilla r 2 c 1 r c 0 on kaksinkertainen juuri. Määritä rekursioyhtälön x n+2 = c 1 x n+1 + c 0 x n, n N,
LisätiedotRekursiolause. Laskennan teorian opintopiiri. Sebastian Björkqvist. 23. helmikuuta Tiivistelmä
Rekursiolause Laskennan teorian opintopiiri Sebastian Björkqvist 23. helmikuuta 2014 Tiivistelmä Työssä käydään läpi itsereplikoituvien ohjelmien toimintaa sekä esitetään ja todistetaan rekursiolause,
LisätiedotTaas laskin. TIES341 Funktio ohjelmointi 2 Kevät 2006
Taas laskin TIES341 Funktio ohjelmointi 2 Kevät 2006 Rakennepuutyyppi data Term = C Rational T F V String Term :+: Term Term : : Term Term :*: Term Term :/: Term Term :==: Term Term :/=: Term Term :
Lisätiedotuv n, v 1, ja uv i w A kaikilla
2.8 Säännöllisten kielten rajoituksista Kardinaliteettisyistä on oltava olemassa (paljon) ei-säännöllisiä kieliä: kieliä on ylinumeroituva määrä, säännöllisiä lausekkeita vain numeroituvasti. Voidaanko
LisätiedotSäännöllisten kielten sulkeumaominaisuudet
Säännöllisten kielten sulkeumaominaisuudet Osoitamme nyt, että säännöllisten kielten joukko on suljettu yhdisteen, konkatenaation ja tähtioperaation suhteen. Toisin sanoen jos A ja B ovat säännöllisiä,
Lisätiedot1. Universaaleja laskennan malleja
1. Universaaleja laskennan malleja Laskenta datan käsittely annettuja sääntöjä täsmällisesti seuraamalla kahden kokonaisluvun kertolasku tietokoneella, tai kynällä ja paperilla: selvästi laskentaa entä
LisätiedotOhjelmoinnin peruskurssien laaja oppimäärä
Ohjelmoinnin peruskurssien laaja oppimäärä Luento 7: Funktionaalista ohjelmointia (mm. SICP 3.5) Riku Saikkonen 13. 11. 2012 Sisältö 1 Laiskaa laskentaa: delay ja force 2 Funktionaalinen I/O 3 Funktionaalista
Lisätiedotb) Määritä myös seuraavat joukot ja anna kussakin tapauksessa lyhyt sanallinen perustelu.
Johdatus yliopistomatematiikkaan Helsingin yliopisto, matematiikan ja tilastotieteen laitos Kurssikoe 23.10.2017 Ohjeita: Vastaa kaikkiin tehtäviin. Ratkaisut voi kirjoittaa samalle konseptiarkille, jos
LisätiedotTIEA341 Funktio-ohjelmointi 1, kevät 2008
TIEA341 Funktio-ohjelmointi 1, kevät 2008 Luento 4 Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 17. tammikuuta 2008 Modulin viimeistelyä module Shape ( Shape ( Rectangle, E l l i p
LisätiedotOhjelmoinnin perusteet Y Python
Ohjelmoinnin perusteet Y Python T-106.1208 1.4.2009 T-106.1208 Ohjelmoinnin perusteet Y 1.4.2009 1 / 56 Tentti Ensimmäinen tenttimahdollisuus on pe 8.5. klo 13:00 17:00 päärakennuksessa. Tämän jälkeen
LisätiedotRekursiiviset palautukset [HMU 9.3.1]
Rekursiiviset palautukset [HMU 9.3.1] Yleisesti sanomme, että ongelma P voidaan palauttaa ongelmaan Q, jos mistä tahansa ongelmalle Q annetusta ratkaisualgoritmista voidaan jotenkin muodostaa ongelmalle
LisätiedotITKP102 Ohjelmointi 1 (6 op)
ITKP102 Ohjelmointi 1 (6 op) Tentaattori: Antti-Jussi Lakanen 20. huhtikuuta 2018 Vastaa kaikkiin tehtäviin. Tee kukin tehtävä omalle konseptiarkille. Noudata ohjelmointitehtävissä kurssin koodauskäytänteitä.
LisätiedotS BAB ABA A aas bba B bbs c
T-79.148 Kevät 2003 Tietojenkäsittelyteorian perusteet Harjoitus 8 Demonstraatiotehtävien ratkaisut 4. Tehtävä: Laadi algoritmi, joka testaa onko annetun yhteydettömän kieliopin G = V, Σ, P, S) tuottama
LisätiedotTIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 2. helmikuuta 2012
TIEA241 Automaatit ja, kevät 2012 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 2. helmikuuta 2012 Sisällys Sisällys Chomskyn hierarkia kieli säännöllinen kontekstiton kontekstinen rekursiivisesti lueteltava
LisätiedotT Syksy 2002 Tietojenkäsittelyteorian perusteet Harjoitus 8 Demonstraatiotehtävien ratkaisut
T-79.148 Syksy 2002 Tietojenkäsittelyteorian perusteet Harjoitus 8 Demonstraatiotehtävien ratkaisut 4. Tehtävä: Laadi algoritmi, joka testaa onko annetun yhteydettömän kieliopin G = V, Σ, P, S tuottama
LisätiedotRajoittamattomat kieliopit (Unrestricted Grammars)
Rajoittamattomat kieliopit (Unrestricted Grammars) Laura Pesola Laskennanteorian opintopiiri 13.2.2013 Formaalit kieliopit Sisältävät aina Säännöt (esim. A -> B C abc) Muuttujat (A, B, C, S) Aloitussymboli
LisätiedotMatematiikan tukikurssi, kurssikerta 2
Matematiikan tukikurssi kurssikerta 1 Relaatioista Oletetaan kaksi alkiota a ja b. Näistä kumpikin kuuluu johonkin tiettyyn joukkoon mahdollisesti ne kuuluvat eri joukkoihin; merkitään a A ja b B. Voidaan
Lisätiedot12. Näppäimistöltä lukeminen 12.1
12. Näppäimistöltä lukeminen 12.1 Sisällys Arvojen lukeminen näppäimistöltä yleisesti. Arvojen lukeminen näppäimistöltä Java-kielessä. In-luokka. Luetun arvon tarkistaminen. Tietovirrat ja ohjausmerkit.
LisätiedotAlityypitys. TIES542 Ohjelmointikielten periaatteet, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos
Alityypitys TIES542 Ohjelmointikielten periaatteet, kevät 2007 Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 5. maaliskuuta 2007 Muistatko tietueet? {I 1 = E 1,..., I n = E n } : {I
LisätiedotRekursiivinen Derives on periaatteessa aivan toimiva algoritmi, mutta erittäin tehoton. Jos tarkastellaan esim. kieliopinpätkää
Rekursiivinen Derives on periaatteessa aivan toimiva algoritmi, mutta erittäin tehoton. Jos tarkastellaan esim. kieliopinpätkää S AB CA... A CB...... ja kutsua Derives(S, abcde), niin kutsu Derives(B,
LisätiedotTämän vuoksi kannattaa ottaa käytännöksi aina kirjoittaa uuden funktion tyyppi näkyviin, ennen kuin alkaa sen määritemää kirjoittamaan.
3.1. LISTAT 35 destaan pisteittäisesti: init :: [α] [α] init (x : []) = [] init (x : xs) = x : init xs Varuskirjastoon kuuluu myös funktiot take ja drop, jotka ottavat tai tiputtavat pois, funktiosta riippuen,
Lisätiedotvaihtoehtoja TIEA241 Automaatit ja kieliopit, syksy 2016 Antti-Juhani Kaijanaho 13. lokakuuta 2016 TIETOTEKNIIKAN LAITOS
TIEA241 Automaatit ja kieliopit, syksy 2016 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 13. lokakuuta 2016 Sisällys Harjoitustehtävätilastoa Tilanne 13.10.2016 klo 9:42 passed waiting redo submitters
LisätiedotTIEA341 Funktio-ohjelmointi 1, kevät 2008
TIEA341 Funktio-ohjelmointi 1, kevät 2008 Luento 11 Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 21. tammikuuta 2008 Listakomprehensio Uusi tapa luoda (ja muokata) listoja: [ lauseke
LisätiedotAS-0.1103 C-ohjelmoinnin peruskurssi 2013: C-kieli käytännössä ja erot Pythoniin
AS-0.1103 C-ohjelmoinnin peruskurssi 2013: C-kieli käytännössä ja erot Pythoniin Raimo Nikkilä Aalto-yliopiston sähkötekniikan korkeakoulu - Automaation tietotekniikan tutkimusryhmä 17. tammikuuta 2013
LisätiedotFORMAALI SYSTEEMI (in Nutshell): aakkosto: alkeismerkkien joukko kieliopin määräämä syntaksi: sallittujen merkkijonojen rakenne, formaali kuvaus
FORMAALI SYSTEEMI (in Nutshell): Formaali kieli: aakkosto: alkeismerkkien joukko kieliopin määräämä syntaksi: sallittujen merkkijonojen rakenne, formaali kuvaus esim. SSM:n tai EBNF:n avulla Semantiikka:
LisätiedotOhjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:
1 (7) Tiedon lukeminen näppäimistöltä Scanner-luokan avulla Miten ohjelma saa käyttöönsä käyttäjän kirjoittamaa tekstiä? Järjestelmässä on olemassa ns. syöttöpuskuri näppäimistöä varten. Syöttöpuskuri
Lisätiedot(0 1) 010(0 1) Koska kieli on yksinkertainen, muodostetaan sen tunnistava epädeterministinen q 0 q 1 q 2 q3
T-79.48 Tietojenkäsittelyteorian perusteet Tentti 25..23 mallivastaukset. Tehtävä: Kuvaa seuraavat kielet sekä säännölisten lausekkeiden että determinististen äärellisten automaattien avulla: (a) L = {w
Lisätiedot7/20: Paketti kasassa ensimmäistä kertaa
Ohjelmointi 1 / syksy 2007 7/20: Paketti kasassa ensimmäistä kertaa Paavo Nieminen nieminen@jyu.fi Tietotekniikan laitos Informaatioteknologian tiedekunta Jyväskylän yliopisto Ohjelmointi 1 / syksy 2007
Lisätiedot