Mitä funktionaalinen ohjelmointi on



Samankaltaiset tiedostot
Mitä funktionaalinen ohjelmointi on

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

815338A Ohjelmointikielten periaatteet Harjoitus 7 Vastaukset

Luku 3. Listankäsittelyä. 3.1 Listat

TIEA341 Funktio-ohjelmointi 1, kevät 2008

815338A Ohjelmointikielten periaatteet

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

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

Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005

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

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin.

TIEA341 Funktio-ohjelmointi 1, kevät 2008

String-vertailusta ja Scannerin käytöstä (1/2) String-vertailusta ja Scannerin käytöstä (2/2) Luentoesimerkki 4.1

Jatkeet. TIES341 Funktio ohjelmointi 2 Kevät 2006

5.5 Jäsenninkombinaattoreista

Matematiikan tukikurssi

Funktionimien kuormitus. TIES341 Funktio ohjelmointi 2 Kevät 2006

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

Haskell ohjelmointikielen tyyppijärjestelmä

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

Luonnollisten lukujen laskutoimitusten määrittely Peanon aksioomien pohjalta

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Java kahdessa tunnissa. Jyry Suvilehto

Ohjelmoinnin peruskurssien laaja oppimäärä

Java-kielen perusteet

ITKP102 Ohjelmointi 1 (6 op)

Imperatiivisen ohjelmoinnin peruskäsitteet. Meidän käyttämän pseudokielen lauseiden syntaksi

Tutoriaaliläsnäoloista

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

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Ohjelmoinnin peruskurssien laaja oppimäärä

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

1. Omat operaatiot 1.1

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

ITKP102 Ohjelmointi 1 (6 op)

Ohjelmoinnin perusteet Y Python

811120P Diskreetit rakenteet

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Sisällys. 12. Monimuotoisuus. Johdanto. Alityypitys. Johdanto. Periytymismekanismi määrittää alityypityksen.

Ohjelmoinnin peruskurssien laaja oppimäärä

Tietotyypit ja operaattorit

5 Olio-ohjelmointi 133

Ohjelmoinnin peruskurssien laaja oppimäärä

Racket ohjelmointia. Tiina Partanen 2014

Luku 6. Dynaaminen ohjelmointi. 6.1 Funktion muisti

Ohjelmointikieli TIE Principles of Programming Languages Syksy 2017 Ryhmä 19

Ohjelmoinnin perusteet Y Python

TIEA341 Funktio-ohjelmointi 1, kevät 2008

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin peruskurssien laaja oppimäärä

Vertailulauseet. Ehtolausekkeet. Vertailulauseet. Vertailulauseet. if-lauseke. if-lauseke. Javan perusteet 2004

OHJ-1151 Ohjelmointi IIe

Javan perusteita. Janne Käki

Ohjelmoinnin peruskurssi Y1

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

Java-kielen perusteet

815338A Ohjelmointikielten periaatteet

Ohjelmoinnin perusteet Y Python

A TIETORAKENTEET JA ALGORITMIT

Mathematica Sekalaista asiaa

Sisältö Yleistä. Esittely ja luominen. Alkioiden käsittely. Kaksiulotteinen taulukko. Taulukko metodin parametrina. Taulukko ja HelloWorld-ohjelma. Ta

Java-kielen perusteet

Ohjelmointiharjoituksia Arduino-ympäristössä

ITKP102 Ohjelmointi 1 (6 op)

Funktionaalinen ohjelmointi

Tietueet. Tietueiden määrittely

18. Abstraktit tietotyypit 18.1

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

2. Lisää Java-ohjelmoinnin alkeita. Muuttuja ja viittausmuuttuja (1/4) Muuttuja ja viittausmuuttuja (2/4)

ELM GROUP 04. Teemu Laakso Henrik Talarmo

C-ohjelma. C-ohjelma. C-ohjelma. C-ohjelma. C-ohjelma. C-ohjelma. Operaatioiden suoritusjärjestys

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Ohjelmoinnin peruskurssien laaja oppimäärä

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta

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

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

Sisällys. 18. Abstraktit tietotyypit. Johdanto. Johdanto

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

C-kielessä taulukko on joukko peräkkäisiä muistipaikkoja, jotka kaikki pystyvät tallettamaan samaa tyyppiä olevaa tietoa.

7/20: Paketti kasassa ensimmäistä kertaa

Koka. Ryhmä 11. Juuso Tapaninen, Akseli Karvinen. 1. Taustoja 2. Kielen filosofia ja paradigmat 3. Kielen syntaksia ja vertailua JavaScriptiin Lähteet

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

Ohjelmoinnin peruskurssien laaja oppimäärä

Funktionaalinen ohjelmointi

Johdatus diskreettiin matematiikkaan Harjoitus 7,

