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

Samankaltaiset tiedostot
TIEA341 Funktio-ohjelmointi 1, kevät 2008

Luku 3. Listankäsittelyä. 3.1 Listat

TIEA341 Funktio-ohjelmointi 1, kevät 2008

TIEA341 Funktio-ohjelmointi 1, kevät 2008

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

TIEA341 Funktio-ohjelmointi 1, kevät 2008

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

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

Kompleksilukujen kunnan konstruointi

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

Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005

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

Tietojenkäsittelyteorian alkeet, osa 2

TIEA341 Funktio-ohjelmointi 1, kevät 2008

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

TIEA341 Funktio-ohjelmointi 1, kevät 2008

TIEA341 Funktio-ohjelmointi 1, kevät 2008

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

Todistusmenetelmiä Miksi pitää todistaa?

Jatkeet. TIES341 Funktio ohjelmointi 2 Kevät 2006

Johdatus diskreettiin matematiikkaan (syksy 2009) Harjoitus 3, ratkaisuja Janne Korhonen

Abstraktit tietotyypit. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Ydin-Haskell Tiivismoniste

815338A Ohjelmointikielten periaatteet Harjoitus 7 Vastaukset

Matematiikan johdantokurssi, syksy 2016 Harjoitus 11, ratkaisuista

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

(iv) Ratkaisu 1. Sovelletaan Eukleideen algoritmia osoittajaan ja nimittäjään. (i) 7 = , 7 6 = = =

T Syksy 2004 Logiikka tietotekniikassa: perusteet Laskuharjoitus 7 (opetusmoniste, kappaleet )

Matematiikan tukikurssi

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

Induktiota käyttäen voidaan todistaa luonnollisia lukuja koskevia väitteitä, jotka ovat muotoa. väite P(n) on totta kaikille n = 0,1,2,...

Ohjelmoinnin peruskurssien laaja oppimäärä

Miten osoitetaan joukot samoiksi?

Ohjelmoinnin peruskurssien laaja oppimäärä

1 Lineaariavaruus eli Vektoriavaruus

TIES542 kevät 2009 Rekursiiviset tyypit

Bootstrap / HTDP2 / Realm of Racket. Vertailu

Ohjelmoinnin peruskurssi Y1

811120P Diskreetit rakenteet

5.5 Jäsenninkombinaattoreista

802320A LINEAARIALGEBRA OSA I

Johdatus diskreettiin matematiikkaan Harjoitus 5, Ratkaise rekursioyhtälö

Kompleksilukujen kunnan konstruointi

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

ITKP102 Ohjelmointi 1 (6 op)

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

Matematiikan tukikurssi, kurssikerta 2

MS-A0402 Diskreetin matematiikan perusteet Esimerkkejä, todistuksia ym., osa I

Diskreetin matematiikan perusteet Laskuharjoitus 2 / vko 9

MS-A0402 Diskreetin matematiikan perusteet Esimerkkejä, todistuksia ym., osa I

ITKP102 Ohjelmointi 1 (6 op)

811120P Diskreetit rakenteet

Johdatus matemaattiseen päättelyyn

Matematiikan tukikurssi, kurssikerta 5

802328A LUKUTEORIAN PERUSTEET OSA III BASICS OF NUMBER THEORY PART III. Tapani Matala-aho MATEMATIIKKA/LUTK/OULUN YLIOPISTO

802328A LUKUTEORIAN PERUSTEET OSA III BASICS OF NUMBER THEORY PART III

1 Määrittelyjä ja aputuloksia

Sekalaiset tehtävät, 11. syyskuuta 2005, sivu 1 / 13. Tehtäviä

Johdatus matematiikkaan

ja λ 2 = 2x 1r 0 x 2 + 2x 1r 0 x 2

Ohjelmoinnin perusteet Y Python

Diskreetin Matematiikan Paja Ratkaisuhahmotelmia viikko 1. ( ) Jeremias Berg

Johdatus matematiikkaan

13. Ratkaisu. Kirjoitetaan tehtävän DY hieman eri muodossa: = 1 + y x + ( y ) 2 (y )

14.1 Rekursio tyypitetyssä lambda-kielessä

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

Ohjelmoinnin perusteet Y Python

Diskreetin matematiikan perusteet Laskuharjoitus 1 / vko 8

Taas laskin. TIES341 Funktio ohjelmointi 2 Kevät 2006

Ohjelmoinnin peruskurssien laaja oppimäärä

7. Olemassaolo ja yksikäsitteisyys Galois n kunta GF(q) = F q, jossa on q alkiota, määriteltiin jäännösluokkarenkaaksi

Rekursiiviset tyypit

Matematiikan peruskurssi 2

Tehtävä 4 : 2. b a+1 (mod 3)

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

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

Ohjelmoinnin peruskurssien laaja oppimäärä

Johdatus matematiikkaan

