JFO: Johdatus funktionaaliseen ohjelmointiin
|
|
- Antti Hovinen
- 8 vuotta sitten
- Katselukertoja:
Transkriptio
1 JFO: Johdatus funktionaaliseen ohjelmointiin Matti Nykänen Tietojenkäsittelytieteen laitos, Itä-Suomen yliopisto Lukuvuosi , III periodi Sisältö 1 Mitä funktionaalinen ohjelmointi on? 1 2 Lambda-laskennan perusteista α-konversio β-reduktio η-konversio Normalisointi δ-määritelmät Ohjelmoijan tekemät määritelmät Tyypitys Funktionaalisen ohjelmoinnin historiaa 19 4 Haskell-kieliset ohjelmat Syntaksista Atomiset perustietotyypit Totuusarvotyyppi Merkkityyppi Kokonaislukutyypit Liukulukutyypit Funktiot Paikalliset määritelmät Suluton syntaksi Rakenteiset tietotyypit Monikot Hahmonsovitus Listat Tulkkaamisesta ja kääntämisestä Tyyppisynonyymit Uusien tyyppien määrittely Uusvanhat tyypit Heikko päänormaalimuoto Valmiiden ohjelmamodulien käyttöönotto i
2 5 Monimuotoisuus Parametrinen monimuotoisuus Tyyppiluokat Automaattinen jäsenyys Jo määritellyn tyyppiluokan jäseneksi liittyminen Uuden tyyppiluokan määritteleminen Konstruktoriluokat Liittyvät tyypit Tyyppiperheet Rajoitelaji Algebralliset tietotyypit Haamutyypit Eksistentiaaliset tyypit Yleistetyt algebralliset tietotyypit Monadit Monadinen syntaksi IO-monadi Tuttuja tyyppejä monadeina Tilaperustaisen ohjelmoinnin monadi Monadimuuntimet Monadinen jäsennys Abstraktit tietotyypit Tuonnit Viennit Päämoduli Curryn ja Howardin vastaavuudesta 143 Viitteet R. Kent Dybvig. The Scheme Programming Language: ANSI Scheme. Prentice Hall, second edition, Graham Hutton. Programming in Haskell. Cambridge University Press, Oleg Kiselyov, Ken Shan, and Simon Peyton Jones. Fun with type functions (version 3). To appear in the proceedings of Tony Hoare s 75th birthday celebration, May URL FunWithTypeFuns. Miran Lipovačka. Learn You a Haskell for Great Good! No Starch Press, URL Simon Marlow. Haskell 2010 Language Report, July URL haskell.org/onlinereport/haskell2010/. Robin Milner, Mads Tofte, Robert Harper, and David MacQueen. Standard ML (Revised). The MIT Press, The Definition of ii
3 Dominic Orchard and Tom Schrijvers. Haskell type constraints unleashed. In Matthias Blume, Naoki Kobayashi, and Germán Vidal, editors, Proceedings of the 10th International Conference on Functional and Logic Programming (FLOPS 10), volume 6009 of Lecture Notes in Computer Science, pages Springer-Verlag, Bryan O Sullivan, Don Stewart, and John Goerzen. Real World Haskell. O Reilly, URL Simon Peyton Jones, editor. Haskell 98 Language and Libraries: the Revised Report. Cambridge University Press, URL Also available as Journal of Functional Programming special issue 13(1), Morten Heine Sørensen and Pawel Urzyczyn. Lectures on the Curry-Howard Isomorphism, volume 149 of Studies in Logic and the Foundations of Mathematics. Elsevier, Guy L. Steele. Common Lisp the Language. Digital Press, 2nd edition, URL Simon Thompson. Haskell: the Craft of Functional Programming. Addison Wesley, 3rd edition, iii
4 1 Mitä funktionaalinen ohjelmointi on? On olemassa useita erilaisia ohjelmointiparadigmoja (programming paradigms) koska on useita erilaisia tapoja mallintaa ohjelmointiongelmia, esimerkiksi: Proseduraalinen ohjelmointi jossa mallinnuksen voi ajatella keskittyvän ohjaamaan tietokonetta. Esimerkkikieli: C. Olio-ohjelmointi jossa mallinnus keskittyy löytämään ongelmassa esiintyviä olioita ja niiden välisiä suhteita. Esimerkkikieliä: Java, C++, Objective C,... Tietovuo-ohjelmointi jossa mallinnus keskittyy tiedon kulkuun eri komponenttien välillä. Esimerkki ohjelmointiympäristöstä: LabView. Logiikkaohjelmointi jossa ongelma mallinnetaan loogisina relaatioina ja ohjelman suoritus todistamisena. Esimerkkikieli: Prolog. Ohjelmointiparadigmat liittyvät mallinnukseen, joten ne ovat sinänsä ohjelmointikielistä riippumattomia, mutta jokaiselle paradigmalle on suunniteltu omia kieliään, joilla siten suunniteltuja ohjelmia olisi vaivatonta kirjoittaa. Tällä kurssilla esiteltävä paradigma on siis Funktionaalinen ohjelmointi jossa mallinnus keskittyy löytämään ongelmasta sellaisia tiedon muunnoksia jotka voidaan esittää matemaattisina funktioina annetuilta syötetiedoilta haluttihin tulostietoihin. Ohjelmoinnissa funktion matemaattisuus tarkoittaa sitä, että se ottaa sisään informaatiota vain parametreissaan antaa ulos informaatiota vain tuloksessaan, ja tämä tulos riippuu vain sisään tulleesta informaatiosta. Tilaperustainen ja tilaton ohjelmointi Mutta meillähän on jo tuttu aliohjelman käsite! Mitä uutta funktionaalinen ohjelmointi muka tuo? Aliohjelma ei välttämättä ole funktio, koska se ei välttämättä noudata tätä informaationkulkukuria, vaan se voi lukea muistista (tai käyttäjältä tai tiedostosta tai... ) jonkin ohjelmallisen muuttujan nykyisen arvon ja tämä on lisäinformaatiota joka ei näy sen argumenteissa! kirjoittaa tällaiselle muuttujalle uuden arvon jonka jälkeen tämän (tai jonkin toisen!) aliohjelman seuraava kutsukerta voikin palauttaa jonkin toisen arvon, vaikka sen saamat argumentit pysyisivätkin samoina! Hyvä ohjelmointapa kehottaa välttämään tällaisia aliohjelmia, koska ne vaikeuttavat ohjelman debuggausta ja muokkaamista. 1
5 Funktionaalisessa ohjelmoinnissa pyritään välttämään sellaisia jo suunnitteluvaiheessa. Yleisemmin eri ohjelmointiparadigmat voidaan ryhmitellä tilaperustaisiin joissa ohjelma sisältää sijoituslauseita (assignment statement) joka tämä = tuo 1 ensin laskee tuon lausekkeen (expression) arvon ja 2 sitten tallentaa sen tämän muistipaikan uudeksi sisällöksi. Ohjelman suoritus etenee askelin yhdestä muistin tilasta (eli sisällöstä) seuraavaan. Proseduraalinen ohjelmointi on tilaperustaista puhtaimmillaan: ohjelmoija kirjoittaa haluamansa askelsarjat. Useimmat olio-ohjelmointitavat ovat tilaperustaisia, joskin epäsuoremmin: oliolla on sisäinen tila jota sen metodit muokkaavat. tilattomiin joissa ohjelmoija ei käytäkään tällaista muokattavaa muistia ja ohjelman suoritus ei etenekään tällaisesta tilasta seuraavaan vaan jotenkin muuten. Funktionaalinen ohjelmointi on tilatonta: Muuttujaa määriteltäessä sille annetaan jokin arvo jota ei muuteta enää myöhemmin. Siten määrittely tämä = tuo antaa tämän nimen tuon lausekkeen arvolle. Ohjelman suoritus etenee laskemalla lausekkeiden arvoja (eikä tottelemalla lausein ilmaistuja käskyjä). Funktionaalista ohjelmointia kutsutaankin myös soveltavaksi (applicative) koska sen suoritusaskel on muotoa sovella (apply) tätä funktiota tuohon argumenttiin. Viittausten läpikuultavuus Tämä arvon nimeäminen mahdollistaa sen, että voimme korvata kaikki tämän nimen esiintymät lähdekoodissa tuolla lausekkeella, joka määritteli sen arvon, eikä ohjelman laskema tulos siitä muutu. Jos pelkän nimeämisen sijaan käytämmekin (uudelleen)sijoituslausetta, niin tätä ei voi enää tehdä, koska tämä muistipaikka voi sisältää eri arvoja eri aikoina ja nämä arvot riippuvat ohjelman suoritushistoriasta, joka ei näy pelkästä lähdekoodista. Samoin jos tuo lauseke sisältää sivuvaikutuksia, kuten syötteen lukemista käyttäjältä. Viittausten läpikuultavuus (referential transparency) tarkoittaa sitä, että ohjelmointikieli sallii tämän korvaamisen, koska tuon lausekkeen arvo on aina sama (eikä se riipu esimerkiksi siitä milloin se lasketaan). 2
6 Viittausten läpikuultavuus pätee pätee funktionaalisesti tehdyn ohjelmakoodin kaikissa niissä osissa, joissa ei käytetä kielen tilaperustaisia piirteitä voi rikkoutua tilaperustaisen ohjelmakoodin missä tahansa osassa, koska missä tahansa voi taphtua esimerkiksi uudelleensijoitus. Jos ohjelmoija voi luottaa viittausten läpikuultavuuteen, niin hän voi käsitellä ohjelmakoodiaan matemaattisin menetelmin. Matemaattisten menetelmien soveltaminen tilaperustaiseen ohjelmointiin on epäsuorempaa: Voidaan käsitellä ohjelman loogista spesifikaatiota, eli loogisia väitteitä siitä mitä ohjelma tekee mutta ei itse ohjelmakoodia. Funktionaalinen ohjelmoija voi käsitellä koodiaan erityisesti algebrallisesti: Hän voi muodostaa koodiyhtälöitä koodi koodi (1) jotka tarkoittavat ohjelmakoodinpätkä koodi laskee kaikilla muuttujiensa arvoilla saman tuloksen kuin ohjelmakoodinpätkä koodi. Viittausten läpikuultavuuden nojalla hän voi korvata ohjelmakoodinpätkän toisella koodiyhtälönsä (1) mukaisesti, eikä ohjelman muu koodi mene siitä rikki. Tilallisessa ohjelmoinnissa koodiyhtälöitä (1) on hankalampi muodostaa ja tarkistaa: Päteekö esimerkiksi koodiyhtälö 1 y = 1 x; 2 if x y 3 P 4 else Q 1 y = 1 x; 2 P kaikilla kokonaisluvun sisältävillä muistipaikoilla x ja y? (P ja Q ovat mielivaltaista ohjelmakoodia.) Ei jos x ja y ovat sama muistipaikka! (Muuten kyllä.) Se voi puolestaan riippua (ei vain näistä ohjelmakoodinpätkistä vaan myös) ohjelman muusta tilasta ja suoritushistoriasta: Olkoot x ja y viittaukset a[i] ja a[j] samaan taulukkoon a. Tämän vuoksi tilallisessa ohjelmoinnissa tavallisesti todistetaankin konkreettinen koodi toimivaksi eikä yritetä lausua abstraktimpia yleisperiaatteita kaikilla x ja y pätee Tällaisessa todistuksessa käsitellään loogisia väitteitä jotka koskevat suorituksen tilaa ohjelmakoodin eri kohdissa väitteet ilmaistaan eri kielellä kuin koodi. 3
7 Miksi funktionaalinen ohjelmointi on kiinnostavaa? Ohjelmistotekniikan näkökulmasta: Ohjelmoijan kannattaa tuntea erilaisia ohjelmointiparadigmoja, jotta kohdatessaan erilaisia ohjelmointiongelmia hän osaa valita kuhunkin niistä sopivimman. Jos sinulla on vain vasara, niin kaikki näyttää naulalta. Funktionaalinen ohjelmointi painottaa erittäin tiukkoja rajapintoja. Millaista sellainen ohjelmointi on vapauteen tottuneelle ohjelmoijalle? Funktionaalinen ohjelmointi painottaa koodin uudelleenkäytettävyyttä erilaisten abstraktioiden kautta ja tarjoaa siihen välineitä. Funktionaalinen ohjelmointi kannustaa näin kokoamaan ohjelmat monesta pienestä (aiemmin ohjelmoidusta) funktiosta. Teorian näkökulmasta: LAP-kurssilla mainitut Turingin koneet ovat hyvä teoreettinen malli sille, miten mekaaninen tilaperustainen laskenta etenee ja mitä sillä voidaan laskea. Turingin koneiden teoria ei kuitenkaan tarjoa kovin luontevaa mallia sille, mikä ohjelma on itsenäisenä käsitteenä erillään sitä suorittavasta laitteesta. Funktionaalisen ohjelmoinnin taustalla oleva λ-laskenta voidaan nähdä tällaisena ohjelmien teoriana. Funktionaalinen ohjelmointi on λ-laskennan kautta ohjelmaparadigmoista lähimpänä formaalin logiikan todistusteoreettisia ideoita. Funktionaalinen ohjelmanpätkä ja sen oikeellisuustodistus voidaan ilmaista samalla kielellä. Ohjelmointikielten näkökulmasta: Tämä λ-laskenta on myös yleisen ohjelmointikielten teorian perusväline. Se soveltuu funktionaalisen ohjelmoinnin lisäksi muihinkin ohjelmointiparadigmoihin. Esimerkiksi tilaperustainen ohjelmointikieli voidaan mallintaa muokattavan muistin ja λ-laskennan yhdistelmänä. Siinä yhdistelmässä λ-laskenta tarjoaa aliohjelman ja muuttujannimen käsitteet, mutta ne nimeävätkin muistipaikkoja. Ohjelmointikielten uusia ideoita esitelläänkin koekäyttöön usein ensin johonkin funktionaaliseen kieleen, koska niissä matka teoriasta käytäntöön on lyhyt. Siksi ohjelmointikielistä kiinnostuneen kannattaa tuntea myös λ-laskentaa ja funktionaalista ohjelmointia, vaikka pääkiinnostus olisikin muissa ohjelmointiparadigmoissa. Millaiseen ohjelmointitehtävään funktionaalinen ohjelmointi soveltuu erityisen hyvin? Sellaiseen, jossa saatuun syötteeseen kohdistetaan jokin (monivaiheinen) muunnos, ja lopuksi ilmoitetaan muunnoksen tuottama tulos. 4
8 Sellaiseen, jossa tällä muunnettavalla informaatiolla on lista- tai puurakenne. Tällaista on esimerkiksi XML-dokumenttien käsittely. Tällaista on myös ohjelmointikielen kääntäminen: 1 Selausvaihe lukee lähdekooditiedoston ja muuntaa sen alkiojonoksi. 2 Jäsennysvaihe muuntaa alkiojonon sitä vastaavaksi jäsennyspuuksi kieliopin mukaisesti. 3 Seuraavat työvaiheet kuten tyypintarkistus jne. käyvät läpi jäsennyspuuta ja lisäävät siihen uutta informaatiota. 4 Lopuksi koodingenerointivaihe käy läpi lopullisen jäsennyspuun ja tulostaa sitä vastaavan konekoodin tulostiedostoon. Millaiseen ohjelmointitehtävään funktionaalinen ohjelmointi ei sovellu kovin hyvin? Sellaiseen, jossa halutaankin tuottaa jokin vaikutus (eikä tulosta). Esimerkiksi ikkunoiva käyttöliittymä. Sellaiseen, jossa keskeisessä osassa on jokin tila jota luetaan ja päivitetään eri tavoin monessa eri kohdassa ohjelmaa koska silloin tämä tila toimii informaationsiirtokanavana ohjelman osien välillä. Esimerkiksi jonkin tietokannan ympärille rakennettu ohjelma. Sellaiseen, jossa on seurattava tarkasti suoitusajan ja muistin kulutusta. Esimerkiksi laiteläheinen ohjelmointi. Tällaisissa ohjelmointitehtävissä funktionaalisen ohjelmoinnin haasteena onkin modulaarisuus: Suunnitella sellaiset rajapinnat, joilla ohjelma voidaan osittaa funktionaaliseen ja ei-funktionaaliseen osaan sekä niiden väliseen kommunikointiin. 2 Lambda-laskennan perusteista Esitellään seuraavaksi λ-laskennan pääperiaatteet. Valitsemme ohjelmoijan näkökulman aiheeseen: Meille λ-laskenta on abstrakti melli ohjelmointikielelle. Tämä abstraktius tarkoittaa, että esitämme ohjelmointikielen ja sen toiminnan puhtaasti mekaanisena systeeminä joka käsittelee symbolisia lausekkeita. Me emme siis vetoa mihinkään taustalla olevaan tietokoneeseen (tai sellaisen abstraktiin malliin malliin, kuten Turingin koneisiin) jossa olisi muistipaikkoja joita ohjelman suoritus muokkaisi edetessään, tai muuhun sellaiseen. 5
9 Termien syntaksi Otetaan käyttöön joukko X Muuttujasymboleita. Tyypittömän λ-laskennan Termien kontekstiton kielioppi: Termi Muuttuja (2) λmuuttuja.termi (3) (Termi 1 Termi 2 ) (4) Termistä voi jättää pois sisäkkäisiä sulkuja sopimalla että ja niin edelleen. (Termi 1 Termi 2 Termi 3 ) on sama kuin ((Termi 1 Termi 2 ) Termi 3 ) (5) Sulkuja voi jättää pois myös sopimalla, että λ-abstraktion (3) sisällä oleva Termi on mahdollisimman laaja eli että sen Muuttuja näkyy mahdollisimman laajasti. Termiin voi myös lisätä sulkuja (...) selkeyttämään sen rakennetta lukijalle. Termien merkitysoppi Ohjelmoijina ajattelemme Termiä lähdekoodin pätkänä: λ-abstraktio (3) määrittelee funktion, jonka ainoa formaali parametri on tämä Muuttuja ja tulos on tuon Termin arvo tälle Muuttujalle annetulla argumenttiarvolla. Muuttuja (2) viittaa tällaiselle formaalille parametrille annettuun argumenttiarvoon. Soveltaminen (4) kutsuu sitä funktiota jota Termi 1 esittää sillä argumenttiarvolla jota Termi 2 esittää. Muuttujan x esiintymä Termissä f on vapaa jos se ei ole minkään λ-abstraktion (3) λx.g sisällä, eli sen Termissä g sidottu muuten lähimpään sen sisältävään λ-abstraktioon (3) λx.g. Siten λ-abstraktioilla (3) on tuttu leksikaalinen näkyvyys (lexical scope). Termi on suljettu (closed) jos kaikki siinä olevat Muuttujan esiintymät ovat sidottuja. Määritellään λ-laskenta koodiyhtälöinä Termi Termi. Sellainen tarkoittaa intuitiivisesti, että kun valitaan mitkä tahansa Termit arvoiksi niissä vapaina esiintyville muuttujille, niin kummankin antama tulos on sama. Idea on sama kuin koodiyhtälössä (1). Emme kuitenkaan ole vielä määritelleet, mitä tarkoitamme tuloksella... 6
10 Määritellään keskeinen apuoperaatio f[x a] = Termi f jossa Muuttujan x jokainen vapaa esiintymä on korvattu Termillä a induktiolla yli Termin f rakenteen seuraavasti: { a kun y on sama Muuttuja kuin x y[x a] = y kun y on eri Muuttuja kuin x. Ylemmässä haarassa tapahtuu se korvaus. { λy.g kun y on sama Muuttuja kuin x λy.g[x a] = λy.(g[x a]) kun y on eri Muuttuja kuin x. Alemmassa haarassa vaaditaan myös, ettei Muuttuja y esiinny vapaana Termissä a, jotta tämä korvaus olisi laillinen. 2.1 α-konversio (g h)[x a] = ((g[x a]) (h[x a])). Ensimmäinen koodiyhtälömme on α-konversio (α-conversion): λx.f λy.(f[x y]) (α) jossa tämän korvauksen pitää olla laillinen. Laillisuus voidaan järjestää valitsemalla Muuttuja y sopivasti. Tämä α-konversio sanoo, että formaalille parametrille annetulla nimellä ei ole merkitystä. Ohjelmointikielen toteutuksessa ei tarvita tätä α-konversiota, koska käännetyssä koodissa ei enää ole Muuttujannimiä. Muistisääntö: α is about the alphabet. 2.2 β-reduktio Keskeisin koodiyhtälömme on β-reduktio (β-reduction): ((λx.f) g) f[x g] (β) jossa tämän korvauksen pitää olla laillinen. Vasemmalta oikealle luettuna se määrittelee mitä funktiokutsu tarkoittaa: Formaali parametri x korvataan argumentilla g kutsuttavan funktion rungossa f. Laillisuus voidaan aina järjestää tekemällä Termiin f ensin tarvittavat α- konversiot. Sellainen lauseke, joka on samaa muotoa kuin sen vasen puoli, on nimeltään redeksi (redex, reducible expression). Tämä β-reduktio on funktionaalisen ohjelman suorituksen perusaskel. Muistisääntö: β is the basic computation step. 7
11 Kuritus Vailla λ-laskennassa määritelläänkin vain 1-parametriset funktiot, niin siinä on myös 2-parametriset funktiot: ((λx.λy.f) g h) (((λx.(λy.f)) g) h) kun sulut merkitään näkyviin. Tämä selittää myös miksi näkymättömät sulut sovittiin näin päin. kun tehdään 1. β-reduktio. kun tehdään 2. β-reduktio. ((λy.(f[x g])) h) f[x g][y h] Siten 1. parametri x korvautui 1. argumentilla g, ja 2. parametri y korvautui 2. argumentilla h, kuten pitääkin. Näin k-parametrinen funktio voidaan kirjoittaa 1-parametrisena funktiona, jonka runko on (k 1)-parametrinen funktio, ja niin edelleen. Ääritapauksena vakiot ovat 0-parametrisia funktioita. Tämän tekniikan nimi on Currying. Suomeksi voisimme kutsua sitä kuritukseksi. Kuritus on yleistä funktionaalisessa ohjelmoinnissa, jossa on luontevaa määritellä sellainen funktio, joka palauttaa tuloksenaan toisen funktion. Funktionaaliset ohjelmointikielet mahdollistavat kurituksen, mutta useimmissa niistä sitä käytetään silti harvoin. Ohjelmointikielessä Haskell kuritusta käytetään hyvin usein. Kuritus lisää omalta osaltaan koodin modulaarisuutta, koska esimerkiksi osittain kutsuttu funktio ((λx.λy.f) g) voi olla hyödyllinen eri arvoilla h 1, h 2, h 3, η-konversio Kolmas koodiyhtälömme on η-konversio (η-conversion): f λx.(f x) jos Muuttuja x ei esiinny vapaana Termissä f. (η) Sen oikealla puolella voidaan ehkä tehdä sellaisia β-reduktioita, joita sen vasemmalla puolella ei voida tehdä (koska siellä ei ole Muuttujaa x). Näin se ilmaisee ekstensionaalisuus-idean: Kaksi Termiä tarkoittavat samaa täsmälleen silloin kun ne antavat saman tuloksen kaikilla argumenteilla. Toisin sanoen, vain Termin tuloksilla on väliä, mutta ei sillä miten se on kirjoitettu. Vastakkainen idea olisi nimeltään intensionaalisuus. Muistisääntö: η is about extensionality. 8
12 2.4 Normalisointi Normaalimuodon (Normal Form, NF) idea on yleisesti: Olkoon meillä jokin kieli, jossa voimme ilmaista saman asian monin eri tavoin. Keskitymme yhteen tiettyyn tapaan, ja muunnamme eli normalisoimme muut ilmaukset sellaisiksi. Esimerkiksi propositio- eli lauselogiikassa voimme käyttää negaatioita ( ), konjunktioita ( ) ja disjunktioita ( ) monin eri tavoin ilmaisemaan saman loogisen väitteen. Konjunktiivisessa normaalimuodossa (Conjunctive NF, CNF) keskitymme ilmaisemaan väitteet seuraavasti: Disjunktio Konjunktio Literaali Konjunktio Konjunktio Disjunktio Literaali Literaali Konjunktio Propositiosymboli Propositiosymboli Koska tahdomme määritellä käsitteen Termin tulos niin silloin luonteva normaalimuoto on: Termi on β-normaalimuodossa täsmälleen silloin kun siinä ei ole yhtään redeksiä. Intuitiivisesti, kun se on loppuun saakka laskettu. Usein etuliite β- jätetäänkin pois, ja niin mekin teemme. Normalisointi voidaan ilmaista yleisellä tasolla seuraavana pseudokoodina: normalisoi f: 1 while Termissä f on yhä redeksejä 2 Valitse jokin niistä; 3 Korvaa se vastaavan β-reduktion oikealla puolella; 4 return f. Tämä on λ-ohjelmointikielemme toteutus epädeterministisenä (rivi 2) ja tulkattuna (rivi 3). Käytännössä ohjelmointikielet määritellään (useimmiten) deterministisinä ja ne voidaan toteuttaa kääntäjinä, joiden ei tarvitse etsiä ja korvata redeksejä. Termillä ei välttämättä ole normaalimuotoa. Esimerkiksi Termin normalisointi ei koskaan pääty. (λx.(x x)) (λx.(x x)) (6) Intuitiivisesti sellaiset Termit vastaavat päättymätöntä laskentaa. 9
13 normalisoinnin päättyminen voi riippua rivin 2 valinnoista. Esimerkiksi Termissä (λy.λz.z) (Termi (6)) (7) kutsuttava funktio ei käytä lainkaan parametriaan y, joten sen normalisointi pysähtyy, jos joskus valitaan se redeksi (eikä koko ajan Termiä (6)). Lause 1 (normaalimuodon yksikäsitteisyys). Jos Termillä on monia eri β-normaalimuotoja, niin ne saadaan täsmälleen samaan muotoon α-konversioilla. Siten voimme määritellä, että Termin tulos on sen (mikä tahansa) β-normaalimuoto, koska se on hyvin määritelty. Lause 1 osoittaa λ-laskennan kiinnostavuuden myös rinnakkaisuuden (parallelism) kannalta: Ohjelman lopputulos pysyy samana, vaikka sen eri osia β-redusoitaisiinkin yhtä aikaa. Kun λ-laskennalla mallinnetaan ohjelmointikieliä, niin silloin käytetäänkin jotakin heikkoa (Weak) normaalimuotoa β-normaalimuodon sijasta... jossa redeksin etsintä ei etene λ-abstraktion λx.g sisään sen alitermiin g... koska se on käännetty konekieliseksi aliohjelmaksi, ja sen alitermiin g muokkaaminen tarkoittaisi jo käännetyn konekoodin muokkaamista ja sellaista kutsutaan eikä muokata. Lause 2 (Turing-täydellisyys). Osittain määritelty funktio f : N N voidaan laskea Turingin koneella täsmälleen silloin kun f voidaan esittää tyypittömän λ-laskennan Terminä. Osittain määritelty (partially defined, partial) funktio g on sellainen, jonka tulosta ei olekaan määritelty jokaiselle mahdolliselle parametrin arvolle. Esimerkiksi g(n) = 1 n on sellainen, koska parametrin arvolla n = 0 sen tulosta 1 0 ei ole määritelty. Lauseen 2 vasenta puolta varten pitäisi ensin määritellä, mitä tarkoittaa että Turingin kone M laskee funktion f : Sovitaan luonnollisille luvuille N jokin esitystapa merkkijonoina, jotta Turingin koneet voivat lukea ja kirjoittaa niitä nauhoillaan. Kun moninauhainen M saa syötenauhallaan luvun n merkkijonona (ja sen mahdolliset työnauhat ovat aluksi tyhjiä) niin laskenta joko pysähtyy ja sen tulosnauhalla on arvo f(n) merkkjonona tai ei pysähdy jos arvoa f(n) ei ole määritelty. Kääntäen Lauseen 2 oikeaa puolta varten pitäisi määritellä, mitä tarkoittaa Termi h esittää funktion f : Sovitaan jokaiselle luonnolliselle luvulle n N jokin Termi n joka esittää sitä, jotta Termien voidaan ajatella laskevan luonnollisilla luvuilla. SovellusTermin normalisointi 10 (h n )
14 joko päättyy ja sen normaalimuoto on arvon f(n) esitys f(n) Terminä tai ei pääty kun arvoa f(n) ei ole määritelty. Lauseen 2 perusteella myös tyypittömien λ-termien normalisointi on eräs mekaanisen laskettavuuden (computability, eli sen mitä voidaan laskea) malli vaikka se ei olekaan lainkaan niin ilmeistä kuin Turingin koneilla. Toisin kuin Turingin koneilla, tässä mallissa on laiteriippumaton itsenäinen ohjelman käsite: Termit. Eräs yksinkertainen luonnollisten lukujen Termiesitys ovat Churchin numeraalit n = λf.λx.(f (f (f (... (f }{{} n kappaletta eli sovella n kertaa funktiota f argumenttiin x. Siten 0 = λf.λx.x Esimerkiksi niiden yhteenlasku on 1 = λf.λx.(f x) 2 = λf.λx.(f (f x)). m + n = λf.λx.(( n f) ( m f x)). x)... )))) Lause 3 (normaali sievennysjärjestys). Jos rivillä 2 valitaan aina vasemmanpuoleisin redeksi, niin silloin Termin normalisointi päättyy jos sillä on jokin normaalimuoto. Näin käy esimerkiksi Termillä (7). Tämä valinta tarkoittaa, että kun redeksin etsintä aloitetaan koko Termin juuresta, ja kohdataan soveltaminen (Termi 1 Termi 2 ) niin siitä jatketaan etsintää ensin Termi 1 stä, ja jos sieltä ei löydy, niin vasta sen jälkeen Termi 2 sta. Tätä valintaperiaatetta kutsutaan normaaliksi sievennysjärjestykseksi (normal order reduction). Päänormaalimuodossa (Head NF) redeksiä etsitään soveltamisessa vain Termi 1 stä, ja Termi 2 unohdetaan. Lauseen 3 mukaan valitsemalla aina mekaanisesti Mene aina (ensin) vasemmalle! löydetään aina sellainen redeksi, jonka valitseminen on oikein, koska sen valitseminen ei voi johtaa normalisointia mihinkään ikuiseen silmukkaan, joka olisi voitu välttää valitsemalla jokin muu redeksi sen sijasta. Siten oikean redeksin valintaan ei (ehkä yllättäen) tarvitakaan älykkyyttä, joten myös tyypittömien λ-termien normalisointi on eräs mekaanisen laskennan (computation, eli sen, miten voidaan laskea) malli vaikka se ei olekaan lainkaan niin ilmeistä kuin Turingin koneilla. 11
15 Lauseen 3 mukaan normaali sievennysjärjestys sallii koodiyhtälöiden vapaan käytön: Jos Termissä f korvataan jokin sen alitermi g jollakin toisella Termillä h jolla f h, niin Termin f normalisointi ei voi sen seurauksena muuttua pysähtyvästä pysähtymättömäksi. Muut sievennysjärjestykset eivät pysty takaamaan tätä! Normaalissa sievennysjärjestyksessä redeksi (funktio argumentti) suoritetaan niin, että argumenttia ei lasketakaan ennen kuin se lähetetään kutsuttavaan funktioon sen parametrina. Sen sijaan argumentti lasketaankin vain jos kutsuttava funktio tarvitsee sen arvoa. Esimerkiksi Termissä (7) kutsuttava funktio ei tarvitse argumenttinsa arvoa ja sen laskeminen johtaisikin ikuiseen silmukkaan. Tätä parametrinvälitystapaa kutsutaan nimellä CBN: Call by... Name jos argumentti lasketaan aina uudelleen jokaisen kerran kun funktio tarvitsee sen arvoa, koska se sai parametrinaan arvon nimen eli määritelmän. Need jos argumentti lasketaankin vain silloin kun funktio tarvitsee sen arvoa ensimmäisen kerran, ja se muistetaan myöhäisempiä kertoja varten. Tämtä kutsutaan myös laiskaksi (lazy) suoritukseksi, koska argumentti lasketaan vain jos sen arvoa tarvitaan, ja silloinkin vain kerran. Kun käytössä on Call by Name, niin β-reduktiot etenevät (λx.(x x)) ((λy.a y) b) ((λy.a y) b) ((λy.a y) b) (a b) ((λy.a y) b) (a b) (a b) koska argumenttilausekkeen kumpikin kopio β-redusoidaan erikseen. Kun käytössä on Call by Need, niin ne etenevät kuin (x x) jossa x on ((λy.a y) b) koska siinä muistetaan, että Muuttujan x molemmat esiintymät jakavat yhteisen argumenttilausekkeen. Sitten (x x) jossa x on (a b) koska tämä yhteinen argumenttilauseke β-redusoidaan vain kerran, ja sen arvo näkyy samalla kumpaankin esiintymään. Se tarkoittaa samaa kuin (a b) (a b). Nykyisissä ohjelmointikielissä käytetään kuitenkin tavallisesti muuta kuin normaalia sievennysjärjestystä: tavallisesti redeksi (funktio argumentti) määritelläänkin sievennettäväksi niin, että 12
16 1 ensin lasketaan argumentti arvoonsa a, ja vasta 2 sitten kutsutaan funktiota siten, että sen parametriksi annetaan tämä laskettu arvo a. Se voidaan määritellä normalisoinnissa siten, että 1 ensin normalisoidaankin sen argumentti näillä samoilla säännöillä, ja vasta 2 sitten suoritetaan tämän redeksin β-reduktio. Tätä parametrinvälitystapaa kutsutaan nimellä Call by Value (CBV). Sitä kutsutaan myös ahkeraksi tai innokkaaksi (eager) koska siinä riennetään laskemaan a tietämättä onko se tarpeen. Sitä kutsutaan myös tiukaksi (strict) koska a lasketaan siksi että niin ohjelma käskee tekemään. Kääntäen, CBN on non-strict, suomeksi vaikkapa rento. Ahkeraan ohjelmointikieleen voidaan lisätä tuki laiskuudelle (joko uutena piirteenä tai erillisenä kirjastona). Esimerkiksi Lisp-murteessa Scheme (Dybvig, 1996, luku 5.7) lauseke (delay e) ei laskekaan argumenttilausekkeensa e arvoa heti, vaan luo siitä lupauksen (promise) p, jonka (force p) myöhemmin lunastaa, eli sieventää lausekkeen e arvoonsa a. Jos sama p lunastetaan uudelleen, niin lauseketta e ei sievennetä uudelleen, vaan käytetään jo saatua arvoa a. Ohjelmoijan vastuulle jää kertoa koodissaan, mitkä sen osat hän haluaa suoritettavan laiskasti eikä ahkerasti koordinaatio laiskan ja ahkeran koodinsa välillä. Jos e ei ole funktionaalista koodia, niin a riippuu siitä, milloin p lunastetaan ensimmäisen kerran! pysytellä funktionaalisena laiskan koodinsa sisällä mutta ohjelmointikielet eivät voi yleensä varmistaa, että hän tottelisi... Laiskassa ohjelmointikielessä on puolestaan käänteinen ongelma: Miten ohjelmoija voi kertoa koodissaan, että sen jokin osa pitääkin suorittaa ahkerasti eikä laiskasti? Erityisesti: Miten laiskassa kielessä ilmaistaan, että tämä osa koodia pitää suorittaa loppuun ennen kuin tuon osan suoritus alkaa? Koska laiska suoritusjärjestys riippuu vain siitä, mitä informaatiota kutsuttava funktio käyttää argumentistaan, niin ohjelmoijan on luotava tällainen riippuvuus: tämän funktion pitää tuottaa jokin sellainen välitulos, jota tuo funktio tarvitsee päästäkseen käyntiin. Silloin laiskankin suorituksen on pakko edetä samassa järjestyksessä kuin ahkera etenisi. 13
17 Ohjelmointikielemme Haskell käyttää tätä lähestymistapaa esimerkiksi siinä osassa ohjelmaa, jolla on oikeus suorittaa I/O-operaatioita. Haskell voi varmistaa tämän oikeuden jo ohjelmakoodin käännösaikana kehittyneen tyyppijärjestelmänsä vuoksi. Kolmas yleinen parametrinvälitystapa on Call by Reference (CBR) jossa kutsuttava funktio saakin parametrinaan sen muistiosoitteen johon argumentti on talletettu. Silloin kutsuttava funktio voi vaihtaa koko argumenttinsa arvon toiseksi. Siten CBR rikkoo funktioiden informaationkulkukuria, jonka mukaan informaatiota saa tulla ulos vain tuloksessa. Oliokielissä käytetään usein CBR-johdannaista, jossa funktio ei voi vaihtaa koko argumenttiaan kokonaan toiseksi olioksi, mutta se voi muuttaa sen sisältöä. Tätä johdannaista on kutsuttu nimellä Call by Worth (CBW). CBW on myös laiskuuden toteutustekniikka: Silloin ohjelmointikielen toteutuksen ajonaikainen järjestelmä (run-time system) voi muuttaa argumenttina saadun lausekkeen arvokseen. Näin saadaan toteutettua lasketun argumenttiarvon muistaminen siten, että kun kutsuttu funktio viittaa argumenttiinsa joskus myöhemmin, niin se näkeekin sille nyt lasketun arvon. Muistintaminen (memoization) on yleisnimi tällaiselle ohjelmointitekniikalle, jossa funktion toimintaa tehostetaan sisäisesti muistamalla sen laskemia välituloksia, vaikka funktio näyttääkin ulospäin yhä matemaattiselta. 2.5 δ-määritelmät Nämä αβη-periaatteet antavat puhtaan λ-laskennan, jossa on vain Termien funktioita, mutta ei minkään muun tyyppisiä alkioita. Esimerkiksi luonnolliset luvut N lisättiin leikisti päättämällä, että tietty Termi n esittää luonnollista lukua n N. Matematiikassa yleisesti ja ohjelmoinnissa erityisesti on kuitenkin hyödyllistä ajatella, että jotkut perusasiat kuten Z = {..., 2, 1, 0, 1, 2,...} ja B = {False, True} ja niiden alkeisoperaatiot on jo annettu valmiina. (Vertaa konearitmetiikka.) Sitten voimme tarkastella miten λ-laskennalla voi määritellä niille mutkikkaampia funktioita. δ-reduktiot ovat tekninen väline tähän. Muistisääntö: δ is about definitions. Esimerkiksi kokonaisluvut Z voidaan lisätä aidosti λ-laskentaan seuraavasti: Myös jokainen kokonaisluku on Termi. 14
18 Tällaiset vakiotermit ovat valmiiksi normaalimuodossaan (joten niitä ei tietenkään enää tarvitse laskea eteenpäin). Myös alkeisoperaatioita saa käyttää Termien muodostamiseen. Esimerkiksi p + Z q, jossa + Z on alkeisoperaation kahden kokonaisluvun yhteenlasku nimi, on Termi kun myös sen argumentit p ja q ovat. Tällaisen alkeisoperaation merkitysoppi on annettu kokoelmana (joka saa olla ääretön) δ-reduktioita ja niin edelleen. 0 + Z Z Z Z Z Z 2 3 Tällainen vasen oikea luetaan jos redeksiä etsittäessä kohdataan vasen puoli, niin korvataan se oikealla puolella. Ennen tätä korvausta kumpikin Termi p ja q on ensin pitänyt normalisoida vastaaviksi vakioiksi. Siten alkeisoperaatiot voivat tarvita argumenttiensa arvoja ahkerasti myös laiskassa kielessa. Ehtolauseke on puolestaan totuusarvojen B keskeinen alkeisoperaatio: Sen δ-reduktiot ovat if True toka vika toka if False toka vika vika. Se siis tarvitsee ensimmäisen parametrinsa totuusarvoa, mutta ei tokan eikä vikan parametrinsa arvoa. Itse asiassa tokan ja vikan arvoa ei saa laskea, ennen kuin on selvinnyt, kumpaa niistä tarvitaan. Siten ahkerakin kieli tarvitsee myös tämän rennon piirteen. Tämä piirre lisätään ahkeraan kieleen kontrollirakenteina kuten if... then... else... joilla ohjelmoija ohjaa käsin sievennyksen etenemistä. Laiska kieli ei puolestaan tarvitse tätä jakoa kontrollirakenteisiin ja lausekkeisiin, koska siellä if voidaan tulkita tavalliseksi funktioksi Ohjelmoijan tekemät määritelmät Myös ohjelmoija voi kirjoittaa omia määritelmiään muodossa Muuttuja = Termi ja nekin tulkitaan kuten alkeisoperaatioiden δ-reduktiot: Jos redeksin etsintä kohtaa tämän Muuttujannimen, niin korvaa se tuolla Termillä. Funktionaalinen ohjelma onkin kokoelma tällaisia määritelmiä, ja sen suoritus on annetun Termin redusointia niitä käyttäen. Nämä määritelmät saavat olla rekursiivisia eli niiden oikean puolen Termi saavat käyttää niiden vasenten puolten Muuttujannimiä. 15
19 Rekursio onkin arkipäiväistä funktionaalisessa ohjelmoinnissa: Ohjelmoinnin perustyökalu on funktionkutsu, eikä ole eroa kutsutaanko siinä jotakin toista vaiko tätä samaa funktiota. Tämä johtuu siitä, että funktionaalisessa ohjelmoinnissa kaikki toisto pitää ilmaista rekursion avulla: Miten tavallinen silmukka kuten kertomafunktion arvon laskenta 1 m = 1; 2 while n > 1 3 m = m n; 4 n = n 1; 5 return m n! = n voisi pysähtyä, jos silmukan runko ei saakaan muuttaa sen ehdossa mainittujen muuttujien arvoja? Tämän silmukan rekursiivisessa muotoilussa fact = λn. if (n > 1) (n fact (n 1)) 1 (8) jokainen rekursiokutsu saakin oman paikallisen kopionsa muuttujasta n, ja niillä saa siten olla eri arvot. Funktionaalisen ohjelmoijan näkökulmasta silmukka onkin yksinkertaista rekursiota, eikä kaksi erillistä käsitettä. Arvon 2! laskenta etenee ahkerasti seuraavalla tavalla, jossa alleviivaus osoittaa sen kohdan, jota käsitellään seuraavaksi, ja alaindeksi osoittaa, mitä sille tehdään: fact 2 δ (λn. if (n > 1) (n fact (n 1)) 1) 2 β if (2 > 1) (2 fact (2 1)) 1 δ if True (2 fact (2 1)) 1 δ 2 fact (2 1) δ 2 fact 1 δ 2 ((λn. if (n > 1) (n fact (n 1)) 1) 1) β 2 (if (1 > 1) (1 fact (1 1)) 1) δ 2 (if False (1 fact (1 1)) 1) δ 2 1 δ 2. 16
20 Käytännön funktionaaliset ohjelmointikielet esittävät rekursion tällä tavalla. Teorian näkökulmasta olisi kuitenkin eleganttia, jos oikean puolen Termissä ei tarvittaisi vasemman puolen Muuttujannimeä, koska silloin koko määritelmä olisi yksinkertainen kuin sanakirjassa. Tämä voidaan tehdä lisäämällä kieleen Y- eli kiintopistekombinaattori (fixpoint / fixed-point combinator) ja sille δ-reduktiosääntö. Esimerkiksi normaalin sievennysjärjestyksen kielessä se on fix e e (fix e). Se voitaisiin jopa määritellä ilman rekursiota fix = λf.(λx.f (x x)) (λx.f (x x)) (9) joskin tämän määritelmän muoto riippuu normalisointijärjestyksestä. Esimerkiksi kertomafunktio (8) saisi muodon Tyypitys fact = fix (λf.λn. if (n > 1) (n f (n 1)) 1) jossa uusi parametri f saa arvokseen factin oikean puolen. Nämä δ-reduktiot tuovat kuitenkin mukanaan sen, että enää ei aina tiedetäkään, miten voitaisiin jatkaa normalisointia: Jos redeksinä löydetään vaikkapa 6 + Z True niin sille ei olekaan vastaavaa oikeaa puolta. Ohjelmoija sanoisi, että silloin ohjelmassa on tyyppivirhe koska siinä yritetään soveltaa alkeisoperaatiota vääränlaiseen argumenttiin. Alun perin tyyppiteoriaa (type theory) kehitettiin matemaattisessa logiikassa torjumaan sellaisia itseensä viittaavia ilmauksia kuten valehtelijan paradoksi Tämä lause ei ole totta! joille ei voi antaa loogista tulkintaa. Tietojenkäsittelijät puolestaan soveltavat tyyppiteoriaa tyyppivirheiden käsittelyyn ohjelmointikielissä, joissa itseensä viittaaminen eli rekursio on välttämätöntä. Ohjelmointikielessä voi olla dynaaminen tyypitys, jolloin ohjelman suoritus keskeytyy suoritusaikaiseen tyyppivirheeseen, jos se päätyy tilanteeseen, jota ei osatakaan normalisoida. staattinen tyypitys, jolloin kielen toteutus tutkii etukäteen ohjelman lähdekoodin, ja yrittää varmistua, ettei sen suoritus voi johtaa tyyppivirheisiin. Ohjelma hyväksytään suoritettavaksi vain jos tämä pystytään varmistamaan hylätään jos ei pystytä, ja ohjelmoijalle raportoidaan käännösaikainen tyypitysvirhe. Ohjelmoijan näkökulmasta dynaaminen tyypitys on joustavampaa kuin staattinen 17
21 staattinen tyypitys vähentää debuggauksen vaatimaa vaivaa, koska se karsii osan ohjelmointivirheistä jo ohjelmaa kirjoitettaessa. Staattinen tyypitys jakautuu vielä tyypintarkistukseen (typechecking) jossa ohjelmoija ilmoittaa koodissaan käyttämiensä muuttujien tyypit, ja kielen toteutus tarkastaa, että ne ovat oikein. Esimerkiksi kertomafunktiossa (8) ohjelmoija ilmoittaisi, että sen tyyppi on Z Z eli funktio, jonka parametri on tyyppiä Z ja tulos tyyppiä Z. Useimmat staattisesti tyypitetyt ohjelmointikelet käyttävät tyypintarkistusta. tyypinpäättelyyn (type inference) jossa ohjelmoijan ei tarvitse ilmoittaa muuttujiensa tyyppejä, vaan toteutus päättelee ne käyttöyhteyden perusteella. Esimerkiksi kertomafunktiossa (8) tämä päättely voisi edetä seuraavasti: 1 Koska fact alkaa λn niin sen pitää olla jotakin funktiotyyppiä t u. 2 Koska parametristä n vähennetään 1 Z niin myös sen tyypin t pitää olla Z. 3 Koska tuloksena voi olla 1 Z niin myös sen tyypin u pitää olla Z. Tällaiset menetelmät pohjautuvat logiikkaan. LAP-kurssin muistiinpanoissa mainittiin seuraava tulos: Lause 4 (Ricen lause). Jokainen Turingin koneiden epätriviaali semanttinen ongelma on ratkeamaton. Lauseen 2 perusteella sama pätee myös λ-laskennalla kirjoitetuille ohjelmille. Koska on epätriviaali semanttinen ongelma varmistaa voiko annettu ohjelmakoodi päätyä tyyppivirheeseen vaiko ei, niin tarkka tyypinpäättely on ratkeamatonta eli sellaisen ohjelmointikielen toteutus mahdotonta. Siksi tyypinpäättely on turvallisesti epätarkkaa: Ohjelmakoodi hyväksytään suoritettavaksi vain jos ohjelmointikielen toteutus on pystynyt varmistumaan että tyyppivirheet ovat mahdottomia. Tätä epätarkkuutta on vähennetty ohjelmointikielten tutkimuksessa ja kehityksessä vaihe vaiheelta määrittelemällä niille yhä ilmaisuvoimaisempia tyyppijärjestelmiä. Tällä kurssilla ja Haskell-kielessä sitoudutaan seuraavaan täsmällisen tyypityksen periaatteeseen: Jokaisella muuttujalla on täsmälleen yksi tarkka tyyppi, ja se määrätään staattisesti. Matematiikassa ei noudateta tätä periaatetta aivan orjallisesti: 1 Esimerkiksi jokainen luonnollinen luku m N kuuluu myös kokonaislukutyyppiin Z, ja 2 kaikki ne kuuluvat puolestaan rationaalilukutyyppiin Q, ja 3 kaikki ne kuuluvat puolestaan reaalilukutyyppiin R, ja 4 kaikki ne kuuluvat puolestaan kompleksilukutyyppiin C. Samaan tapaan olio-ohjelmoinnissa luokan L jäsenet ovat samalla myös sen jokaisen yliluokan jäseniä. 18
22 3 Funktionaalisen ohjelmoinnin historiaa 1934 Kurt Gödel aloitti rekursiivisten funktioiden ja relaatioiden teorian. Se oli osa hänen kuuluisan epätäydellisyyslauseensa todistusta. Se oli ensimmäinen ehdotus arkikielen käsitteen mekaanisesti laskettava tarkaksi määritelmäksi. Se oli kuitenkin puhtaan looginen fomalismi, jolloin sen mekaanisuus ei ollut aivan ilmiselvää muille kuin matemaatikoille kehitettiin (toisistaan riippumatta) 2 sen selkeämmin mekaanista muotoilua: Alan Turing esitteli nimeään kantavat koneet. Alonzo Church esitteli λ-laskennan, jota käsittelimme luvussa 2. Siten funktionaalinen ohjelmointi oli läsnä jo silloin, kun teoreettinen tietojenkäsittelytiede otti ensiaskeleensa määriteltiin 2 vanhinta yhä käytössä olevaa ohjelmointikieltä: Fortran (Formula translator) josta tuli pitkäksi aikaa tieteellisen laskennan eli numeronmurskauksen valtakieli. Lisp (List processing) josta tuli pitkäksi aikaa tekoälytutukimuksen valtakieli. Se lainasi λ-laskennan ideoita. Siinä on dynaaminen tyypitys. Siten funktionaalinen ohjelmointi oli läsnä jo modernien ohjelmointikielten syntyessä välillä tyyppiteoriaa (jonka tutkimus oli alkanut jo 1900-luvun alussa) kehitettiin edelleen, aluksi loogikkojen ja myöhemmin myös tietojenkäsittelijöiden toimesta. Tämän tutkimuksen keskeinen tuotos oli Hindley-Milner- eli HM -tyyppiteoria, johon useimmat nykyiset tyypinpäättelevät ohjelmointikielet pohjautuvat. Tietojenkäsittelijä Robin Milner keksi itsenäisesti uudelleen saman teorian, jota loogikko J. Roger Hindley oli tutkinut aiemmin. Alkuperäisen idean olivat keksineet logiikassa jo Churchin oppilas Haskell B. Curry ja Robert Feys. Sekä ohjelmointikieli Haskell että kuritus (jonka tosin keksi toinen loogikko Moses Schönfinkel) on nimetty Curryn mukaan luvun alussa funktionaalisten ohjelmointikielten kehitys oli edennyt standardointivaiheeseen saakka: Dynaamisesti tyypitetty Common Lisp -kieli sai lopullisen standardinsa (Steele, 1990). (Saman kieliperheen yhä kehittyviä kieliä ovat mm. Scheme ja Clojure.) HM-tyypinpäättelevän ahkeran kielen Standard ML (SML) ensimmäinen standardi ilmestyi. 19
23 HM-tyypinpäättelevän kielemme Haskell ensimmäinen standardi ilmestyi. Standardi määrittelee, että Haskell-kielen suoritustapa on rento. Haskell-kielen toteutukset ovat laiskoja, vaikka standardi ei sitä tarkkaan ottaen vaadikaan. Siksi Haskell-kieltäkin on tapana kutsua laiskaksi luvun lopussa myös näiden HM-tyypinpäättelevien kielten standardointi oli edistynyt: SML 97 (Milner et al., 1997) on SML-kielen lopulliseksi tarkoitettu standardi. (Samaan kieliperheeseen kuuluvat mm. OCaml ja F#.) Haskell 98 (Peyton Jones, 2003) on Haskell-kielen merkittävä standardi luvulla kiinnostus erityisesti Haskell-kieleen on kasvanut. Sillä on rentona kielenä monia kauniita teoreettisia ominaisuuksia. Onko niistä hyötyä käytännön ohjelmoinnissa vai haittaa? Sen laajennettu HM-tyypinpäättely on yhdistänyt parametrisen ja ad hoc - monimuotoisuuden. Tälle yhdistelmälle on löytynyt monia yllättäviä käyttökohteita ohjelmoinnissa. Tällä hetkellä uusin standardi on Haskell 2010 (Marlow, 2010). 4 Haskell-kieliset ohjelmat Tällä kurssilla tutustutaan funktionaaliseen ohjelmointikieleen Haskell. Sen kotisivu on josta löytyy standardi- ja muuta materiaalia, toteutuksia, kirjastoja,... Haskell-oppikirjoja on sekä painettuna (Hutton, 2007; Thompson, 2011) että verkossa (Lipovačka, 2011; O Sullivan et al., 2008). Haskell on puhdas (pure) funktionaalinen kieli, eli siinä ei ole lainkaan sivuvaikutuksia. Jos Haskell hyväksyy lähdekoodisi ilman virheilmoituksia, niin koodisi on varmasti funktionaalista. Puhtaus ja laiskuus ovat yhteydessä toisiinsa: epäpuhtaassa laiskassa kielessä ei tiedettäisi milloin eri sivuvaikutukset tapahtuisivat. Haskell onkin varsin uskollinen (rennolle laiskalle) λ-laskennalle. Keskitymme aluksi Haskellin suorituspuoleen ja syvennämme myöhemmin sen tyypityspuolen kuvausta. Käytämme Haskell Platform -pakettia, jonka voi asentaa kielen kotisivulta, ja jossa on kääntäjä ghc (eli Glasgow Haskell Compiler). 20
24 lauseke tuottaa omaa arvo omaa tyyppi on osa sopii omaa hahmo Kuva 1: Jokainen arvo, lauseke, ja hahmo omaa tietyn tyypin. tulkki ghci (eli ghc interactive) jota käytämme pääosan kurssista. Sen keskeisiä komentoja ovat :load tiedosto joka lataa Haskell-lähdekoodia. :type lauseke joka ilmoittaa päätellyn tyypin. kirjastot jotka laajentavat Haskell-standardin peruskirjastoja merkittävästi uusilla tietotyypeillä ja funktioilla. Paketin lisäksi kannattaa hankkia jokin editori, joka ymmärtää Haskell-syntaksia. Haskell-koodin merkitys voi riippua siitä, kuinka se on sisennetty. Yliopiston Windows-koneissa sellainen on Notepad++. Luennoija suosii itse Emacs-editorin Haskell-moodia. Esittelemme rinnakkain 4 käsitettä: Arvot jotka ovat laskennan tuloksia. Tyypit koska jokaisella arvolla on yksi tietty tyyppi. Lausekkeet jotka tuottavat arvoja. Lausekkeen tyyppi kertoo, minkä tyyppisiä arvoja se voi tuottaa. Hahmot (patterns) joita käytetään lausekkeissa sopimaan (match) arvoihin. Hahmon tyyppi kertoo, minkä tyyppisiin arvoihin se sopii. Kuva 1 esittää niiden väliset suhteen. 4.1 Syntaksista Muuttujien nimet alkavat pienellä kirjaimella, ja jatkuvat kirjaimilla, numeroilla, alaviivoilla _ ja heittomerkeillä. Muut nimet (kuten tyyppien, konstruktorien ja modulien nimet) ovat muuten samanlaisia, mutta ne alkavatkin SUURELLA kirjaimella. Haskellissa saa käyttää samaa nimeä eri tarkoituksiin, kun ne eivät voi mennä sekaisin: Esimerkiksi tyypin nimi voi olla sama kuin konstruktorin nimi, koska tyypit ja konstruktorit eivät voi esiintyä samassa osassa koodia. 21
25 x y tarkoittaa == x on sama kuin y /= x ei ole sama kuin y < x on pienempi kuin y > x on pienempi kuin y <= x on pienempi tai yhtäsuuri kuin y >= x on suurempi tai yhtäsuuri kuin y + lukujen yhteenlasku, - vähennuslasku, * kertolasku, / jakolasku ja ˆ potenssiinkorotus && x ja y x tai y ++ listojen x ja y yhdistäminen peräkkäin. yhdistetty funktio x y $ Oikealle assosioiva heikommin kuin sitova funktionkutsuoperaattori. Siis x $ y $ z uxson x (y (z u)). Taulukko 1: Haskell-kielen valmiiksi määriteltyjä operaattoreita. Lisäksi muuttujan nimi voi koostua pelkästään erikoismerkeistä, jolloin se tulkitaan binääriseksi operaattoriksi, eli 2-parametriseksi funktioksi, joka kirjoitetaankin niiden väliin. Taulukossa 1 on yleisimmät valmiiksi määritellyt operaattorit. Jos on operaattori, niin (x ) on funktio λy.x y eli jolle on annettu vain sen 1. argumentti x ( y) on funktio λx.x y eli jolle on annettu vain sen 2. argumentti y ( ) on funktio λx.λy.x y eli ilman kumpaakaan sen argumenttia, eli tavallisena niiden eteen kirjoitettavana funktiona. Siis x y on sama kuin ( ) x y. Kääntäen, tavallisesta nimestä saa operaattorin ympäröimällä se takaperoisilla lainausmerkeillä.... Siis x f y on sama kuin f x y. Operaattoreille voi määritellä assosiaatiosuunnan ja sidontavoiman infixp q jossa p on l(eft, vasemmalle) tai r(ight, oikealle) tai puuttuu (ei assosioidu) oletusarvo on l q on sidontavahvuus 0 q 9 heikoimmasta vahvimpaan oletusarvo on 9. Kommentteja on kolmenlaisia: Merkkiyhdistelmä -- aloittaa kommentin, joka jatkuu tämän rivin loppuun saakka. 22
26 Merkkiyhdistelmien {- -} välinen teksti on kommenttia. Nämä kommentit voivat olla sisäkkäin, joten niillä voi kommentoida lähdekoodia pois. Merkkiyhdistelmien {-# #-} välinen teksti on erikoiskommentti, johon voi laittaa Haskell-toteutukselle ohjeita (pragmas). Tällä kurssilla tärkein niistä on lähdekooditiedoston alkuun sijoitettava {-# LANGUAGE laajennus_1,laajennus_2,... #-} jolla voi kääntää päälle haluamansa laajennukset standardikieleen ja käyttää niitä tässä lähdekooditiedostossa. Jos yrittää käyttää jotakin laajennusta kääntämättä sitä ensin päälle, niin ghci ilmoittaa asiasta. 4.2 Atomiset perustietotyypit Haskell δ-määrittelee joitakin atomisia perustietotyyppejä operaatioineen. Tyypin rakenteisuus tarkoittaa, että tyypin arvot sisältävät pienempiä osia, joilla on omat arvonsa esimerkiksi tietue sisältää kenttiä, joilla on omat sisältönsä atomisuus tarkoittaa, että tyypin arvoja ei voi jakaa tällä tavoin pienempiin rakenneosiinsa. Näiden atomisten tyyppien vakiot ovat samalla lausekkeita jotka tuottavat vastaavan arvon siis lukuvakio 123 tuottaa lukuarvon 123 ja niin edelleen hahmoja jotka sopivat siihen arvoon Totuusarvotyyppi Totuusarvotyypin nimi on Bool. Sen vakiot ovat False and True. Lausekkeessa if ehto then tulos_t else tulos_f ehto on tyyppiä Bool sen kumpikin tuloshaara tulos_t ja tulos_f on keskenään samaa tyyppiä t tämä tyyppi t on samalla koko if-lausekkeen tyyppi koska kaikki sen tuottamat arvot ovat sitä tyyppiä. else-haaraa ei voi jättää pois mikä arvo silloin tuotettaisiin, jos ehto on False? 23
27 4.2.2 Merkkityyppi Haskell käyttää Unicode-merkistöä. Merkkityypin nimi on Char. Merkkivakio kirjoitetaan yksinkertaisten lainausmerkkkien... väliin. Merkkivakioiden kirjoitusasu (Marlow, 2010, luku 2.6) on ohjelmointikielen C käyttämän kirjoitusasun laajennus. Haskell käsittelee merkkijonoja merkkilistoina Kokonaislukutyypit Haskell sisältää valmiina tyypit Integer matemaatikon mielivaltaisen kokoisille kokonaisluvuille Z. Niissä bittien lukumäärää rajoittaa vain vapaa muisti. Aritmetiikka on hidasta, koska se on toteutettu ohjelmallisesti. Int ohjelmoijan kokonaisluvuille, joiden bittien lukumäärä on toteutuksen luonnollinen sananpituus. Se on tyypillisesti 32 tai 64 bittiä, Haskell-toteutuksesta riippuen. Haskell-standardi sallii sen, että toteutus voi varata muutaman biteistä omaan sisäiseen kirjanpitoonsa, joten bittien määrä voikin olla esimerkiksi vain 30. Aritmetiikka on nopeaa, koska se on toteutettu konekäskyillä. Aritmetiikka pyörähtää ympäri hiljaisesti kun luku ei enää mahdu tähän maksimipituuteen. Kirjastossa Data.Int on tyypit Intb joilla voi antaa etumerkillisen kokonaisluvun bittien määrän b = 8, 16, 32, 64 tarkasti. Kirjastossa Data.Word on vastaavat tyypit Wordb etumerkittömille kokonaisluvuille tyyppi Word joka on etumerkitön vastine tyypille Int. Kirjastossa Data.Ratio on tyyppi Rational matemaatikon murtoluvuille Q. välineet joilla voi määritellä tyyppien Intb perusteella sellaiset ohjelmoijan murtoluvut, joissa on haluttu määrä bittejä Liukulukutyypit Haskell sisältää valmiina IEEE-standardin mukaiset tyypit Real yksin- ja Double kaksinkertaisen tarkkuuden liukuluvuille. Epätarkkuutensa vuoksi yksinkertaista tarkkuutta kannattaa käyttää vain silloin jos kaksinkertainen kuluttaisi liikaa muistia. Kirjastossa Data.Complex on välineet, joilla voi määritellä kompleksiluvut C näiden tyyppien perusteella. 24
1 Mitä funktionaalinen ohjelmointi on?
1 Mitä funktionaalinen ohjelmointi on? On olemassa useita erilaisia ohjelmointiparadigmoja (programming paradigms) koska on useita erilaisia tapoja mallintaa ohjelmointiongelmia, esimerkiksi: Proseduraalinen
fix e e (fix e). fix = λf.(λx.f (x x)) (λx.f (x x)) (9)
Käytännön funktionaaliset ohjelmointikielet esittävät rekursion tällä tavalla. Teorian näkökulmasta olisi kuitenkin eleganttia, jos oikean puolen Termissä ei tarvittaisi vasemman puolen Muuttujannimeä,
tää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
TIEA341 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
Haskell 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
2.4 Normaalimuoto, pohja ja laskentajärjestys 2.4. NORMAALIMUOTO, POHJA JA LASKENTAJÄRJESTYS 13
2.4. NORMAALIMUOTO, POHJA JA LASKENTAJÄRJESTYS 13 Toisinaan voi olla syytä kirjoittaa α- tai β-kirjain yhtäsuuruusmerkin yläpuolelle kertomaan, mitä muunnosta käytetään. Esimerkki 4 1. (λx.x)y β = y 2.
Lisää laskentoa. TIEA341 Funktio ohjelmointi 1 Syksy 2005
Lisää laskentoa TIEA341 Funktio ohjelmointi 1 Syksy 2005 Kertausta: Laajennettu aritmetiikka Lasketaan rationaaliluvuilla vakiot yhteen, vähennys, kerto ja jakolasku Laajennetaan sitä määrittelyillä: vakio
TIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 26. kesäkuuta 2013
ja ja TIEA241 Automaatit ja kieliopit, kesä 2012 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 26. kesäkuuta 2013 Sisällys ja ja on yksi vanhimmista tavoista yrittää mallittaa mekaanista laskentaa. Kurt
Algebralliset 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...
Java-kielen perusteet
Java-kielen perusteet Tunnus, varattu sana, kommentti Muuttuja, alkeistietotyyppi, merkkijono, literaalivakio, nimetty vakio Tiedon merkkipohjainen tulostaminen 1 Tunnus Java tunnus Java-kirjain Java-numero
Luku 2. Ohjelmointi laskentana. 2.1 Laskento
Luku 2 Ohjelmointi laskentana Funktio-ohjelmoinnin, olio-ohjelmoinnin ja käskyohjelmoinnin ero on löydettävissä niiden pohjalla olevista laskennan mallista. Automaattisen tietojenkäsittelyn yksi historiallinen
ELM 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................................
Laajennetaan 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:
TIES542 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,
815338A 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
TIEA255 Tietotekniikan teemaseminaari ohjelmointikielet ja kehitysalustat. Antti-Juhani Kaijanaho. 16. helmikuuta 2011
TIEA255 Tietotekniikan teemaseminaari ohjelmointikielet ja kehitysalustat Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 16. helmikuuta 2011 Sisällys Sisällys Ohjelmointikieli? programming language n. a
Tyyppiluokat 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,
Rekursiolause. 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,
13. Loogiset operaatiot 13.1
13. Loogiset operaatiot 13.1 Sisällys Loogiset operaatiot AND, OR, XOR ja NOT. Operaatioiden ehdollisuus. Bittioperaatiot. Loogiset operaatiot ohjausrakenteissa. Loogiset operaatiot ja laskentajärjestys.
Lisää 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
Geneeriset 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?
Rajoittamattomat 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
11/20: Konepelti auki
Ohjelmointi 1 / syksy 2007 11/20: Konepelti auki Paavo Nieminen nieminen@jyu.fi Tietotekniikan laitos Informaatioteknologian tiedekunta Jyväskylän yliopisto Ohjelmointi 1 / syksy 2007 p.1/11 Tämän luennon
Ohjelmoinnin 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
Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)
Alkuarvot ja tyyppimuunnokset (1/5) Aiemmin olemme jo antaneet muuttujille alkuarvoja, esimerkiksi: int luku = 123; Alkuarvon on oltava muuttujan tietotyypin mukainen, esimerkiksi int-muuttujilla kokonaisluku,
Tietorakenteet ja algoritmit - syksy 2015 1
Tietorakenteet ja algoritmit - syksy 2015 1 Tietorakenteet ja algoritmit - syksy 2015 2 Tietorakenteet ja algoritmit Johdanto Ari Korhonen Tietorakenteet ja algoritmit - syksy 2015 1. JOHDANTO 1.1 Määritelmiä
Tyyppejä 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
Todistus: Aiemmin esitetyn mukaan jos A ja A ovat rekursiivisesti lueteltavia, niin A on rekursiivinen.
Lause: Tyhjyysongelma ei ole osittain ratkeava; ts. kieli ei ole rekursiivisesti lueteltava. L e = { w { 0, 1 } L(M w ) = } Todistus: Aiemmin esitetyn mukaan jos A ja A ovat rekursiivisesti lueteltavia,
1. 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ä
815338A Ohjelmointikielten periaatteet
815338A Ohjelmointikielten periaatteet 2015-2016 VI Funktionaalinen ohjelmointi Sisältö 1. Johdanto ja peruskäsitteitä 2. LISP- ja Scheme-kielet 3. Haskell 4. IO funktionaalisissa kielissä 5. Muita funktionaalisia
TIEA341 Funktio-ohjelmointi 1, kevät 2008
TIEA341 Funktio-ohjelmointi 1, kevät 2008 Aloitusluento Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 7. tammikuuta 2008 Aikataulu Luennot salissa Ag C231.1: ma klo 10 12, to klo 14-16
Turingin koneet määritteli sen laitelähtöisesti: Laskenta etenee suorittamalla yksinkertaisia käskyjä siten kuin sen ohjelma
1 Johdanto Aloitetaan lyhyellä kuvauksella funktionaalisen ohjelmoinnin ja siihen kannustavien ohjelmointikielten historiasta. Sitten kuvaillaan lyhyesti, mitä funktionaalisella ohjelmoinnilla tarkoitetaan
Tutoriaaliläsnäoloista
Tutoriaaliläsnäoloista Tutoriaaliläsnäolokierroksella voi nyt täyttää anomuksen läsnäolon merkitsemisestä Esim. tagi ei toiminut, korvavaltimon leikkaus, yms. Hyväksyn näitä omaa harkintaa käyttäen Tarkoitus
Ohjelmoinnin peruskurssien laaja oppimäärä
Ohjelmoinnin peruskurssien laaja oppimäärä Luento 8: Pienen ohjelmointikielen tulkki (ohjelmoitava laskin) (mm. SICP 4-4.1.5 osin) Riku Saikkonen 15. 11. 2012 Sisältö 1 Nelilaskintulkki, globaalit muuttujat
Pythonin alkeet Syksy 2010 Pythonin perusteet: Ohjelmointi, skriptaus ja Python
Pythonin alkeet Syksy 2010 Pythonin perusteet: Ohjelmointi, skriptaus ja Python 8. marraskuuta 2010 Ohjelmointi Perusteet Peruskäsitteitä Olio-ohjelmointi Pythonin alkeet Esittely Esimerkkejä Muuttujat
Vasen 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
Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen
Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari 1 1. JOHDANTO 1.1 Määritelmiä 1.2 Tietorakenteen ja algoritmin valinta 1.3 Algoritmit ja tiedon määrä 1.4 Tietorakenteet ja toiminnot 1.5 Esimerkki:
Demo 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
ITKP102 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
JFO: Johdatus funktionaaliseen ohjelmointiin
JFO: Johdatus funktionaaliseen ohjelmointiin Matti Nykänen Tietojenkäsittelytieteen laitos, Itä-Suomen yliopisto matti.nykanen@uef.fi Lukuvuosi 2010-11, IV periodi Sisältö 1 Johdanto 1 1.1 Historiaa.....................................
AS-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
Mathematica Sekalaista asiaa
Mathematica Sekalaista asiaa Asetusoperaattorit Mathematicassa voi käyttää omia muuttujasymboleja melko rajattomasti ja niiden nimeämisessä voi käyttää miltei mitä tahansa merkkejä. Käytännössä nimeämisessä
815338A Ohjelmointikielten periaatteet Harjoitus 4 vastaukset
815338A Ohjelmointikielten periaatteet 2015-2016. Harjoitus 4 vastaukset Harjoituksen aiheena ovat imperatiivisten kielten lauseisiin, lausekkeisiin ja aliohjelmiin liittyvät kysymykset. Tehtävä 1. Mitä
Ohjelmoinnin perusteet Y Python
Ohjelmoinnin perusteet Y Python T-106.1208 20.1.2010 T-106.1208 Ohjelmoinnin perusteet Y 20.1.2010 1 / 40 Arvon pyytäminen käyttäjältä Käyttäjän antaman arvon voi lukea raw_input-käskyllä. Käskyn sulkujen
815338A 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/
Ydin-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,...,
Matematiikan 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
Luku 6. Dynaaminen ohjelmointi. 6.1 Funktion muisti
Luku 6 Dynaaminen ohjelmointi Dynaamisessa ohjelmoinnissa on ideana jakaa ongelman ratkaisu pienempiin osaongelmiin, jotka voidaan ratkaista toisistaan riippumattomasti. Jokaisen osaongelman ratkaisu tallennetaan
Säännölliset kielet. Sisällys. Säännölliset kielet. Säännölliset operaattorit. Säännölliset kielet
TIEA241 Automaatit ja kieliopit, kesä 2013 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 24. toukokuuta 2013 Sisällys Formaalit kielet On tapana sanoa, että merkkijonojen joukko on (formaali) kieli. Hieman
TIEA241 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
Johdatus Ohjelmointiin
Johdatus Ohjelmointiin Syksy 2006 Viikko 2 13.9. - 14.9. Tällä viikolla käsiteltävät asiat Peruskäsitteitä Kiintoarvot Tiedon tulostus Yksinkertaiset laskutoimitukset Muuttujat Tiedon syöttäminen Hyvin
Ohjelmoinnin peruskurssien laaja oppimäärä
Ohjelmoinnin peruskurssien laaja oppimäärä Luento 11: Tulkin muokkaaminen, sisäiset määrittelyt, makrot (mm. SICP 3.2.4, 4-4.1.6) Riku Saikkonen 29. 11. 2012 Sisältö 1 Kirjan tulkin muokkaaminen 2 Yksityiskohta:
Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen
Chapel TIE-20306 Ryhmä 91 Joonas Eloranta Lari Valtonen Johdanto Chapel on Amerikkalaisen Cray Inc. yrityksen kehittämä avoimen lähdekoodin ohjelmointikieli. Chapel on rinnakkainen ohjelmointikieli, joka
Uusi näkökulma. TIEA341 Funktio ohjelmointi 1 Syksy 2005
Uusi näkökulma TIEA341 Funktio ohjelmointi 1 Syksy 2005 Aloitetaan alusta... Otetaan uusi näkökulma Haskelliin ohjelmointi laskentana kertausta toisaalta, uusia käsitteitä toisaalta helpottanee sitten
Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti
C! Perusteet 19.1.2017 Palautteesta (1. kierros toistaiseksi) Toistaiseksi helppoa Miksi vain puolet pisteistä? Vaikeinta oli ohjelmointiympäristön asennus ja käyttö Vaikeaa eroavuudet Pythonin ja C:n
Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin.
2. Ohjausrakenteet Ohjausrakenteiden avulla ohjataan ohjelman suoritusta. peräkkäisyys valinta toisto Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet
Matematiikan tukikurssi
Matematiikan tukikurssi Kurssikerta 4 Jatkuvuus Jatkuvan funktion määritelmä Tarkastellaan funktiota f x) jossakin tietyssä pisteessä x 0. Tämä funktio on tässä pisteessä joko jatkuva tai epäjatkuva. Jatkuvuuden
Java-kielen perusteet
Java-kielen perusteet Tunnus, varattu sana, kommentti Muuttuja, alkeistietotyyppi, merkkijono, Vakio Tiedon merkkipohjainen tulostaminen Ohjelmointi (ict1tx006) Tunnus (5.3) Javan tunnus Java-kirjain Java-numero
Tähtitieteen käytännön menetelmiä Kevät 2009 Luento 4: Ohjelmointi, skriptaus ja Python
Tähtitieteen käytännön menetelmiä Kevät 2009 Luento 4: Ohjelmointi, skriptaus ja Python 31. tammikuuta 2009 Ohjelmointi Perusteet Pythonin alkeet Esittely Esimerkkejä Muuttujat Peruskäsitteitä Käsittely
Koka. Ryhmä 11. Juuso Tapaninen, Akseli Karvinen. 1. Taustoja 2. Kielen filosofia ja paradigmat 3. Kielen syntaksia ja vertailua JavaScriptiin Lähteet
Koka Ryhmä 11 Juuso Tapaninen, Akseli Karvinen 1. Taustoja 2. Kielen filosofia ja paradigmat 3. Kielen syntaksia ja vertailua JavaScriptiin Lähteet 1 1. Taustoja Koka on Daan Leijenin ja Microsoft:n kehittämä
815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset
815338A Ohjelmointikielten periaatteet 2015-2016. Harjoitus 3 vastaukset Harjoituksen aiheena ovat imperatiivisten kielten muuttujiin liittyvät kysymykset. Tehtävä 1. Määritä muuttujien max_num, lista,
IDL - 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,
Algoritmit 1. Luento 2 Ke Timo Männikkö
Algoritmit 1 Luento 2 Ke 11.1.2017 Timo Männikkö Luento 2 Algoritmin esitys Algoritmien analysointi Suoritusaika Asymptoottinen kertaluokka Peruskertaluokkia NP-täydelliset ongelmat Algoritmit 1 Kevät
TIEA341 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
ATK 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,
Se 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
Osoitin ja viittaus C++:ssa
Osoitin ja viittaus C++:ssa Osoitin yksinkertaiseen tietotyyppiin Osoitin on muuttuja, joka sisältää jonkin toisen samantyyppisen muuttujan osoitteen. Ohessa on esimerkkiohjelma, jossa määritellään kokonaislukumuuttuja
TIE Principles of Programming Languages. Seminaariesityksen essee. Ryhmä 18: Heidi Vulli, Joni Heikkilä
TIE-20306 Principles of Programming Languages Seminaariesityksen essee Ryhmä 18: Heidi Vulli, Joni Heikkilä SISÄLLYSLUETTELO 1. Johdanto... 1 2. Ohjelmointikielen perusperiaatteet... 1 2.1 Paradigmat...
HY / Matematiikan ja tilastotieteen laitos Johdatus logiikkaan I, syksy 2018 Harjoitus 5 Ratkaisuehdotukset
HY / Matematiikan ja tilastotieteen laitos Johdatus logiikkaan I, syksy 2018 Harjoitus 5 Ratkaisuehdotukset 1. Päättele resoluutiolla seuraavista klausuulijoukoista: (a) {{p 0 }, {p 1 }, { p 0, p 2 },
ICS-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.
Ohjelmoinnin 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
815338A Ohjelmointikielten periaatteet 2014-2015
815338A Ohjelmointikielten periaatteet 2014-2015 X Skriptiohjelmointi Sisältö 1. Johdanto 2. Skriptikielten yleispiirteitä 3. Python 815338A Ohjelmointikielten periaatteet, Skriptiohjelmointi 2 X.1 Johdanto
Ohjelmoinnin perusteet Y Python
Ohjelmoinnin perusteet Y Python T-106.1208 21.1.2009 T-106.1208 Ohjelmoinnin perusteet Y 21.1.2009 1 / 32 Tyypeistä Monissa muissa ohjelmointikielissä (esim. Java ja C) muuttujat on määriteltävä ennen
5/20: Algoritmirakenteita III
Ohjelmointi 1 / syksy 2007 5/20: Algoritmirakenteita III Paavo Nieminen nieminen@jyu.fi Tietotekniikan laitos Informaatioteknologian tiedekunta Jyväskylän yliopisto Ohjelmointi 1 / syksy 2007 p.1/17 Tämän
Yksinkertaiset tyypit
Yksinkertaiset tyypit TIES542 Ohjelmointikielten periaatteet, kevät 2007 Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 13. helmikuuta 2007 Tyypitön puhdas λ-laskento E ::= I E 1 E 2
Yhtälönratkaisusta. Johanna Rämö, Helsingin yliopisto. 22. syyskuuta 2014
Yhtälönratkaisusta Johanna Rämö, Helsingin yliopisto 22. syyskuuta 2014 Yhtälönratkaisu on koulusta tuttua, mutta usein sitä tehdään mekaanisesti sen kummempia ajattelematta. Jotta pystytään ratkaisemaan
Ohjelmoinnin peruskurssien laaja oppimäärä
Ohjelmoinnin peruskurssien laaja oppimäärä Luento 5: Sijoituslause, SICP-oliot, tietorakenteen muuttaminen (mm. SICP 33.1.3, 3.33.3.2) Riku Saikkonen 6. 11. 2012 Sisältö 1 Muuttujan arvon muuttaminen:
Ohjelmoinnin peruskurssien laaja oppimäärä
Ohjelmoinnin peruskurssien laaja oppimäärä Luento 2: SICP kohdat 22.2.3 Riku Saikkonen 2. 11. 2010 Sisältö 1 Linkitetyt listat 2 Listaoperaatioita 3 Listarakenteet 4 Gambit-C:n Scheme-debuggeri Linkitetyt
12. Javan toistorakenteet 12.1
12. Javan toistorakenteet 12.1 Sisällys Yleistä toistorakenteista. Laskurimuuttujat. While-, do-while- ja for-lauseet. Laskuri- ja lippumuuttujat. Tyypillisiä ohjelmointivirheitä. Silmukan rajat asetettu
Ohjelmoinnin 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
811120P Diskreetit rakenteet
811120P Diskreetit rakenteet 2018-2019 1. Algoritmeista 1.1 Algoritmin käsite Algoritmi keskeinen laskennassa Määrittelee prosessin, joka suorittaa annetun tehtävän Esimerkiksi Nimien järjestäminen aakkosjärjestykseen
811120P Diskreetit rakenteet
811120P Diskreetit rakenteet 2016-2017 1. Algoritmeista 1.1 Algoritmin käsite Algoritmi keskeinen laskennassa Määrittelee prosessin, joka suorittaa annetun tehtävän Esimerkiksi Nimien järjestäminen aakkosjärjestykseen
Logiikan kertausta. TIE303 Formaalit menetelmät, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos.
TIE303 Formaalit menetelmät, kevät 2005 Logiikan kertausta Antti-Juhani Kaijanaho antkaij@mit.jyu.fi Jyväskylän yliopisto Tietotekniikan laitos TIE303 Formaalit mentetelmät, 2005-01-27 p. 1/17 Luento2Luentomoniste
System.out.printf("%d / %d = %.2f%n", ekaluku, tokaluku, osamaara);
Mikäli tehtävissä on jotain epäselvää, laita sähköpostia vastuuopettajalle (jorma.laurikkala@uta.fi). Muista nimetä muuttujat hyvin sekä kommentoida ja sisentää koodisi. Ohjelmointitehtävien osalta palautetaan
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 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
Täydentäviä muistiinpanoja laskennan rajoista
Täydentäviä muistiinpanoja laskennan rajoista Antti-Juhani Kaijanaho 10. joulukuuta 2015 1 Diagonaalikieli Diagonaalikieli on D = { k {0, 1} k L(M k ) }. Lause 1. Päätösongelma Onko k {0, 1} sellaisen
Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista
Sisällys 1. Omat operaatiot Yleistä operaatioista. Mihin operaatioita tarvitaan? Oman operaation määrittely. Yleisesti, nimeäminen ja hyvä ohjelmointitapa, määreet, parametrit ja näkyvyys. HelloWorld-ohjelma
lausekkeiden tapauksessa. Jotkin ohjelmointikielet on määritelty sellaisiksi,
3.5. TYYPIT 59 indeksit voidaan siirtää kielitasolta oliotasolle siirryttäessä alkamaan nollasta. Vain mikäli indeksin alin arvo oliotasolla ei ole tiedossa ennen suorituksen alkua, on tarpeen lisätä taulukko-olioon
Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti
C! Perusteet 19.1.2017 Palautteesta (1. kierros toistaiseksi) (Erittäin) helppoa Miksi vain puolet pisteistä? Vaikeinta oli ohjelmointiympäristön asennus ja käyttö Ei selvää että main funktion pitikin
Kielioppia: toisin kuin Javassa
Object Pascal Pascal kielen oliolaajennus (Inprise/Borland:n oma) luokat Voit uudelleenkäyttää luomiasi objekteja esim. komponentteja Periytyminen Kielioppia: toisin kuin Javassa Ei eroa isojen ja pienien
Concurrency - Rinnakkaisuus. Group: 9 Joni Laine Juho Vähätalo
Concurrency - Rinnakkaisuus Group: 9 Joni Laine Juho Vähätalo Sisällysluettelo 1. Johdanto... 3 2. C++ thread... 4 3. Python multiprocessing... 6 4. Java ExecutorService... 8 5. Yhteenveto... 9 6. Lähteet...
7/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
1. Omat operaatiot 1.1
1. Omat operaatiot 1.1 Sisällys Yleistä operaatioista. Mihin operaatioita tarvitaan? Oman operaation määrittely. Yleisesti, nimeäminen ja hyvä ohjelmointitapa, määreet, parametrit ja näkyvyys. HelloWorld-ohjelma
Ohjelmointikieli TIE Principles of Programming Languages Syksy 2017 Ryhmä 19
Ohjelmointikieli TIE-20306 Principles of Programming Languages Syksy 2017 Ryhmä 19 Juho Kärnä Ville Mäntysaari 1. Johdanto D on yleiskäyttöinen, strukturoitu, staattisesti tyypitetty, käännettävä ohjelmointikieli
Tietotyypit ja operaattorit
Tietotyypit ja operaattorit Luennossa tarkastellaan yksinkertaisten tietotyyppien int, double ja char muunnoksia tyypistä toiseen sekä esitellään uusia operaatioita. Numeeriset tietotyypit ja muunnos Merkkitieto
TIE Principles of Programming Languages CEYLON
TIE-20306 Principles of Programming Languages CEYLON SISÄLLYSLUETTELO 1. YLEISTIETOA KIELESTÄ JA SEN KEHITTÄMISESTÄ... 1 2. CEYLONIN OMINAISUUKSIA... 2 2.1 Modulaarisuus... 2 2.2 Tyypit... 2 2.3 Muita
1. Olio-ohjelmointi 1.1
1. Olio-ohjelmointi 1.1 Sisällys Olio-ohjelmointi on eräs ohjelmointiparadigma. Olio-ohjelmoinnin muotoja. Ohjelmiston analyysi ja suunnittelu. Olioparadigman etuja ja kritiikkiä. 1.2 Ohjelmointiparadigmoja
11.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ä
TIEA241 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
System.out.printf("%d / %d = %.2f%n", ekaluku, tokaluku, osamaara);
Kysy Karilta tai Kimmolta, jos tehtävissä on jotain epäselvää. Kerro WETOon liittyvät ongelmat suoraan Jormalle sähköpostitse (jorma.laurikkala@uta.fi). Muista nimetä muuttujat hyvin sekä kommentoida ja