Sisältö. 2. Taulukot. Yleistä. Yleistä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin perusteet Y Python

815338A Ohjelmointikielten periaatteet Harjoitus 4 vastaukset

Ohjelmoinnin perusteet Y Python

Ehto- ja toistolauseet

Algoritmit ja tietorakenteet Copyright Hannu Laine. 1, kun n= 0. n*(n-1)!, kun n>0;

Lisää pysähtymisaiheisia ongelmia

Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä

Transkriptio:

Funktionaalinen ohjelmointi Mitä funktionaalinen ohjelmointi on - Funktionaalisessa ohjelmoinnissa mallinnus keskittyy löytämään ongelmasta sellaisia tiedon muunnoksia, jotka voidaan esittää matemaattisina funktioina annetuilta syöttötiedoilta haluttuihin tulostietoihin. - Funktio ottaa sisään informaatioita parametreissään ja palauttaa informaatioita tuloksessaan. Tulos riippuu vain sisään tulleesta informaatiosta. - Perustuu matemaattiseen funktioteoriaan - Funktiokutsujen arvot riippuvat vain osalausekkeiden arvoista - Sivuvaikutuksia ei ole - Sijoituslausetta ei ole myöskään (paitsi epäpuhtaissa kielissä) - Funktiot ovat "ensimmäisen luokan kansalaisia"

- Funktio voi olla toisen funktion argumenttina, paluuarvona ja tietorakenteen alkiona - Voidaan rakentaa sellaisia funktioita, jotka käsittelevät ja palauttavat arvonaan muita funktioita - Vapaa suoritusjärjestys, laiska tai rinnakkainen evaluointi - Implisiittinen muistinhallinta - Ei sijoituslausetta, muuttuja ei voi muuttua - Ei voi tehdä for-silmukkaa, toteutus korvataan rekursiolla - Funktiota ei evaluoida ennen kuin sen parametrit on evaluoitu

Funktionaalisen ohjelmoinnin historia - 1936 kaksi tietojenkäsittelytieteen matemaattisen pohjan muodotavaa laskennan mallia. - Turingin kone (tehtävä T voidaan suorittaa mekaanisesti) - Ohjelman suoritus etenee muuttamalla koneen tilaa käskyjen mukaan -> tietokoneen abstrakti malli - lambda-kalkyyli, jonka pohjalta kehitettiin Lisp-kieli (funktio f on sääntö, jota seuraamalla sen arvo selviää) - Arvon laskenta etenee sieventämällä lauseketta yksinkertaisemmaksi annetuilla säännnöillä -> ohjelmointikielen abstrakti malli - Turingin koneet ja lambda-ohjelmat voidaan kääntää toisikseen

- 1970-luvun lopussa ML- ja SML- ohjelmointikielet ((Standard) Meta Language), joihin mm. Haskell perustuu - Alettiin suunnitella tyypit päätteleviä kieliä - Kääntäjä huomaa minkä tyyppisiä tietoalkioita koodissa käsitellään - Ohjelmoijan ei tarvitse kirjoittaa ainakaan kaikkia tyyppejä koodiinsa, kääntäjä osaa konstruoida uusia tyyppejä vanhoja sopivasti yhdistelemällä - Suorituksen eteneminen, ei koskaan jouduta tilanteeseen, jota ei olisi kielen spesifikaatiossa "No core dumbs" - Tyyppien säilyttäminen, käännösaikana tietoalkioille päätellyt tyypit pysyvät samoina koko suorituksen ajan "No type casts".

Miksi funktionaalista ohjelmointia - Kannattaa tuntea erilaisia ohjelmointiparadigmoja, kun ohjelmoija kohtaa ohjelmointiongelmia osaa hän valita kuhunkin ongelmaan sopivimman lähestymistavan. - Funktionaalisessa ohjelmoinnissa painotetaan tiukkoja rajapintoja. Opitaan määrittelemään ohjelma täsmällisesti, soveltamaan rekusriota ja käyttämään generisyyttä. - Painottaa uudelleenkäyttöä - Yhteys matematiikkaan, ohjelma voidaan kirjoittaa vastaamaan määritelmä ja todistaa oikeaksi induktiolla - Kannustaa kokoamaan ohjelmat pienemmistä palasista. Parempi tuki funktioiden käsittelylle, ohjelma voidaan jakaa pienempiin ja yleiskäyttöisempiin ohjelmiin - Ei sivuvaikutuksia