(2n 1) = n 2

DIFFERENTIAALI- JA INTEGRAALILASKENTA I.1. Ritva Hurri-Syrjänen/Syksy 1999/Luennot 6. FUNKTION JATKUVUUS

Yksinkertaiset tyypit

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

Esitetään tehtävälle kaksi hieman erilaista ratkaisua. Ratkaisutapa 1. Lähdetään sieventämään epäyhtälön vasenta puolta:

Matematiikan tukikurssi

f(n) = Ω(g(n)) jos ja vain jos g(n) = O(f(n))

4 Matemaattinen induktio

KOMPLEKSIANALYYSI I KURSSI SYKSY 2012

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

Alkulukujen harmoninen sarja

Tehtävä 2. Osoita, että seuraavat luvut ovat algebrallisia etsimällä jokin kokonaislukukertoiminen yhtälö jonka ne toteuttavat.

Diofantoksen yhtälön ratkaisut

Ohjelmoinnin perusteet Y Python

Matematiikan peruskurssi 2

1.4 Funktioiden kertaluokat

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

Mitä funktionaalinen ohjelmointi on

Cantorin joukon suoristuvuus tasossa

Ohjelmoinnin peruskurssien laaja oppimäärä

Tietorakenteet ja algoritmit

Transkriptio:

3.1. LISTAT 35 destaan pisteittäisesti: init :: [α] [α] init (x : []) = [] init (x : xs) = x : init xs Varuskirjastoon kuuluu myös funktiot take ja drop, jotka ottavat tai tiputtavat pois, funktiosta riippuen, parametrina annetun määrän alkioita annetun listan alusta: take :: Int [α] [α] take _ [] = [] take n xs n < 0 = error "Negatiivinen taken parametri" n == 0 = [] otherwise = x : take (n 1) xs (Drop määritellään samaan tyyliin.) Funktio zip, joka kuuluu varuskirjastoon, vetää vetoketjun kiinni ottamalla kaksi listaa ja pareittain yhdistämällä ne parien listaksi: zip :: [α] [β] [(α, β)] zip (x : xs) (y : ys) = (x, y) : zip xs ys zip = [] Varuskirjastossa on myös sen käänteisfunktio unzip :: [(α, β)] ([α], [β]). Huomautus 5 Huomaa, kuinka jo monien funktioiden tyypeistä voi varsin hyvin päätellä, mitä ne tekevät: concat :: [[α]] [α] ( ) :: (β γ) (α β) (α γ) zip :: [α] [β] [(α, β)] unzip :: [(α, β)] ([α], [β]) Tämän vuoksi kannattaa ottaa käytännöksi aina kirjoittaa uuden funktion tyyppi näkyviin, ennen kuin alkaa sen määritemää kirjoittamaan.

36 LUKU 3. LISTANKÄSITTELYÄ Hyödylliseksi on osoittautunut zipin variantti, jossa parikonstruktorin tilalle voi laittaa haluamansa (sopivantyyppisen) funktion: zipwith :: (α β γ) [α] [β] [γ] zipwith f (x : xs) (y : ys) = f x y : zipwith f xs ys zipwith _ = [] Tällöin zipkin voi saada toisenlaisen muodon: zip :: [α] [β] [(α, β)] zip = zipwith (, ) Lopuksi vielä esitellään varuskirjastoon kuuluva listan indeksointioperaattori: (!!) :: [α] Int α (x : xs)!! n n < 0 = error "Negatiivinen (!!):n parametri" n == 0 = x otherwise = xs!! n 3.2 Lista-aiheisista todistuksista Toisinaan on hyvä voida todistaa, että tietyllä funktiolla on tietty ominaisuus, ihan luotettavuuden parantamisen kannalta. Esimerkiksi voisi olla hyvä todistaa yhtälö (xs ++ ys) ++ zs = xs ++(ys ++ zs). Tällaiset todistukset etenevät parhaiten induktiolla. Listan yli tapahtuvassa induktiossa on kolme askelta; todistaakseen, että kaikilla listoilla on jokin ominaisuus, pitää todistaa seuraavat asiat: Perustapaus P( ) Todista, että ko. ominaisuus on (listatyyppisellä) pohjalla. Perustapaus P([]) Todista, että ko. ominaisuus on tyhjällä listalla. Induktioaskel Todista, että jos ko. ominaisuus on mielivaltaisella listalla xs niin silloin kaikilla (tyypiltään sopivilla) alkioilla x ko. ominaisuus on listalla x : xs. Jos jättää todistamatta pohjan perustapauksen mutta todistaa muut, tulee ominaisuus todistettua vain äärellisille listoille; jos jättää todistamatta tyhjän listan perustapauksen mutta todistaa muut, tulee ominaisuus todistettua vain osittaislistoille.

