Nada, KTH Tentamensdag 2003 - maj -24 Tentamen i Funktionell Programmering Skrivtid 5 h Antalet uppgifter : 4 ( 26p +29p +20p + 25 p= 100 p ) Lärare, jourhavande lärare : Leif Kusoffsky ------------------------------------------------------------------------------------------------------------------ Tillåtna hjälpmedel: Hudak : The Haskell School of Expression Häften i årets kursbunt : Kompletterande material för 2D1370 Funktionell programmering Datastrukturer i Haskell (Björn von Sydow) Laborationer 2D1370 Funktionell programmering ----------------------------------------------------------------------------------------------------------------- I alla deluppgifterna, använd Haskell och skriv typen för de identifierare du definierar. 1 Denna funktion nextrand :: Int -> Int nextrand n = (25173 * n + 13849) mod 65536 kan, givet ett tal i, som resultat ge ett nytt tal j i intervallet 0 <= j < 65536. Detta senare tal kan i sin tur generera ytterligare ett tal osv. De genererade följden är psedoslumptal n rektangulärfördelade i 0 <= n < 65536. a) Definiera en funktion (gärna med hjälp av hjälpfunktioner ) randcolor :: Int -> (Color, Int) som givet ett startslumpvärde i retunerar slumpmässigt (och med samma sannolikhet) ett par med någon av de tre färgerna Blue, Yellow eller Red i datatypen Color (finns i SOEGraphics) och ett nytt slumptal. (11 p) Exempelvis : randcolor 17489 blir (Blue,59134), randcolor 59134 blir (Red,9327) osv b) Definiera, med hjälp nextrand, en funktion randomseq :: Int -> [Int] som ger en ström psedoslumptal n rektangulärfördelade i 0 <= n < 65536. Argumentet, "fröet", är första slumptalet. Använd inte de definitioner som är svar på c) eller d). Exempelvis take 5 (randomseq 17489) blir [17489,59134,9327,52468,43805] (5p) c) Definiera, med hjälp nextrand, randomseq med iterate (sid 327 i Hudaks bok) (5p) d) Definiera, med hjälp nextrand, randomseq med map (jfr Ex 14.4 sid 205 i Hudaks bok). (5p)
2 Uppgiften handlar om ett spel Play21. Exempel på körning (inmatning kursiverat) : Play21> main Tvåpersonersspelet Först till 21. Spelet börjar med värdet 0. Två personer A och B kan sedan växelvis öka värdet med ett eller två genom att trycka på tangenterna 1 eller 2. Först till 21 vinner. Ställningen är 0. A : 1 Ställningen är 1. B : 2 Ställningen är 3. A : 2 Ställningen är 5. B : 1 Ställningen är 6. A : 4 Tryck endast 1 eller 2 Ställningen är 6. A : 1... Ställningen är 16. B : 2 Ställningen är 18. A : 1 Ställningen är 19. B : 2 B vinner Play21> a) Definiera i Haskell en ADT (abstrakt datatyp) ADT21 som exporterar typen ADT21 med operationer: ADT21, -- abstract data type The ADT has a value i::int 0<= i <= 21 zeroadt21,-- :: ADT21 result: an ADT21 with value == 0 onemore, -- :: ADT21 -> ADT21 -- result : ADT21 with value == argument ADT21 value + 1 twomore, -- :: ADT21 -> ADT21 -- result : ADT21 with value == argument ADT21 value + 2 ismax, -- :: ADT21 -> Bool result : ADT21 value == 21? fromenum, -- :: ADT21 -> Int result : value == ADT21 value toenum -- :: Int -> ADT21 -- result : ADT21 with value == argument om argument i::int 0<= i <= 21 annars writes " error : toenum for ADT21" -- för full poäng : och alla andra operationer i Enum För full poäng (det är inte jobbigt om man gör rätt) skall definitionen göras så att ADT21 blir en instans av Enum (Hudak sid 33, 334). (14p) b) Definiera med hjälp av ADT21 modulen Play21 med main :: IO () så att man kan spela som ovan i ingressen. Det är OK att använda modulen Interact (finns i labhäftet sid 23). PS. (15p) OK, det är kanske att ta i att definera en ADT för detta enkla problem, men det är inte så lätt att hitta på realistiska tentauppgifter som samtidigt inte blir för jobbiga att skriva. DS.
3 Denna uppgift är inte alls så svår som man kanske först tror. För att göra uppgiften a) behöver man inte förstå programmet nedan. Detta är en variant av det program som finns i kapitel 17 för att köra våra FAL-program: reactimate :: [Char] -> Behavior Graphic -> IO () reactimate title franprog = rungraphics ( do w <- openwindowex title (Just (0,0)) (Just (xwin, ywin)) drawbufferedgraphic (Just 30) t0 <- timegettime -- start time ch <- newchan -- channel (buffer) addevents w 0.0 ch -- add first events contents <- getchancontents ch --??????? mapm_ (drawpict w t0 ch) ((mkfe franprog) (map snd contents, map fst contents )) ) a) Skriv om programmet utan att använda do-syntax, använd monadoperationerna. 15 p) b) Om man läser programmet ovan skrivet med do-syntax som om det vore ett program skrivet i ett konventionellt imperativt språk, verkar det underligt att man kan få strömmen contents med tills synes alla elementen, raden märkt med --???????, trots att bara det första händelserna har tillfogats kanelen ch (med addevents) "innan", och resten av elementen tillfogas av drawpict (som anropar addevents) "efteråt". Förklara med några meningar varför det fungerar i Haskell. 5p)
4. a) Skriv i FAL (Hudak kap 15, ditt program kan importera det FAL som användes i laborationen och som finns i labhäftet och använder mm istället för tum) ett program Histo som kan rita en stapel i ett histogram (dvs en rektangel) med initialhöjden 1 mm och som sedan ökar sin höjd med mellan 0 och 9 mm, till exempel med 4 mm om man trycker på 4-tangenten, med 6 mm om man trycker på 6-tangenten osv. Man behöver inte behandla fallet att någon tangent som inte är märkt med en siffra trycks. Tips : Jag använde bla stepaccum. (10p) 4. b) Skriv i FAL (Hudak kap 15, ditt program kan importera det FAL som användes i laborationen och som finns i labhäftet och använder mm istället för tum) ett program Quick som gör att man kan spela ett spel för två personer som fungerar så här : På skärmen visas en cirkel som är blå, gul eller röd. Färgen bestäms slumpmässigt. (Man kan använda delar av uppgift 1 förstås). Det gäller sedan att vara den spelare som först trycker på rätt tangent: Om cirkelfärgen är blå gul röd skall spelare A trycka på 1 2 3 skall spelare B trycka på 7 8 9 Den som först trycker på rätt tangent får 1 poäng. Därefter visas en ny färg, och spelet fortsätter. Om motspelaren tyckt på rätt tangent först eller om felaktig tangent trycks sker inget. Aktuell ställning visas hela tiden med en vit stapel för spelare A och en grön stapel för spelare B. Stapelns höjd ökar med 1 mm varje gång en spelare hinner först. Tips : Jag använde bla ett sammansatt Behavior i stepaccum och fstb, sndb. (15p) Bifogas : Översikt av FAL.
sqrt exp sin log cos tan asin acos atan sinh cosh tanh asinh acosh atanh negate abs signum integral + - * / pi time Behavior Float step a ->> b -> a =>>.. stepacccum Event a Behavior a mouse (, ) ell rec >* <* Behavior Bool &&* * when while Event (a->a) Event () Event Char lbp Event b key (Behavior Float, Behavior Float) mm Event (Float, Float) red, blue, yellow green white black translate Behavior Region untilb switch Event (Behavior a) pairb Behavior Color Behavior (a, b) fstb sndb paint over Behavior Picture snapshot_ snapshot withelem_ Event a Event (b, a) withelem test [a] IO()
Förfrågan: Kan du tänka dig att vara labbassistent under läsperiod 1 i kursen programmeringsparadigemer i D2? Kryssa gärna i ett eller båda alternativen. (Naturligtvis ej bindande) O I Haskelldelen av kursen O I Prologdelen av kursen Hur kan jag kontakta dig (tex e-pos, tel ):
Förslag till svar tentamen i Funktionell Programmering 2D1370 2003 - maj -24 randcolor:: Int -> (Color, Int) -- 1 a) randcolor i = (numbertocolor(scal 1 3 i), nextrand i) where numbertocolor :: Int -> Color numbertocolor 1 = Blue numbertocolor 2 = Yellow numbertocolor 3 = Red scal :: Int -> Int -> Int -> Int scal from to n = n div den + from where range = to - from + 1 den = 65536 div range randomseq seed = seed : randomseq2 (nextrand seed) -- 1 b) randomseq seed = (iterate nextrand) seed -- 1 c) randomseq seed = (seed) : map nextrand(randomseq1 seed) -- 1 d) module ADT21 ( -- 2 a) ADT21, -- data type The ADT has a value i::int 0<= i <= 21 zeroadt21, -- :: ADT21 result: an ADT21 with value == 0 onemore, -- :: ADT21 -> ADT21 result : ADT21 with value == argument value + 1 twomore, -- :: ADT21 -> ADT21 result : ADT21 with value == argument value + 2 ismax, -- :: ADT21 -> Bool result : ADT21 value == 21? fromenum, -- :: ADT21 -> Int result : value == ADT21 value toenum -- :: Int -> ADT21 result : writes " error : toenum for ADT21" -- och övriga funktioner i Enum ) where newtype ADT21 = Adt21 Int zeroadt21 :: ADT21 zeroadt21 = Adt21 0 onemore :: ADT21 -> ADT21 onemore (Adt21 21) = Adt21 21 onemore (Adt21 i) = Adt21 (i+1) twomore :: ADT21 -> ADT21 twomore (Adt21 21) = Adt21 21 twomore (Adt21 20) = Adt21 21 twomore (Adt21 i) = Adt21 (i+2) ismax :: ADT21 -> Bool ismax (Adt21 21) = True ismax _ = False -- data också OK instance Enum ADT21 where fromenum (Adt21 i) = i toenum i 0 <= i && i <= 21 = Adt21 i otherwise = error " error : toenum for ADT21"
module Play21 (main) where -- 2 b) import Interact import ADT21 main :: IO () main = makeinteractivprogram startstatus firstpromt f firstpromt :: String firstpromt = " Tvåpersonersspelet Först till 21. \n" ++ " Spelet börjar med värdet 0. Två personer A och B kan sedan\n" ++ " växelvis öka värdet med ett eller två genom att trycka på\n" ++ " tangenterna 1 eller 2. Först till 21 vinner.\n" ++ promt startstatus startstatus :: (ADT21, Bool) startstatus = (zeroadt21, True) f :: (ADT21, Bool) -> String {-input-} -> ( (ADT21, Bool), String, Bool) f ( n, turn) str str == "1" = newn(onemore n, not turn) str == "2" = newn(twomore n, not turn) otherwise = ((n, turn), "Tryck endast 1 eller 2 \n" ++ promt (n,turn), True ) promt :: (ADT21, Bool) -> String promt (n, True) = "Ställningen är " ++ show (fromenum n) ++ ". A : " promt (n, _ ) = "Ställningen är " ++ show (fromenum n) ++ ". B : " newn :: (ADT21, Bool) -> ((ADT21, Bool), String, Bool) newn ( n, turn) ismax n = ( (n, turn), finalpromt turn, False) otherwise = ( (n, turn), promt (n, turn), True) finalpromt :: Bool -> String finalpromt True = " B vinner " finalpromt _ = " A vinner " reactimate title franprog = rungraphics ( -- 3 a) openwindowex title (Just (0,0)) (Just (xwin, ywin)) drawbufferedgraphic (Just 30) >>= \w -> timegettime >>= \t0 -> newchan >>= \ch -> addevents w (w32totime(0)) ch >> getchancontents ch >>= \contents -> mapm_ (drawpict w t0 ch) ((mkfe franprog) (map snd contents, map fst contents ))) -- 3 b) Som framgår av svaret 3a) är hela argumentet till rungraphics ett uttryck, som som vanligt med Haskells lata evaluering beräknas "utifån och in". map i sista raden till exempel kommer igång med att jobba om det finns lite grann av stömmen att börja med, vilket gör att (drawpict w t0 ch) kan jobba lite grann och fylla på strömmen ch som då får lite mer innehåll som gör mapparna nöjda när man tviongas in i uttrycken igen osv
module Quick (main) where -- 4 import Picture(Picture, Region, containsr) import Fal import SOEGraphics hiding (Region, Event) {- -- byt modulnamnet ovan till Hist, samma importer -- 4 a) main :: IO() main = test (paint white (translate (40, 0.5*sizeB) (rec 3 sizeb))) sizeb :: Behavior Float sizeb = 1.0 stepaccum ( key =>> newfloat ) newfloat :: Char -> Float ->Float newfloat k h = h + read (k:"") -} main :: IO() main = test (ellp over histoi over histoj) ellb :: Behavior Region ellb = ell 30 30 ellp :: Behavior Picture ellp = paint colb ellb colb = fstb (sndb stateb) sizei = fstb (fstb stateb) sizej = sndb (fstb stateb) stateb :: Behavior ((Float, Float), (Color, Int)) stateb = ((1.0,1.0), (Red, 17485 )) stepaccum ( key =>> newstate ) nextrand :: Int -> Int -- se uppgift 1 randcolor:: Int -> (Color, Int) -- se uppgift 1 newstate :: Char -> ((Float, Float), (Color, Int)) -> ((Float, Float), (Color, Int)) newstate 1 ((i,j), (Blue, gc)) = ((i+1, j), randcolor gc) newstate 2 ((i,j), (Yellow, gc)) = ((i+1, j), randcolor gc) newstate 3 ((i,j), (Red, gc)) = ((i+1, j), randcolor gc) newstate 7 ((i,j), (Blue, gc)) = ((i, j+1), randcolor gc) newstate 8 ((i,j), (Yellow, gc)) = ((i, j+1), randcolor gc) newstate 9 ((i,j), (Red, gc)) = ((i, j+1), randcolor gc) newstate _ ((i,j), (g, gc)) = ((i, j), (g, gc)) histoi = paint white (translate (40, 0.5*sizeI) (rec 3 sizei)) histoj = paint green (translate (80, 0.5*sizeJ) (rec 3 sizej))