Funktionaalisen ja proseduraalisen ohjelmoinnin eroja - Proseduraalisissa eli imperatiivisissa ohjelmointikielissä käskyt suoritetaan järjestyksessä alkaen ensimmäisestä käskystä päättyen viimeiseen. Suoritusjärjestystä voidaan ohjata valinta- ja toistorakenteilla. - Proseduraalisessa ohjelmoinnissa ohjelma pilkotaan aliohjelmiksi, jotka ratkaisevat jonkin yksinkertaisen ongelman. Aliohjelma voi kutsua toista aliohjelmaa. Suuret ohjelmat jaetaan moduuleihin, jotka voivat sisältää useita aliohjelmia. - Funktionaalisessa ohjelmoinnnissa asiat esitetään asioiden välisinä relaatioina. Ohjelman rakenne koostuu toisiaan kutsuvista funktioista, jotka muistuttavat matematiikan funktioita, funktion arvo saadaan kun lauseketta sievennetään aina vain yksinkertaisemmaksi.

- Puhtaasti funktionaalisessa ohjelmassa kaikki tehdään funktioiden avulla, funktion argumentteihin kodistetaaan funktion toiminta, funktio ei voi muuttaa argumentin arvoa, vaan tuottaa uuden arvon tai funktion. - Funktionaalisella ohjelmalla ei ole tilaa, tämä tarkoittaa sitä, että funktiolla ei ole muuttujia, joita voitaisiin muuttaa sijoituslauseilla tai toistorakenteilla. Sijoituslauseita tai toistorakenteita ei tarvita. Tästä seuraa, että funktioilla ei ole sivuvaikutuksia. - Kaikki funktionaaliset ohjelmat eivät kuitenkaan ole puhtaasti funktionaalisia, useissa kielissä tuetaan muuttujien käyttöä ja sivuvaikutuksia. - Funktionaalinen kieli on laiska, jos funktion argumetit lasketaan vasta sitten kun niitä tarvitaan. Täten vältetään turhaa laskentaa ja tehokkuus paranee. Voidaan käyttää äärettömiä tietorakenteita, jossa ohjelma käy läpi vain äärellisen määrän alkioita. Tietorakenteet kuvataan taulukoiden tai listojen avulla, listat ja taulukot voivat sisältää alkioinaan listoja tai taulukoita.

Funktionaalisen ohjelmoinnin hyödyt ja haitat - Funktionaaliset ohjelmat käyttäytyvät aina samalla tavalla, ei yllättäviä sivuvaikutuksia ohjelman suorituksen aikana. Funktioita helppo käyttää uudellen sivuvaikutusten puuttuessa. Ohjelmia helppo laajentaa, koska osat eivät vaikuta toisiinsa. Funktioiden testaaminen on helppoa, koska funktio käyttäytyy samoilla argumenteilla aina samalla tavalla. - Korkea abstraktiotaso, tarvitaan vähemmän koodia, josta seuraa, että ohjelmat sisältävät vähemmän virheitä. Kirjoitustyyli lähellä ongelman määrittelyä. - Funktionaalisissa kielissä on automaattinen roskienkeruu, eli muistin varaaminen ja vapauttaminen tehdään automaattisesti.

- Rinnakkaisessa ohjelmoinnissa ohjelmien kehittäminen helpompaa, Tiedon eheys säilyy, koska ei ole vaaraa, että funktiot lukitsisivat yhteisiä muuttujia tai kirjoittaisivat samaan muuttujaan yhtäaikaa. - Funktionaaliset ohjelmat sopivat hyvin matemaattisten ongelmien ratkaisemiseen, annettuun syötteeseen kohdistetaan jokin muunnos, joka voi olla monivaiheinen ja lopuksi ilmoitetaan muunnoksen tuottama tulos. - Funktionaalinen ohjelmonti sopii sellaiseen tilanteeseen, jossa muunnettavalla informatiolla on lista- tai puurakenne, esimerkiksi xml-dokumenttien käsittely. - Ohjelmointikielen kääntäminen, jossa mm. selausvaiheessa luetaan lähdetiedostoa ja muunetaan tiedot alkiojonoksi, jäsennysvaiheessa muunetaan alkiojono vastaavaksi kieliopin mukaiseksi jäsennyspuuksi,..., lopuksi koodin generointivaiheessa käydään läpi jäsennyspuu ja tulostetaan sitä vastaava konekielinen koodi tulostiedostoon. - Imperatiivisesta ohjelmoinnista funktionaaliseen ohjelmointiin siirtyminen saattaa olla vaikeaa, koska ajatusmaailmat poikkeavat toisistaan, ei ole muuttujia eikä toistorakenteita.

