TIEA341 Funktio-ohjelmointi 1, kevät 2008 Luento 5 Ympärysmitta. Puut. Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 21. tammikuuta 2008
CASE: YMPÄRYSMITTA
Lasketaan kuvioiden ympärysmittoja module P e r i m e t e r ( p e r i m e t e r, module Shape ) where import Shape p e r i m e t e r : : Shape > Double p e r i m e t e r = undefined Syntaksia: vientilistassa module Shape sanoo, että tämä moduli vie kaiken sen minkä Shape undefined :: a on vakiokirjaston monikäyttöinen vakio
Alku on helppoa p e r i m e t e r : : Shape > Double p e r i m e t e r ( R e c t a n g l e s1 s2 ) = 2 ( s1 + s2 ) p e r i m e t e r ( R t T r i a n g l e s1 s2 ) = s1 + s2 + s q r t ( s1 ^2 + s2 ^2) p e r i m e t e r ( Polygon vs ) = f o l d r (+) 0 ( s i d e s vs ) p e r i m e t e r ( E l l i p s e r1 r2 ) = u n d e f i n e d s i d e s : : [ V e r t e x ] > [ S i d e ] s i d e s [ ] = [ ] s i d e s ( v : vs ) = zipwith d i s t B e t w e e n vs ( t a i l vs ++ [ head vs ] ) zip xs ys = zipwith (\x y > (x,y)) xs ys vakiokirjastossa
Matematiikkaa Ellipsin kehän pituus p on p = 4r 1 0 1 ε 2 cos 2 u du, π/2 missä ellipsin eksentrisyys ε on kun r 1 r 2. ε = r 2 1 r 2 2 r 1,
Sarjaratkaisu s 1 = 1 4 ε2, (2i 1)(2i 3) s i = s i 1 4i 2 ε 2, (i 2), ( ) p = 2r 1 π 1 s i. i=1 huomaa, että s i < s i 1! siis: jos katkaistaan summa jostain kohtaa, menetetään vain tarkkuutta
Toiveajattelua... Saisiko rakennettua listan sarjan arvoista? [ 1/4 ecc ^2, 1/4 ecc ^2 ecc ^2 (2 2 1 ) ( 2 2 3) / (4 2^2), j a t k u u l o p u t t o m i i n ] Tästä sitten voisi etsiä ensimmäisen liian pienen alkion ja summata sitä edeltävät alkiot.... ei varmana onnistu?
Onnistuu! 40:s listan alkio syntyisi näin: l e t nexts s i = s (2 i 1) (2 i 3) ecc ^2 / (4 i ^2) i n f o l d l (\ s i > n e x t S s i ) ( 0. 2 5 e c c ^2) [ 2.. 4 0 ] = 8.280358150319898e 29 (kun ecc = 0.5) Tehokkaampaa kuitenkin käyttää kirjastofunktiota scanl, joka toimii kuten foldl mutta palauttaakin listan kaikista välituloksista (eli melkein sen listan, jonka haluamme)! Kaiken lisäksi Haskell ymmärtää äärettömiä listoja. Riittää, kun pyydetään scanl ylläolevaan tapaan listasta [2..].
s c a n l : : ( a > b > a ) > a > [ b ] > [ a ] s c a n l f q [ ] = [ q ] s c a n l f q ( x : xs ) = q : s c a n l f ( f q x ) xs vakiokirjastossa scanl f z [x1, x2] == [z, z f x1, (z f x1) f x2] last (scanl f z xs) == foldl f z xs ikään kuin foldl mutta palauttaa listan kaikista välivaiheista myös olemassa scanr
Koodina p e r i m e t e r ( E l l i p s e r 1 r 2 ) r 1 < r 2 = p e r i m e t e r ( E l l i p s e r 2 r 1 ) o t h e r w i s e = 2 r 1 p i (1 ssum ) where e c c = s q r t ( r 1 ^2 r 2 ^2) / r 1 nexts s i = s (2 i 1) (2 i 3) ecc ^2 / (4 i ^2) s = s c a n l (\ s i > n e x t S s i ) ( 0. 2 5 e c c ^2) [ 2.. ] ssum = f o l d r (+) 0 ( t a k e W h i l e (\ x > x > 0. 0 0 0 1 ) s )
Koodina p e r i m e t e r ( E l l i p s e r 1 r 2 ) r 1 < r 2 = p e r i m e t e r ( E l l i p s e r 2 r 1 ) o t h e r w i s e = 2 r 1 p i (1 ssum ) where e c c = s q r t ( r 1 ^2 r 2 ^2) / r 1 nexts s i = s (2 i 1) (2 i 3) ecc ^2 / (4 i ^2) s = s c a n l (\ s i > n e x t S s i ) ( 0. 2 5 e c c ^2) [ 2.. ] ssum = f o l d r (+) 0 ( t a k e W h i l e (\ x > x > 0. 0 0 0 1 ) s ) [2..] on ääretön lista, johon kuuluvat kaikki kakkosta suuremmat kokonaisluvut (kakkonen mukaan lukien) takewhile (vakiokirjastossa) palauttaa annettua listaa niin kauan kunnes tulee vastaan alkio, jolle annettu funktio palauttaa False t a k e W h i l e : : ( a > B o o l ) > [ a ] > [ a ] t a k e W h i l e p [ ] = [ ] t a k e W h i l e p ( x : x s ) p x = x : t a k e W h i l e p x s o t h e r w i s e = [ ]
PUUT
Rekursiivinen tietotyyppi data I n t L i s t = E m p t y I n t L i s t I n t L i s t E l e m Integer I n t L i s t d e r i v i n g Show tyyppi itse voi olla oman konstruktorinsa parametrina EmptyIntList :: IntList IntListElem :: Integer > IntList > IntList tyypin arvo koostuu jonosta IntListElem-kutsuja ja päättyy (jos päättyy) EmptyIntList-kutsuun arvo voi olla myös ääretön: let ones = IntListElem 1 ones in ones
Parametrisoitu tietotyyppi data L i s t a = EmptyList L i s t I t e m a ( L i s t a ) d e r i v i n g Show tyyppiin viitattaessa on sille annettava tyyppiargumentti EmptyList :: List a ListItem :: a > List a > List a List Integer on isomorfinen IntList n kanssa let ones = ListElem 1 ones in ones Haskellin listatyyppi on isomorfinen List a:n kanssa data [a] = [] (:) a [a] (ei sallittu syntaksi) [] :: [a] (:) :: a > [a] > [a]
Binääripuita (ei vakiokirjastossa) d a t a Tree a = L e a f a Branch ( Tree a ) ( Tree a ) d e r i v i n g Show d a t a I n t e r n a l T r e e a = I L e a f I B r a n c h a ( I n t e r n a l T r e e a ) ( I n t e r n a l T r e e a ) d e r i v i n g Show d a t a FancyTree a b = FLeaf a FBranch b ( FancyTree a b ) ( FancyTree a b ) d e r i v i n g Show Tree n data-alkiot ovat lehtisolmuissa InternalTree n data-alkiot ovat sisäsolmuissa FancyTree n data-alkiot ovat kaikissa solmuissa; lehti- ja sisäsolmujen data voi olla eri tyyppistä FancyTree String Integer sisältää merkkijonoja lehtisolmuissaan ja kokonaislukuja sisäsolmuissaan
(ei vakiokirjastossa) Puuoperaatioita maptree : : ( a > b ) > Tree a > Tree b maptree f ( L e a f x ) = L e a f ( f x ) maptree f ( Branch t1 t2 ) = Branch ( maptree f t1 ) ( maptree f t2 ) f r i n g e : : Tree a > [ a ] f r i n g e ( L e a f x ) = [ x ] f r i n g e ( Branch t1 t2 ) = f r i n g e t1 ++ f r i n g e t2 t r e e S i z e : : Tree a > I n t e g e r t r e e S i z e ( L e a f x ) = 1 t r e e S i z e ( Branch t1 t2 ) = t r e e S i z e t1 + t r e e S i z e t2 t r e e H e i g h t : : Tree a > I n t e g e r t r e e H e i g h t ( L e a f x ) = 0 t r e e H e i g h t ( Branch t1 t2 ) = 1 + max ( t r e e H e i g h t t1 ) ( t r e e H e i g h t t2 )
Abstrakti tyyppi: Äärellinen kuvaus module FiniteMap ( FiniteMap ( ), emptyfm : : FiniteMap k d, unitfm : : k > d > FiniteMap k d, lookupfm : : Ord k => FiniteMap k d > k > Maybe d, insertfm : : Ord k => FiniteMap k d > k > d > FiniteMap k d, deletefm : : Ord k => FiniteMap k d > k > FiniteMap k d, fmtolist : : FiniteMap k d > [ ( k, d ) ], listtofm : : Ord k => [ ( k, d ) ] > FiniteMap k d ) where
FiniteMap on abstrakti tyyppi, koska sen koostimia ei viedä arvojen rakenne on modulin sisäinen salaisuus modulin toteutustekniikkaa voidaan muuttaa vapaasti kunhan rajapinta ei hajoa GHC:n kirjastoissa samankaltainen Data.Map Maybe a = Nothing Just a vakiokirjastossa tilanteisiin, joissa operaatio ei aina onnistu, ja epäonnistuminen ei saa kaataa ohjelmaa Nothing ilmaisee epäonnistumista Just arvo kertoo onnistuneen laskennan lopputuloksen esimerkiksi: case lookupfm fm " k i s s a " of Just x > x ++ " t u l i " Nothing > " e i s a a t u "
Yksinkertainen toteutus b i n a a r i n e n hakupuu ( t a s a p a i n o t t a m a t o n ) d a t a F i n i t e M a p k d = EmptyFM NonEmptyFM ( F i n i t e M a p k d ) k d ( F i n i t e M a p k d ) d e r i v i n g Show emptyfm : : F i n i t e M a p k d emptyfm = EmptyFM unitfm : : k > d > F i n i t e M a p k d unitfm k d = NonEmptyFM EmptyFM k d EmptyFM lookupfm : : Ord k => F i n i t e M a p k d > k > Maybe d lookupfm EmptyFM _ = N o t h i n g lookupfm ( NonEmptyFM l t k d g t ) key key < k = lookupfm l t key key > k = lookupfm g t key o t h e r w i s e = J u s t d i n s e r t F M : : Ord k => F i n i t e M a p k d > k > d > F i n i t e M a p k d i n s e r t F M EmptyFM k d = unitfm k d i n s e r t F M ( NonEmptyFM l t k d g t ) key datum key < k = NonEmptyFM ( insertfm l t key datum ) k d gt key > k = NonEmptyFM l t k d ( insertfm gt key datum ) o t h e r w i s e = NonEmptyFM l t key datum g t