3.3. MUUTAMIA LISTAFUNKTIONAALEJA 37 Todistetaan yhtälö induktiolla xs:n suhteen: (xs ++ ys) ++ zs = xs ++(ys ++ zs) Perustapaus P( ) ( ++ ys) ++ zs = ++ zs = = ++(ys ++ zs). Perustapaus P([]) ([] ++ ys) ++ zs = ys ++ zs = [] ++(ys ++ zs). Induktioaskel Tehdään induktio-oletus, että mielivaltaisella listalla xs pätee (xs ++ ys) ++ zs = xs ++(ys ++ zs). Olkoon x mielivaltainen tyypiltään sopiva alkio. Tällöin pätee ((x : xs) ++ ys) ++ zs = (x : (xs ++ ys)) ++ zs = x : ((xs ++ ys) ++ zs) = x : (xs ++(ys ++ zs)) = (x : xs) ++(ys ++ zs). 3.3 Muutamia listafunktionaaleja Funktio-ohjelmoinnin yksi merkittävimmistä yksittäisistä keinovaroista on funktionaalit. Funktionaaleilla voidaan abstrahoida erilaisia tietorakenteiden läpikäyntejä kuten tee tämä kaikille listan alkioille (map) tai muodosta lista antamalla tälle operaattorille näiden listojen alkiot pareittain operandeiksi (zipwith). Huomautus 6 Funktionaali ( functional mutta tavallisemmin higher-order function, HOF) on funktio, jonka yksi tai useampi parametri on funktio. Koska kaikki Haskellin funktiot ovat curry ttuja, voidaan ajatella, että mikään funktio ei oikeastaan koskaan palauta funktiota, toisinaan funktioita vain kutsutaan vajaalla parametrilistalla. Toisaalta samasta syystä kaikki moniparametriset funktiot toimivat palauttamalla funktion. Näin ollen ei ole mieltä määritellä, että funktionaali olisi funktio, jonka yksi tai useampi parametri tai paluuarvo on funktio. Funktionaali map, joka kuuluu varuskirjastoon, määritellään seuraavasti: map :: (α β) [α] [β] map f [] = [] map f (x : xs) = f x : map f xs Esimerkki 20 map (λn n + 1) [4, 2, 9, 4, 5] = [5, 3, 10, 5, 6]

38 LUKU 3. LISTANKÄSITTELYÄ Funktionaalille map pätee muutama yhtälö (tod. harj.): map id = id (3.1) map (f g) = map f map g (3.2) f head = head map f kun f = (3.3) map f tail = tail map f (3.4) map f reverse = reverse map f (3.5) map f concat = concat map f (3.6) Huomautus 7 Funktioiden yhtäsuuruus määritellään tavallisesti ekstensionaalisesti, jolloin f = g pätee jos ja vain jos f x = g x pätee kaikilla tyypiltään sopivilla lausekkeilla x. Toinen määrittelyvaihtoehto on intensionaalinen, jossa funktioiden yhtäsuuruuteen vaikutttaa jokin muukin tekijä kuin niiden pisteittäinen yhtäsuuruus esimerkiksi se, ovatko ne peräisin samasta funktiomäärittelystä, tai funktioiden ohjelmakoodin osoite muistissa. Tässä monisteessa noudatamme ekstensionaalista määrittelyä. Huomaa, että vaikka määrittelemme näin funktioiden yhtäsuuruuden, silti operaattoria (==) ei ole funktiotyypeille määritelty, sillä funktioiden ekstensionaalisen yhtäsuuruuden testaamiselle ei ole yleispätevää algoritmia. Toinen hyödyllinen funktionaali on filter: filter :: (α Bool) [α] [α] filter p [] = [] filter p (x : xs) p x = x : filter p xs otherwise = filter p xs Se tuottaa annetusta listasta listan, josta on raakattu pois ne alkiot, jotka eivät täytä annettua ehtoa. Esimerkki 21 1. filter even [1, 2, 4, 5, 32] = [2, 4, 32] 2. let { square x = x x } in (sum map square filter even)[1..10] = 220 Huomautus 8 Funktio even :: Integral α α Bool kuuluu varuskirjastoon. Erityissyntaksi [1..10] edustaa kokonaislukujen listaa väliltä 1 10.