- Syöttö ja tulostuoperaatioiden hoitaminen, joudutaan rikkomaan sivuvaikutuksettomuutta. Yleensä sivuvaukutuksisa sisältävät osiot erotetaan tarkasti puhtaasti funktionaalisista osista. Haskelll -kielen monadi, joka on abstrakti tietotyyppi, joka kuvaa ohjelman suoritusta annetussa kontekstissa. (http://en.wikipedia.org/wiki/monad_%28functional_programming%29) - Huonosti toetutettu funktionaalinen ohjelma voi olla hidas ja muistia haaskaava. - Funktionaalinen ohjelmointi ei sovellu kovin hyvin tilanteisiin, joissa tarvitaan jokin vaikutus, esim. ikkunakäyttöliittymä joissa keskeisenä on jokin tila, jota luetaan ja päivitetään monessa kohtaa ohjelmaa - tila toimii informaationvälityskanavana ohjelman osien välillä, esim. jonkin tietokannan ympärille rakennettu ohjelma joissa seurataan suoritusajan ja muistin kulutusta, esim. laiteläheinen ohjelmointi - Haasteena on suunnitella sellaiset rajapinnat, joilla ohjelma voidaa jakaa funktionaaliseen ja ei-funktionaaliseen osaan ja miten osien kommunikointi hoidetaan. (C-ohjelmiin voi liittää Haskel-koodia, Java-kielen funktionaaliset ominaisuudet).

Haskell-kieli - Julkaistu 1987 - Nimetty Haskell Brooks Curryn mukaan

Ominaisuudet - Täysin funktionaalinen kieli, suoritus on sievennystä, "tila"-käsitettä ei ole - Käännettävä kieli, käännösaikainen tyypitys, tyyppipäättely - Laiska laskenta, mitään ei tehdä ennen kuin tulosta tarvitaan, lausekkeet evaluoidaan vasta tarvittaessa. - paramametrejä sievennetään vain sen verran, että nähdään soveltuuko tämä sääntö - sievennyksen tulokset muistetaan, jotta sievennystä ei tarvitse tehdä uudelleen - "Arvojen" voidaan kuvitella olevan parametrittomia funktioita, jota tarvittaessa suoritetaan. - Muuttuja sisältää aina viitteen funktioon, joka laskee muuttujan arvon. - Laiska laskenta mahdollistaa äärettömät tietorakenteet, esim. lista voidaan määäritellä [1..] - Ei ole sijoituslausetta, nimentö (nimi on lyhennysmerkintä arvolle): nimi = arvo

Tietotyypit - Vahva tyypitys ja staattinen tyypitys, funktioilla ja lausekkeilla on tyyppi. - Tyyppi tiedetään jo käännösaikana, tyypinmuunnokset tehtävä eksplisiittisesti, ei automaattista tyypinmuunnosta. - Tyyppien automaattinen päättely. - Haskell -kielessä ei aina tarvitse määritetllä funktion tai ilmaisun (lausekkeen) tyyppiä. - Kieleen on rakennettu mekanismi, jonka avulla voidaan automaattisesti päätellä funktion tyypi (signature), joka ei kuitenkaan aina toimi oikein.

- Jos on määritelty funktio, ghci osaa päätellä (ainakin useimmiten) funktion tyypin esimerkiksi: isdigit a = elem a ['0'..'9'] *Main> :t isdigit isdigit :: Char -> Bool *Main> :t head head :: [a] -> a *Main> :t (++) (++) :: [a] -> [a] -> [a] - a on tyyppimuuttuja (type variable), joka voidaan korvata millä tahansa tyypillä - Funktion tyypit oikealta, ensimmäinen on palautuksen tyyppi ja loput ovat argumenttien tyyppejä.

Perustietoyypit - Int on prosessorikohtainen ja Integer voi käsitellä mielivaltaisen mittaisia kokonaislukuarvoja - Float ja Double ovat liukuluja joiden koko on normaalitarkkuus ja kaksoistarkkuus - Huomaa, String ja [char] ovat identtisiä

Tyyppiluokat (typeclasses) *Main> :t summa summa :: Num a => a -> a -> a - Num on typpiluokka ja a on jokin tietotyyppi, 'Num a =>' on tyyppirajoite (class constraint), joka ilmaisee että tyyppi a on Num -tyyppiluokan jäsen. - Num -tyyppiluokkaan kuuluva tyyppi implementoi tyyppiluokassa määritelyn käyttäytymisen, tyyppiluokka on suunnilleen sama kuin Java-kielen interface. - Eq -luokan jäsenet totetutttavat operaatiot == ja /= - Ord -luokan jäsenet, jotka ovar myös Eq -luokan jäseniä, totetuttavat operaatiot >, >=, < ja <= - Show ja Read - esittävät arvot Stringeinä ja palauttavat arvot Stringeistä - Enum, luokan jäsenet on järjestty perättäisesti, arvot voidaan luetella

- Num, tyypit joiden jäsenet käyttäytyvät kuten luvut - Integral, Floating, Num - ovat tyyppejä, joilla on integer- ja floating- arvot.

Tyyppien määrittelyt - Funktion tyyppi voidaan määritellä eksplisiittisesti ennen funktion esittelyä summa3 a b c = a + b + c *Main> summa3 4.3 9 1 14.3 summa3i :: Int -> Int -> Int -> Int summa3i a b c = a + b + c *Main> summa3i 4.3 9 1 <interactive>:9:9: No instance for (Fractional Int) arising from the literal 4.3 In the first argument of summa3i, namely 4.3 In the expression: summa3i 4.3 9 1 In an equation for it : it = summa3i 4.3 9 1

Arvon tyyppi voidaan ilmaista *Main> 5::Float 5.0 Joissakin tilanteissa tyyppi on pakko ilmaista *Main> read "2" + 8 10 Mutta *Main> read "2" *Main> read "2" *** Exception: Prelude.read: no parse Ei toimi *Main> read "2"::Int 2

Sivuvaikutukset Perus I/O - Tulostaminen yms. IO on sivuvaikutus ja vaatii tarkan suoritusjärjestyksen, tämä on ristiriidassa funktionaalisuuden kanssa. Haskell-kielessä ongelma on ratkaistu ns. Monadeilla - Monadin perusidea on: syötettä voi lukea ja tulostaa vain tietyssä osassa ohjelmaa. IO:ta tekeville funktioille oma "kategoria", IO-funktiot ketjutetaan näkymättömällä parametrilla: pakko suorittaa peräkkäin. - Jokainen IO -funktio palauttaa arvon. getchar : : IO Char - IO Char ilmaisee: kun getchar käynnistetään se suorittaa toiminnan, joka palauttaa merkin. - Toiminnot, jotka palauttavat ei kiinnostavia arvoja, käyttävät tyyppiä ( ).

- Esimerkiksi putchar -funktio: putchar :: Char -> IO ( ) - Funktio ottaa parametrina merkin ja ei palauta mitään hyödyllistä, vastaa esimerkiksi c- kielen void -tyyppiä. - Toiminnot on jaksotettu käyttämällä >>= -operaattoria, joskin tätä operaattoria ei käytetä suoraan, vaan käytetään "syntaktista sokeria" do -notaatiota piilottamaan em. operaattorin käyttö. - Avainsana do esittelee jonon ilmaisuja, jotka suoritetaan peräkkäisessä järjestyksessä. Ilmaisu voi olla toiminto tai hahmo, joka on sidottu <- operaattoria käyttävän toiminnan tulokseen. Tai joukkoon let:iä käyttäviä paikallisia määrittelyjä. - do -notaatiossa voidaan käyttää layouttia samalla tavalla kuin let tai where, joten sulut ja puolipisteet voidaan korvata sisennyksellä. do -notaatio "liimaa" useita I/O -toimintoja yhteen. main :: IO () main = do c <- getchar putchar c

- nimen main käyttö on tärkeä: main on main-ohjelman aloituskohta, kuten vaikka c-kielen main. main -tyypin tulee olla IO ( ). Nimeä main käytetään erityistarkoituksessa vain modulissa Main. - Ohjelma suorittaa kaksi toimintoa peräkkäisesti, ensin se lukee yhden merkin sitoen sen 'muuttujaan' c ja sitten tulostaa merkin. Toisin kuin let -määrittelyllä esitellyt muuttujat, jotka ovat näkyvät kaikille, on <- määrittelyllä määritelty muuttuja näkyvissä vain seuraavissa lausekkeissa. - Esim. halutaan lukea merkki ja tutkia onko luettu merkki 'y' ready :: IO Bool ready = do c <- getchar c == 'y' - c <- getchar voidaan tulkita: suorita I/O -toiminto getchar ja sido sen tulos nimeen c,

- Yllä esitetty ei toimi, koska c == 'y' on pelkkä boolen arvo, ei tapahtuma. Boolen arvolle on rakennettava toiminto, joka palauttaa boolen arvon tuloksena, funktio return tekee sen. return -funktion tyyppi on: return :: a -> IO a - return -funktio toteuttaa joukon perättäisiä alkeistoimintoja. Sen toiminta ei ole sama kuin imperatiivissa kielissä. Korjattu versio: ready :: IO Bool ready = do c <- getchar return (c == 'y')

- Seuraavassa monimutkaisempi I/O-funktio (käyttää rekursiota): getline :: IO String getline = do c <- getchar if c == '\n' then return "" else do l <- getline return (c:l) - else haaran do esittelee yksittäisen lausekejonon, if -rakenteen takia on käytettävä uutta donotaatiota aloittamaan uutta toimintajonoa, jotta voitaan tehdä uusi IO -toiminto. (https://www.haskell.org/tutorial/io.html) - return -funktio sallii tavanomaisten arvojen, kuten boolen arvojen, siirtämisen IO - toiminnolle. - Mutta esimerkisi funkio: f : : Int -> Int -> Int ei voi suorittaa mitään IO-toimintoja, koska IO ei ole paluutyypin arvona. Tämä tarkoittaa että ohjelmia ei voi kovin helposti debugata laittamalla sekaan tulostusoperaatioita.

- Onko seuraava toiminto validi? nimitag = "Terve, nimeni on " ++ getline - Ei, koska ++ operaattori odottaa, että molempien parametrien tulee olla samaa tyyppiä olevia listoja. "Terve, nimeni on " on tyyppiä String ( [Char] ) ja getline on tyyppiä IO String, merkijonoa ja I/O -toimintoa ei voi yhdistää. Merkkijono on saatava ulos I/O -operaatiosta, tämä hoituu <- getline - toiminolla. - do -lohkon viimeistä toimintoa ei voi liittää nimeen, esim main = do foo <- putstrln "Hei kerro nimesi > " -- Ok nimi <- getline faa <- putstrlln ("Terve " ++ nimi ++ ", olet fiksu tyyppi!") -- Ei toimi - do -lohko automaattisesti poimii viimeisen toimen arvon ja liittää sen tulokseensa.

- I/O -toimintoja voidaan suorittaa vain silloin, kun ne ovat nimetty main:issa tai toimet ovat jonkin suuremman I/O -toiminnan sisällä, joka on koottu käyttäen do -lohkoa. - do -lohkoa voidaan käyttää liimaamaan yhteen I/O -toimintoja ja sitten näitä voi käyttää toisessa do -lohkossa jne. Joka tapauksessa näitä voidaan suorittaa vai jos ne ovat main:issa.

Rekursio Toisto ilmaistaan rekursiolla Määritellään funktio viittaamalla määriteltävään funktioon listasumma [] = 0 listasumma (x:xs) = x + listasumma xs *Main> listasumma [] 0 *Main> listasumma [1,2,3,4,5,6] 21 - Listan elementtien summa on 0, jos lista on tyhjä - 1. elementti plus listan loppusosan elementtien summa

Rekursiivisen funktion määritteleminen - Etsi reunaehto, esimerkisi lista on tyhjä, nolla tai 1 yksi... - Kehitä yleisen tapauksen kaava, kaavan oikean puolen täytyy kuvata yksinkertaisempaa ongelmaa kuin vasemman puolen. - Rekursioyhtälö annetaan yleensä kaksiosaisena: toinen osa määrittelee funktion arvon jollain tunnetulla alkuarvolla (alkuarvoja voi olla myös useita) ja toinen osa muulloin. Esimerkki, Quicksort -algoritmi srt [] = [] srt (x:xs) = srt [y y <- xs, y < x] ++ [x] ++ srt [y y <- xs, y >= x] *Main> srt [1,5,3,6,7,9,0] [0,1,3,5,6,7,9]

Tietorakenteet 1. Lista - Listat ja niiden käsittely on olennainen osa funktionaalista ohjelmointitapaa. Listojen käsittelyyn ja muodostamiseen on uhrattu paljon huomiota, jotta niiden käsittely olisi helppoa ja vaivatonta. [1,2,3] [1..3] -- [1,2,3] [1, 3..10] -- [1, 3, 5, 7, 9] - Merkkijonot: ['T','e','r','v',e'] ---> "Terve" - Lisäys alkuun: 0:[1,2,3] ---> [0,1,2,3] - Yhdistely: [0,1] ++ [2,3,4] ---> [0,1,2,3,4] - : "Terve" ++ " Ilkka" ---> "Terve Ilkka" - Laiskuus: lista = kaksi funktiota, toinen laskeen listan 1. alkion, toinen loppulistan

- Listakehitelmät (list comprehensions), voidaan määritellä listoja [ runko määreet ] [ n * n n <- [1..20] ] [1,4,9,16,25,36,49,64,81,100,121,144,169,196,225, 256,289,324,361,400] - Äärettömät listat, laiska evaluointi mahdollistaa päättymättömät listarakenteet, koska arvoja evaluoidaan vasta sitten, kun arvoja tarvitaan. parilliset = [2, 4..] parineliot = [ n * n n <- parilliset ] member parineliot 64 -> True

2. Tupla - Tupla on kiinteän kokoinen arvojen joukko, jossa kukin arvo voi olla eri tyyppiä, toisin kuin lista. Lista voi minkä mittainen tahansa, mutta sen kaikkien elementtien on oltava samaa tyyppiä. (2016, "Ohjelmiston rakentaminen") - Tuplan erikoistapaus on ( ), joka sisältää nolla elementtiä. Tällä tyypillä on vain yksi arvo, joka kirjoitetaan myös ( ), on suunnilleen sama kuin c-kielen void.

Funktiot Funktion määrittely: plus x y = x + y Funktion kutsuminen plus (plus 3 4) 5 Funktion paloittainen määrittely kertoma 0 = 1 kertoma n = n * kertoma (n-1) Parametrien sovitus: tulo [ ] =1 tulo (eka:loput) = eka * tulo loput kertoma2 n = tulo [1..n]

- Laiskuuden vaikutuksia: päättymätön laskenta saattaa paljastaa laiskuuden (estää rinnakkaista laskentaa) Rekursiivinen muuttujan määrittely luvut = 0:[a+1 a <- luvut] fibo = 1:1:[a+b (a,b) <- zip fibo (tail fibo)]

Map - map-funktio saa argumentteina funktion ja listan, ja se käyttää argumenttina saamaansa funktiota jokaiseen listan alkioon. map -funktion palauttama arvo on lista argumenttina saadun funktion tuottamista arvoista. map -funktio on toteutettu seuraavanlaisesti: map _ [ ] = [ ] map f (x:xs) = f x : map f xs - Funktiossa käytetään hahmonsovitusta lista-argumentiin, jos map -funktion kutsussa toisena argumenttina on tyhjä lista, palauttaa funktio tyhjän listan. _ merkintää käytetään kun argumenttia ei tarvita - Jos toisena argunenttina on ainakin yhden alkion sisältävä lista suoritetaan määritelmän toinen rivi ja palautetaan uusi lista jonka ensimmäinen alkio on arvo, jonka funktio f palauttaa argumentilla x. Listan loppuosa on lista, jonka map-funktio palauttaa, kun sitä kutsutaan ensimmäisenä argumenttina funktio f ja toisena agumenttina alkuperäisen listan loppuosa xs. Anonyymi funktio määritellään kirjoitaamalla \ funktion eteen.

Prelude> map (\x -> x^2) [1..20] [1,4,9,16,25,36,49,64,81,100,121,144,169,196,225, 256,289,324,361,400] lisaayksi (xs) = map (\x->x+1) xs *Main> lisaayksi [1,2,3,4] [2,3,4,5] Prelude> import Data.Char Prelude Data.Char> map toupper "Hei maailma!" "HEI MAAILMA!"

foldr, foldl - fold -operaatioita voidaan käyttää toteuttamaan funktioita, joita käytetään listojen läpikäyntiin elementti elementillä, ja funktio palauttaa jotakin edelliselle perustuen - foldl(h, a, l) lista l käydään läpi alusta loppuun, "laskurille" a annetaan alkuarvo, jokaisen alkion z kohdalla päivitetään sen seuraavaksi arvoksi h(a, z) ja lopuksi palautetaan kertynyt lopullinen arvo summa xs = foldl (\acc x -> acc + x) 0 xs summa [3,5,1] 9 -Akkuun acc menee alkuarvoksi 0, funktiota \acc x -> acc + x sovelletaan listan 1. alkioon lukuun 3 (jota x esittää) ja acc saa arvon 0+3. Seuraavaksi x:n arvoksi tulee listan 2. alkio luku 5 ja acc saa arvon 3 + 5 jne. -Funktio: summa foldl (+) 0 tekee saman, koska (+) on sama kuin \acc x -> acc + x

- foldr esimerkki: map' :: (a -> b) -> [a] -> [b] map' f xs = foldr (\x acc -> f x : acc) [] xs map' (+3) [2,4,6] [5,7,9] - Otetaan viimeisin elementti 6 ja sovelletaan siihen funktiota (+3) saadaan tulos 9, joka liitetään akkuun acc, jonka alkuarvo oli [ ] (tyhjä lista) 9:[ ] -> [9], joka akun uusi arvo, seuraavaksi sovelletaan funktiota (+3) arvoon 4 ja saadaan arvo 7, joka liitetään akkuun 7: [9] -> [7, 9] ja lopuksi sovelletaan funktiota (+3) arvoon 2 ja saadaan arvo 5, joka liitetään akkuun 5:[7, 9] -> [5, 7, 9], joka on lopputulos.

Korkeamman asteen funktiot - Kutsutaan myös funktionaalisiksi muodoiksi - Voivat ottaa parametreikseen funktioita - Myös paluuarvo voi olla funktio

Funktioiden yhdistäminen f(x) = x2 ja g(x) = 2x +1 h(x) = f o g(x) = f (g(x)) = f(2x + 1) = (2x+1) 2 = 4x 2 + 4x +1

Konstruktio - Sovelletaan funktiolistan jokaista funktiota yhteen argumenttiin, jolloin saadaan arvojen lista, jossa on yhtä monta alkiota kuin listassa oli funktioita. f(x) = 3x-1 ja g(x) = x2 + 2 (f,g) [3] tuottaa listan [8, 83] - Sovella kaikkiin, jossa samaa funktiota sovelletaan kaikkiin listan alkioihin - Merkitään symbolilla α f(x) = x/3,jolloin α(f, [1,2,3]) saadaan tulokseksi [1/3, 2/3, 1]

Polymorfismi - Funktiolla last pomitaan listan viimeisin alkio last [1,2,3,4,5] 5 last "Ilkka" 'a' last ["ilkka", "tapio", "rasanen"] "rasanen" last (last ["ilkka", "tapio", "rasanen"]) 'n'

- last toimii samalla tavalla oli listan alkioiden tyyppi mikä tahansa. :type last last :: [a] -> a - a on tyyppimuuttuja, joka on jokin tyyppi. Eli voidaan lukea, että last ottaa vastaan listan, jonka kaikki alkiot ovat jotain tyyppiä a ja palauttaa arvon, joka on samaa tyyppiä a. - Kun funktiolla on tyyppimuuttujia allekirjoituksessaan (signature), ilmaisten, että jotkut sen argumenteista voi olla mitä hyvänsä tyyppiä, kutsutaan funktiota polymorfiseksi. Tätä kutsutaan myös parametriseksi polymorfismiksi. - Koodi ei välitä mikä todellinen typpi tulee olemaan, ei edes ole mitään mekanismia jolla voitaisiin selvitää mikä todellinen tyyppi on. Tyyppi on suunnilleen sama kuin Javan generics tyyppi.

Closure (sulkeuma) 1. - Sulkeuma on tietorakenne, joka tallettaa funktion ja sen ympäristön. Käyttää vapaita muuttujia määrittelyssään. mkadder :: Int -> (Int -> Int) mkadder y = \x -> x + y - Funktio ottaa Int -tyyppisen argumentin ja palauttaa funktion: (Int -> Int). Palautettava funktio \x -> x + y sisältää muuttujan y, joka viittaa omaan ympäristöönsä. Kun mkadder -funktiota kutsutaan vaikka arvolla, palautuu sulkeuma, joka sisältää funktion \x -> x + y yhdessä ympäristönsä kannssa. mkadder 5 6 11 1 Sulkeuma on funktion ja sen käyttämien (ei-paikallisten) muuttujien viitetaulun yhdistelmä (wikipedia)

Currying Kaikilla funktioilla on todellisuudessa vain yksi parametri: summa x y = x + y :t summa -> summa :: Num a => a -> a -> a - Summa ottaa parametrin x ja palauttaa toisen funktion, joka ottaa parametrin y ja laskee lopputuloksen - Arvot voidaan kuvitella parametrittomiksi funktioiksi - Mahdollistaa, että vain osan parametreista annetaan summa x y = x + y summa3 = summa 3 :t summa3 -> summa3 :: Integer -> Integer summa3 8 -> 11 map summa3 [2,4,6] -> [5,7,9]

Pattern matching (hahmontunnistus) Funktiot voidaan määritellä hahmontunnistuksella kertoma 0 = 1 kertoma n = n * kertoma (n-1) *Main> kertoma 0 1 *Main> kertoma 1 1 *Main> kertoma 5 120

Hahmontunnistus toimii myös listoilla listanpituus [ ] = 0 listanpituus (_:xs) = 1 + listanpituus xs Alaviiva _ tarkoittaa, että ei tarviste välittää tästä *Main> listanpituus [] 0 *Main> listanpituus ['a','c','d','d'] 4

Vartijat (guards) ja where bmi :: (RealFloat a) => a -> a -> String bmi paino pituus bmi <= laihis = "alipaino" bmi <= normaali = "normaali" bmi <= lihis = "ylipaino" otherwise = "vakava ylipaino" where bmi = paino / pituus ^ 2 laihis = 18.5 normaali = 25.0 lihis = 30.0 -merkillä alkavat rivit kuvaavat ehtoja ja = -merkin oikealla puolella ovat vertailun tulokset. Ehtoja evaluaoidaan ylhäältä alaspäin ja otherwise hoitaa muuta tapaukset, kuten c-kielessä default. where - osiossa esitettyjä nimiä voidaan käyttää vain funktion sisällä, ne eivät näy muualle. Huomaa, että sisennyksellä on merkitystä!

Lähteet: 1. Häkkinen Tuomo 2. Heinonen Matti 3. http://learnxinyminutes.com/docs/haskell/ 4. http://www.seas.upenn.edu/~cis194/lectures.html 5. https://wiki.haskell.org/h-99:_ninety-nine_haskell_problems 6. http://en.wikipedia.org/wiki/monad_(functional_programming) 7. http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html 8. https://www.cs.helsinki.fi/courses/582315/2012/k/k/1