TIEA341 Funktio-ohjelmointi 1, kevät 2008 Luento 4 Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 17. tammikuuta 2008
Modulin viimeistelyä module Shape ( Shape ( Rectangle, E l l i p s e, R t T r i a n g l e, Polygon ), Radius, Side, Vertex, a r e a ) where modulinnimen ja where-avainsanan välissä voi olla sulkeissa vientilista (engl. export list) luetellaan ne nimet, jotka ovat modulin tuojien (import) käytettävissä data-tyypit koostimineen vietävät koostimet suluissa koostinlistan paikalla voi olla (...) tarkoittaa että kaikki ko. tyypin koostimet viedään type-tyypit, funktiot ja vakiot jos vientilista (sulkeineen) puuttuu, kaikki viedään where aloittaa uloimman sisennysalueen
Vaihtoehto let-in-rakenteelle a r e a ( Polygon ( v1 : vs ) ) = p o l y A r e a vs where p o l y A r e a : : [ V e r t e x ] > Double p o l y A r e a ( v2 : v3 : vs ) = t r i A r e a v1 v2 v3 + p o l y A r e a ( v3 : vs ) p o l y A r e a _ = 0 funktion määritelmään voi liittyä where-osa where aloittaa sisennysalueen parametrihahmojen luomat muuttujat näkyvät where-osassa where-osassa määritellyt nimet näkyvät koko funktion määrittelyssä tyyliohje: let muuttujille, where funktioille syntaksihuomio: yksöislainausmerkit kuinka monta tahansa on sallittu nimen lopussa, kuten matikassa.
Kuvioiden piirtoa: Yksikkömuunnos cmtopixel : : Double > I n t cmtopixel x = round ( 4 0 x ) pixeltocm : : I n t > Double pixeltocm x = fromintegral x / 40 oletetaan, että kuvioiden mittayksikkö on senttimetri 100 DPI on noin 40 pikseliä senttimetrillä miksi ei 40 round x? miksi ei fromintegral (x div 40)?
Kuvioiden piirtoa: Koordinaatistomuunnos xwin, ywin : : I n t xwin = 600 ywin = 500 t r a n s : : V e r t e x > P oint t r a n s ( x, y ) = ( xwin div 2 + cmtopixel x, ywin div 2 cmtopixel y ) t r a n s L i s t : : [ V e r t e x ] > [ P oint ] t r a n s L i s t [ ] = [ ] t r a n s L i s t ( p : ps ) = t r a n s p : t r a n s L i s t ps
Kuvioiden piirtoa: Kuviosta grafiikaksi s h a p e T o G r a p h i c : : Shape > G r a p h i c s h a p e T o G r a p h i c ( R e c t a n g l e s1 s2 ) = p o l y g o n $ t r a n s L i s t $ [( s1 /2, s2 / 2 ), ( s1 /2, s2 / 2 ), ( s1 /2, s2 / 2 ), ( s1 /2, s2 / 2 ) ] s h a p e T o G r a p h i c ( E l l i p s e r 1 r 2 ) = e l l i p s e ( t r a n s ( r1, r 2 ) ) ( t r a n s ( r1, r 2 ) ) s h a p e T o G r a p h i c ( R t T r i a n g l e s1 s2 ) = p o l y g o n $ t r a n s L i s t $ [ ( 0, 0 ), ( s1, 0 ), ( 0, s2 ) ] s h a p e T o G r a p h i c ( P o lygon v t s ) = p o l y g o n ( t r a n s L i s t v t s )
Abstrahointi III: Polymorfismi l e n I n t e g e r s : : [ Integer ] > Integer l e n I n t e g e r s [ ] = 0 l e n I n t e g e r s (_: xs ) = 1 + l e n I n t e g e r s xs l e n S t r i n g s : : [ String ] > Integer l e n S t r i n g s [ ] = 0 l e n S t r i n g s (_: xs ) = 1 + l e n S t r i n g s xs y l e i s t y y : l e n : : [ a ] > Integer l e n [ ] = 0 l e n (_: xs ) = 1 + l e n xs
pienellä alkukirjaimella alkava tyyppinimi on tyyppimuuttuja len :: [a] > Integer tulkitaan: Funktion len tyyppi on a > Integer olkoon a mikä tyyppi tahansa. len ei välitä siitä, mitä listassa on sisällä jokaisella funktiolla on yksikäsitteinen yleisin tyyppi (engl. principal type), josta funktion jokainen tyyppi(erikoistapaus) on saatavissa korvaamalla tyyppimuuttujia tyypeillä [Int] > Integer on eräs len n tyyppi [String] > Integer on eräs len n tyyppi Num b =>[a] > b on sen yleisin tyyppi
Muutama esimerkki vakiokirjastosta head : : [ a ] > a head ( x :_) = x head [ ] = e r r o r " P r e l u d e. head : empty l i s t " t a i l : : [ a ] > [ a ] t a i l (_: x s ) = x s t a i l [ ] = e r r o r " P r e l u d e. t a i l : empty l i s t " l a s t : : [ a ] > a l a s t [ x ] = x l a s t (_: x s ) = l a s t x s l a s t [ ] = e r r o r " P r e l u d e. l a s t : empty l i s t " i n i t : : [ a ] > [ a ] i n i t [ x ] = [ ] i n i t ( x : x s ) = x : i n i t x s i n i t [ ] = e r r o r " P r e l u d e. i n i t : empty l i s t " n u l l : : [ a ] > B o o l n u l l [ ] = True n u l l (_:_) = F a l s e
Abstrahointi IV: Funktionaalit Mitä yhteistä? t r a n s L i s t : : [ V e r t e x ] > [ P o i n t ] t r a n s L i s t [ ] = [ ] t r a n s L i s t ( v : vs ) = t r a n s v : t r a n s L i s t ps s c a l a r P r o d u c t : : Double > [ Double ] > [ Double ] s c a l a r P r o d u c t c [ ] = [ ] s c a l a r P r o d u c t c ( x : xs ) = c x : s c a l a r P r o d u c t c xs
Yleistetään map on v a k i o k i r j a s t o s s a map : : ( a > b ) > [ a ] > [ b ] map f [ ] = [ ] map f ( x : xs ) = f x : map f xs t r a n s L i s t : : [ V e r t e x ] > [ P oint ] t r a n s L i s t vs = map t r a n s vs s c a l a r P r o d u c t : : Double > [ Double ] > [ Double ] s c a l a r P r o d u c t c xs = map f xs where f x = c x
funktio voi ottaa parametrikseen toisen funktion parametrifunktiota voidaan kutsua kuten mitä tahansa funktiota mikä tahansa (oikean tyyppinen) funktio voidaan antaa argumentiksi funktioparametrin tyyppi tulee sulkeisiin funktion tyypissä map :: (a > b) > [a] > [b] funktio, joka ottaa funktioparametrin, on funktionaali eli korkean kertaluvun funktio (engl. higher-order function)
Kuriositeetti: funktion kertaluku määritelmä: 1. vakio (eli muuttuja eli funktio, joka ei ota lainkaan parametreja) on nollannen kertaluvun funktio 2. jos funktion parametrina on funktioita, joiden suurin kertaluku on n, kyseessä on (n + 1):nnen kertaluvun funktio funktio, jolla ei ole funktioparametreja, on ensimmäisen kertaluvun funktio funktio, jonka tyyppi on (Int > Int) > Int, on toisen kertaluvun funktio mikä on map n kertaluku?
Lyhennysmerkintöjä \ parametrit > lauseke let f parametrit = lauseke in f (missä f on jokin nimi, joka ei esiinny parametreissa eikä lausekkeessa) ( lauseke @) \ x > lauseke @ x (missä @ on jokin operaattori) (missä x on jokin nimi, joka ei ole ko. operaattori eikä esiinny lausekkeessa) (@ lauseke) \ x > x @ lauseke (missä @ on jokin operaattori, ei kuitenkaan vähennyslasku) (missä x on jokin nimi, joka ei ole ko. operaattori eikä esiinny lausekkeessa)
Esimerkkejä t u p l a a xs = map (2 ) xs s c a l a r P r o d u c t c xs = map ( c ) xs s c a l a r P r o d u c t c xs = map (\ x > c x ) xs
s e u r a a v a t ovat k a i k k i v a k i o k i r j a s t o s s a mapm : : ( a > IO b ) > [ a ] > IO [ b ] mapm f [ ] = return [ ] mapm f ( x : xs ) = do x < f x xs < mapm f xs return ( x : xs ) mapm_ : : ( a > IO b ) > [ a ] > IO ( ) mapm_ f [ ] = return ( ) mapm_ f ( x : xs ) = do f x mapm_ f xs type String = [ Char ] putstr : : String > IO ( ) putstr s t r = mapm_ putchar s t r
module Main where f o r e a c h : : [ a ] > ( a > IO b ) > IO [ b ] f o r e a c h xs f = mapm f xs foreach_ : : [ a ] > ( a > IO b ) > IO ( ) foreach_ xs f = mapm_ f xs l i s t a = [ " k i s s a ", " k o i r a ", " boa " ] main : : IO ( ) main = foreach_ l i s t a $ \ x > do putstr " Otus : " putstrln x
module Main where i m p o r t i m p o r t i m p o r t Draw Shape Graphics. SOE. Gtk f o r e a c h _ : : [ a ] > ( a > IO b ) > IO ( ) f o r e a c h _ x s f = mapm_ f x s t y p e ColoredShape = ( Color, Shape ) drawshapes : : Window > [ ColoredShape ] > IO ( ) drawshapes w x s = f o r e a c h _ x s $ \ ( c, s ) > drawinwindow w $ w i t h C o l o r c $ s h a p e T o G r a p h i c s c i r c l e : : R a d i u s > Shape c i r c l e r = ( E l l i p s e r r ) c o n C i r c l e s : : [ Shape ] c o n C i r c l e s = map c i r c l e [ 2. 4, 2. 1.. 0. 3 ] c o l o r e d C i r c l e s : : [ C o l o r e d S h a p e ] c o l o r e d C i r c l e s = z i p [ Black, Blue, Green, Cyan, Red, Magenta, Yellow, White ] c o n C i r c l e s main : : IO ( ) main = r u n G r a p h i c s $ do w < openwindow " Tikkataulu " ( xwin, ywin ) drawshapes w c o l o r e d C i r c l e s s p a c e C l o s e w
z i p on v a k i o k i r j a s t o s s a zip : : [ a ] > [ b ] > [ ( a, b ) ] zip ( a : as ) ( b : bs ) = ( a, b ) : zipwith z as bs zip = [ ] xs 1 2 3 4 5 ys 2 4 6 8 10 zip xs ys (1,2) (2,4) (3,6) (4,8) (5,10)
Mitä yhteistä? l i s t S u m : : [ Double ] > Double l i s t S u m [ ] = 0 l i s t S u m ( x : xs ) = x + l i s t S u m xs l i s t P r o d : : [ Double ] > Double l i s t P r o d [ ] = 1 l i s t P r o d ( x : xs ) = x l i s t S u m xs
Yleistetään f o l d r on v a k i o k i r j a s t o s s a f o l d r : : ( a > b > b ) > b > [ a ] > b f o l d r op a [ ] = a f o l d r op a ( x : xs ) = x op f o l d r op a xs l i s t S u m : : [ Double ] > Double l i s t S u m xs = f o l d r (+) 0 xs l i s t P r o d : : [ Double ] > Double l i s t P r o d xs = f o l d r ( ) 1 xs
foldr (+) 0 (1:(2:(3:(4:[])))) = (1+(2+(3+(4+0 )))) foldr (*) 1 (1:(2:(3:(4:[])))) = (1*(2*(3*(4*1 )))) (foldr ~ fold right)
Toisaalta... f o l d l on v a k i o k i r j a s t o s s a f o l d l : : ( a > b > a ) > a > [ b ] > a f o l d l op a [ ] = a f o l d l op a ( x : xs ) = f o l d l op ( a op x ) xs l i s t S u m : : [ Double ] > Double l i s t S u m xs = f o l d l (+) 0 xs l i s t P r o d : : [ Double ] > Double l i s t P r o d xs = f o l d l ( ) 1 xs
foldl (+) 0 (1:(2:(3:(4:[])))) = ((((0+1)+2)+3)+4) foldl (*) 1 (1:(2:(3:(4:[])))) = ((((1*1)*2)*3)*4) (foldl ~ fold left)
Fold on monikäyttöinen... k a i k k i o v a t v a k i o k i r j a s t o s s a map : : ( a > b ) > [ a ] > [ b ] map f l s t = f o l d r (\ x x s > f x : x s ) [ ] l s t (++) : : [ a ] > [ a ] > [ a ] x s ++ y s = f o l d r ( : ) y s x s c o n c a t : : [ [ a ] ] > [ a ] c o n c a t l s t = f o l d r (++) [ ] l s t and : : [ B o o l ] > B o o l and l s t = f o l d r (&&) True l s t o r : : [ B o o l ] > B o o l o r l s t = f o l d r ( ) F a l s e l s t r e v e r s e : : [ a ] > [ a ] r e v e r s e l s t = f o l d l (\ x y > y : x ) [ ] l s t f i l t e r : : ( a > B o o l ) > [ a ] > [ a ] f i l t e r t s t l s t = f o l d r f [ ] l s t where f x xs t s t x = x : xs o t h e r w i s e = x s
Vahdit f u n k t i o p a r a m e t r i t ehto1 = l a u s e k e 1 ehto2 = l a u s e k e 2 funktion määritelmään voi liittyä useita vahteja vahti on Bool-tyyppinen lauseke kuhunkin vahtiin liittyy vastinelauseke vahdit testataan järjestyksessä: ensimmäisen, joka antaa True, vastinelauseke tulee funktion määritelmäksi jos mikään vahti ei anna True, toimitaan ikään kuin parametrien hahmonsovitus olisi epäonnistunut määritelmään voi liittyä vain yksi where-osa, vaikka vahteja olisikin useita vahteja voidaan käyttää myös case-of-rakenteen kanssa vakiokirjastossa määritellään otherwise = True