3.3. MUUTAMIA LISTAFUNKTIONAALEJA 39 Edellisessä esimerkissä esiintyi varuskirjastoon kuuluva funktio sum :: Num α [α] α. Se voidaan määritellä esimerkiksi näin: sum :: Num α [α] α sum [] = 0 sum (x : xs) = x + sum xs Vastaavalla tavalla voitaisiin määritellä esimerkiksi myös prod: prod :: Num α [α] α prod [] = 1 prod (x : xs) = x prod xs Nämä kaksi määritelmää ovat saman suunnittelumallin tapauksia; se malli voidaan esittää esimerkiksi näin, kun h on määriteltävä funktio, e on sen arvo tyhjän listan tapauksessa ja ( ) on määritelmässä käytettävä operaattori: h [] = e Funktio h muuttaa listan h (x : xs) = x h xs x 1 : (x 2 : (x 3 : (x 4 : []))) arvoksi x 1 (x 2 (x 3 (x 4 []))). Tämä määrittelykaava voidaan esittää funktionaalina foldr: foldr :: (α β β) β [α] β foldr f e [] = e foldr f e (x : xs) = x f (foldr f e xs) Tämä funktionaali kuuluu toki varuskirjastoon. Se on ehkä tärkein yksittäinen listafunktio(naali), sillä sen avulla voidaan melkein kaikki muut listafunktiot määritellä: concat = foldr (++) [] reverse = foldr (λ x xs xs ++ x) [] length = foldr (λ _ n n + 1) 0 sum = foldr (+) 0 prod = foldr ( ) 1 map f = foldr ((:) f) []

40 LUKU 3. LISTANKÄSITTELYÄ Kaikkia listafunktioita ei kuitenkaan voi määritellä foldr:n pohjalta. Yksi tällainen esimerkki on zip. Aina foldr-määritelmä ei ole tehokkain mahdollinen; esimerkiksi yllä annettu reversen määritelmä on asymptoottiselta aikavaativuudeltaan neliöllinen, kun aiemmin annettiin lineaariaikainen määritelmä. Määritelläänpä nyt toisenlainen versio samasta ideasta: foldl :: (α β α) α [β] α foldl f e [] = e foldl f e (x : xs) = foldl f(z f x) xs Assosiatiivisilla operaattoreilla ( ) pätee foldr ( ) = foldl ( ) (harjoitustehtävä!). Jos operaattori ei ole assosiatiivinen, foldr ja foldl antavat eri tulokset, sillä ne ryhmittelevät laskun eri lailla: foldr ( ) e [x 1, x 2, x 3 ] = x 1 (x 2 (x 3 e)) foldl ( ) e [x 1, x 2, x 3 ] = ((e x 1 ) x 2 ) x 3 Funktionaalilla foldl onnistuu nyt reversen tehokas määrittely: Myös foldl kuuluu varuskirjastoon. reverse = foldl (flip (:)) [] Huomautus 9 Funktionaali flip määritellään varuskirjastossa seuraavasti: flip :: (α β γ) β α γ flip f x y = f y x Varuskirjastossa määritellään myös funktionaalit foldr1, foldl1 :: (α α α) [α] α, jotka eivät käsittele tyhjän listan tapausta vaan ne lopettavat rekursionsa yhden alkion listan tapaukseen ne eivät myöskään siten tarvitse tyhjän listan tapauksen paluuarvoa parametrikseen. Ne ovat hyödyllisiä silloin, kun tyhjän listan tapaukselle ei ole olemassa mitään järkevää vastausta. Esimerkki 22 Varuskirjaston funktiot maxlist ja minlist määritellään seuraavasti: maximum, minimum :: Ord α [α] α maximum = foldl1 max minimum = foldl1 min (Funktiot max ja min ovat tyyppiluokan Ord metodeja.)

3.4. ÄÄRETTÖMISTÄ LISTOISTA 41 3.4 Äärettömistä listoista Äärettömällä listalla tarkoitetaan lauseketta, jolla ei ole normaalimuotoa ja jonka yleisin mahdollinen tyyppi on jokin listatyyppi. Esimerkki 23 Seuraavat ovat äärettömiä listoja: 1. let { ones = 1 : ones } in ones 2. let { nats = 0 : map (λn n + 1) nats } in nats 3. let { fib = 1 : 1 : map (uncurry (+))(zip fib (tail fib)) } in nats Edellisen esimerkin kaikkien lausekkeiden arvo on, koska niillä ei ole normaalimuotoa. Toisaalta esimerkiksi ensimmäisen lausekkeen arvo on myös 1 :, koska (:) on löyhä; esimerkiksi head (1 : ) = 1. Samalla tavalla voidaan sanoa, että sen arvo on 1 : 1 : tai 1 : 1 : 1 : ja niin edelleen. Dana Scottin semanttisten alueiden teoriassa 3 jokaiseen tietotyyppiin eli semanttiseen alueeseen liittyy vertailuoperaattori, joka vertaa alueen arvojen (lausekkeiden) informaatiosisältöä, tai oikeastaan laskennan määrää. Jokaisessa alueessa on tämän suhteen pienin alkio, jota merkitään ; siinä ei ole yhtään informaatiosisältöä. Listatyypissä on voimassa vertailuketju 1 : 1 : 1 :. Voidaan ajatella, että tämän ketjun päässä on ketjun raja-arvo, joka on ääretön lista. 3. Ks. esim. OKP:n syksyn 2002 moniste.