LUNDS TEKNISKA HÖGSKOLA Institutionen för datavetenskap Tentamen EDAA45 Programmering, grundkurs 2017-08-23, 08:00-13:00 Instruktioner Skriv din anonymkod + personlig identifierare här: Tillåtet hjälpmedel: Snabbreferens för Scala & Java. Del A innehåller uppgifter med korta svar som du anger direkt i detta häfte, vilket ska lämnas in tillsammans med omslaget och svaren på del B. Del B innehåller uppgifter med svar i form av programkod som du ska skriva på separat papper. Skriv bara på ena sidan av varje inlämnat blad. Skriv din anonymkod + personlig identifierare överst på varje inlämnat separat papper. Det ska tydligt framgå vilken (del)uppgift du löser. Preliminär poängfördelning Maximalt ges 100p, varav del A omfattar 20p och del B omfattar 80p. För godkänt krävs 50p. Om du på del A erhåller färre än 10p, kan din tentamen komma att underkännas utan att del B bedöms. Poäng och delpoäng som anges ovan och i uppgifterna är preliminära och kan komma att justeras när den slutliga bedömningen fastställs. Upplysningar För att vara tentamensberättigad ska du vara godkänd på alla laborationer och projekt, samt ha genomfört diagnostisk kontrollskrivning. Om du tenterar utan att vara tentamensberättigad annulleras din skrivning. För att undvika att någon skrivning annulleras av misstag kommer alla som, enligt institutionens noteringar, tenterat utan att vara tentamensberättigade att kontaktas via epost. Felaktigheter i institutionens noteringar kan därefter påtalas fram till nästa tentamenstillfälle då resterande skrivningar annulleras. Lösningar läggs ut på kursens hemsida senast dagen efter tentamen. Resultatet läggs in i Ladok när rättningen är klar. 1
2(9) Del A. Evaluera uttryck. Totalt 20p. Du ska fylla i tabellen på nästa sida enligt följande: Antag att du skriver in nedan kod i Scala REPL rad för rad. För varje variabel med namn u1... u10, ange statisk typ (alltså den typ kompilatorn härleder), samt det värde variabeln får efter initialisering, eller sätt i stället kryss i rätt kolumn om det blir ett kompileringsfel respektive exekveringsfel. Vid frånvaro av fel, svara på samma sätt som Scala REPL skriver ut typ respektive värde, enligt exempel u0 i tabellen. Det förekommer varken kompilerings- eller exekveringsfel på raderna till och med deklarationen av u0 nedan, medan efterföljande rader kan innehålla fel. 1 def flip = math.random < 0.5 2 3 sealed trait Fyle { 4 val kansimma: Boolean 5 val kanflyga: Boolean 6 val ärstor: Boolean 7 val id: Int 8 } 9 10 final case class Kråga(id: Int) extends Fyle { 11 override val (kansimma, kanflyga, ärstor) = (false, true, true) 12 } 13 14 final case class Ånka(id: Int, kanflyga: Boolean = flip) extends Fyle { 15 override val (kansimma, ärstor) = (true, true) 16 } 17 18 final case class Pjodd(id: Int, kansimma: Boolean = flip) extends Fyle { 19 override val (kanflyga, ärstor) = (true, false) 20 } 21 22 val fylen = Set[Fyle](Kråga(1), Kråga(2), Ånka(3), Ånka(4), Pjodd(5), Pjodd(6)) 23 24 val u0 = 40 + 2.0 25 26 val u1 = fylen(0) 27 val u2 = fylen(kråga(1)) 28 val u3 = Ånka(kanFlyga = true) 29 val u4 = fylen.tovector.map(_.ärstor).size 30 val u5 = (fylen.map(_.ärstor) ++ fylen.map(_.kansimma)) 31 val u6 = fylen.tovector(6) 32 val u7 = fylen.zipwithindex.tomap.get(kråga(42)) 33 val u8 = fylen.tovector.sortby(_.id).find(_.ärstor).getorelse(ånka(0)).kansimma 34 val u9 = (new Fyle(true, flip, flip, "anonym")).kansimma 35 val u10 = { class Sparv extends Kråga(""); (new Sparv).kanFlyga }
3(9) Vid kompileringsfel sätt kryss. Vid exekveringsfel sätt kryss. Ange statisk typ som kompilatorn härleder om ej kompileringsfel eller exekveringsfel. Ange dynamiskt värde som tilldelas vid exekvering om ej kompileringsfel eller exekveringsfel. u0 Double 42.0 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10
4(9) Del B. Skriva kod. Totalt 80p. Uppgift B1: Mängder, 25p. (correct 3p, isking 4p, next 7p, king 6p, result 2p, sorted 3p) Spelet Keno är ett lotteri där 20 slumpmässiga bollar dras ur en mängd av 70 bollar numrerade från 1 till 70. Dragningen sker med hjälp av en speciell maskin kallad HuxFlux som släpper ut en boll i taget ur en roterande tombola. Dessa regler gäller för Kenospel: 1. Man kan välja hur många nummer man spelar på, vilket påverkar insatsen och utdelningen. 2. Man kan välja att även spela på Kung Keno, som är ett nummer som dras ur de dragna bollarna efter det att alla 20 bollar är dragna. 3. Om man spelar på Kung Keno får man högre utdelning, men kupongen kostar mer. Du ska implementera maskinen HuxFlux och en förenklad Kenokupong enligt nedan med tillhörande beräkning av korrekta nummer och kungsvinst. (Beräkning av insats och utdelningsbelopp ingår ej.) 1 object Keno { 2 type Ball = Int 3 val Min = 1 // Lägsta bollnummer 4 val Max = 70 // Högsta bollnummer 5 val N = 20 // Antalet bollnummer i ett fullständigt Keno-resultat 6 7 case class Coupon(name: String, balls: Set[Ball], iskingplayer: Boolean){ 8 def correct(isdrawn: Set[Ball]): Set[Ball] =??? // Mängd med rätta nummer. 9 def iskingwin(king: Ball): Boolean =??? // Sant om kungsspelare har kung. 10 } 11 12 object HuxFlux { 13 import scala.util.random 14 15 private var isdrawn = Set.empty[Ball] // Hittills dragna bollar. 16 private var kenoking = 0 // Kung Keno. Noll om ej dragen. 17 def reset(): Unit = { isdrawn = Set(); kenoking = 0 } 18 19 def nbrofdrawnballs: Int = isdrawn.size 20 def hasnext: Boolean = nbrofdrawnballs < N 21 def iscomplete: Boolean = nbrofdrawnballs == N 22 23 def next(): Ball =??? // Drar nästa boll. 24 def king: Ball =??? // Ger kung; drar bara ny om ej redan dragen. 25 def result: Set[Ball] =??? // Ger mängd med alla resultatnummer. 26 def sorted: Vector[Ball] =??? // Ger resultat i nummerordning. 27 } 28 } Din kod ska uppfylla instruktionerna i kommentarerna ovan, samt följande krav. Din implementation ska fungera enligt testprogrammet nedan. 1. correct ger en mängd med kupongens rätta nummer givet mängden isdrawn med dragna nummer. 2. iskingwin är sann om spel på Kung Keno (iskingplayer är sann) och king finns i balls. 3. next drar nästa boll ur kvarvarande bollar. 4. king returnerar Kung-Keno-numret, ä.k. kungen. Kungen ska bara dras första gången anrop sker; efterföljande anrop ska ge samma kung. 5. result ger mängd med alla 20 dragna bollar. 6. resultsorted ger sekvens med all 20 dragna bollar sorterade i nummerordning.
5(9) 7. Kontroll av maskinens tillstånd ska ske, enligt följande punktlistan, vid anrop av next, king, och result. Dessa kontroller ska implementeras genom anrop av inbyggda assert(p, errormsg), som då villkoret p är falsk skriver ut strängen errormsg och avbryter exekveringen. Villkoren som ska kontrolleras i respektive metod listas nedan och ska ske allra först i respektive metodkropp: next avbryts med felmeddelandet "balls already complete" om alla bollar är dragna. king avbryts med felmeddelandet "no king available" om ej alla bollar är dragna. result avbryts med felmeddelandet "incomplete result" om ej alla bollar är dragna. 1 object TestKeno { 2 import Keno._ 3 4 val coupons = Vector( 5 Coupon("Anna", balls = Set(1,42,43), iskingplayer = false), 6 Coupon("Björn", balls = Set(3,4,5), iskingplayer = true), 7 Coupon("Sandra", balls = Set(1,2,3,4,42), iskingplayer = true ) 8 ) 9 10 def run(n: Int = 1): Unit = for (i <- 1 to n) { 11 print(s"*** Dragning $i med Kenomaskinen HuxFlux:\nDragningsordning:") 12 while (HuxFlux.hasNext) print(huxflux.next + " ") 13 println("\nnummerordning: " + HuxFlux.sorted.mkString(" ")) 14 println("kung Keno: " + HuxFlux.king) 15 coupons.foreach{ coupon => 16 print(s" $coupon korrekta nummer: ${coupon.correct(huxflux.result)} ") 17 if (coupon.iskingwin(huxflux.king)) println("kung Keno!") else println() 18 } 19 HuxFlux.reset() 20 } 21 22 def main(args: Array[String]): Unit = { 23 if (args.length > 1) scala.util.random.setseed(args(1).tolong) 24 if (args.nonempty) run(args(0).toint) else run() 25 } 26 } Utskriften av en exekvering av TestKeno.main med args = Array("2","77") visas nedan och demonstrerar hur modulen Keno ska fungera. Anna och Björn spelar med 3 nummer, medan Sandra spelar med 5 nummer. Sandra och Björn spelar även på Kung Keno. Sandra vinner Kung Keno i dragning 1. 1 *** Dragning 1 med Kenomaskinen HuxFlux: 2 Dragningsordning:48 15 19 25 52 42 1 54 24 22 23 34 30 12 38 5 32 62 4 16 3 Nummerordning: 1 4 5 12 15 16 19 22 23 24 25 30 32 34 38 42 48 52 54 62 4 Kung Keno: 1 5 Coupon(Anna,Set(1, 42, 43),false) korrekta nummer: Set(1, 42) 6 Coupon(Björn,Set(3, 4, 5),true) korrekta nummer: Set(4, 5) 7 Coupon(Sandra,Set(42, 1, 2, 3, 4),true) korrekta nummer: Set(42, 1, 4) Kung Keno! 8 *** Dragning 2 med Kenomaskinen HuxFlux: 9 Dragningsordning:14 38 51 68 48 17 70 52 9 42 12 2 45 33 8 39 46 10 60 53 10 Nummerordning: 2 8 9 10 12 14 17 33 38 39 42 45 46 48 51 52 53 60 68 70 11 Kung Keno: 60 12 Coupon(Anna,Set(1, 42, 43),false) korrekta nummer: Set(42) 13 Coupon(Björn,Set(3, 4, 5),true) korrekta nummer: Set() 14 Coupon(Sandra,Set(42, 1, 2, 3, 4),true) korrekta nummer: Set(42, 2)
6(9) Uppgift B2: Nyckel-värde-tabeller, 35p. (countries 3p, capitolof 4p, areaof 5p, askcapitol 6p, askbiggest 9p, play 8p) Du ska implementera ett textbaserat frågespel om Europas länder och huvudstäder. Du ska utgå från indata som finns i textfilen europa.txt enligt nedan, där varje rad innehåller följande semikolon-separerade värden: land, huvudstad och yta (i kvadratkilometer). Endast de första 5 raderna av totalt 48 visas: 1 Albanien;Tirana;28748 2 Andorra;Andorra la Vella;468 3 Belgien;Bryssel;30528 4 Bosnien-Hercegovina;Sarajevo;51129 5 Bulgarien;Sofia;110910 6... För läsning av indata och konstruktion av nyckel-värde-tabeller ska du färdigställa modulen Data nedan: 1 object Data { 2 def getlines(): Vector[String] = // ger sekvens av alla rader i filen europa.txt 3 scala.io.source.fromfile("europa.txt", "UTF-8").getLines.toVector 4 5 object Col { // Index för respektive kolumn i filen europa.txt 6 val (country, capitol, area) = (0, 1, 2) 7 } 8 9 val data: Vector[Vector[String]] = getlines.map(_.split(';').tovector) 10 val countries: Vector[String] =??? // sekvens med alla länder 11 val capitolof: Map[String, String] =??? // tabell med land -> huvudstad 12 val areaof: Map[String, Int] =??? // tabell med land -> yta 13 } Exekvering av frågespelet ska se ut som följer, då användaren skriver svaren efter prompten > enligt nedan. 1 2 --- Fråga nummer 1 av 4 --- 3 Är Ukraina större än Italien? (y/n) >y 4 Rätt! Grattis till 50 poäng :) 5 Ukraina är 603700 km^2. 6 Italien är 301230 km^2. 7 8 --- Fråga nummer 2 av 4 --- 9 Är Finland större än Luxemburg? (y/n) >n 10 Fel :( 11 Finland är 338145 km^2. 12 Luxemburg är 2586 km^2. 13 14 --- Fråga nummer 3 av 4 --- 15 Vad heter huvudstaden i Serbien? >belgrad 16 Rätt! Grattis till 100 poäng :) 17 Huvudstad i Serbien är Belgrad. 18 19 --- Fråga nummer 4 av 4 --- 20 Vad heter huvudstaden i Irland? >Belfast 21 Fel :( 22 Huvudstad i Irland är Dublin. 23 24 *** Du fick 150 poäng. Tack och hej! *** Körningen ovan är skapad genom av anrop av Quiz.main nedan med args = Array("4","43").
7(9) 1 object Quiz { 2 import Data._ 3 import scala.io.stdin.readline 4 import scala.util.random 5 6 type Points = Int 7 val ptscapitol: Points = 100 // poäng för rätt svar på huvudstadsfråga 8 val ptsbiggest: Points = 50 // poäng för rätt svar på fråga om störst yta 9 val probabilityofcapitolquestion = 0.4 // sannolikhet för huvudstadsfråga 10 11 def guesscapitol(country: String): String = 12 readline(s"vad heter huvudstaden i $country? >").tolowercase 13 14 def guessifbigger(country1: String, country2: String): Boolean = { 15 val a = readline(s"är $country1 större än $country2? (y/n)> ") 16 a.tolowercase.startswith("y") 17 } 18 19 def printif(iscorrect: Boolean)(points: Int, answer: String): Unit = 20 if (iscorrect) println(s"rätt! Grattis till $points poäng :)\n$answer") 21 else println(s"fel :( \n$answer") 22 23 def showareaof(country: String): String = s"$country är ${areaof(country)} km^2." 24 25 def showcapitolof(country: String): String = 26 s"huvudstad i $country är ${capitolof(country)}." 27 28 def randomcountry(): String = countries(random.nextint(countries.size)) 29 30 def askcapitol(): Points =??? // ställer huvudstadsfråga, se exempel 31 def askbiggest(): Points =??? // ställer fråga om störst yta, se exempel 32 33 def play(nbrofquestions: Int): Points =??? // ställer frågor, samlar poäng 34 35 def main(args: Array[String]): Unit = { 36 val nbrofquestions = args.headoption.map(_.toint).getorelse(1) 37 if (args.length > 1) scala.util.random.setseed(args(1).toint) 38 val pts = play(nbrofquestions) 39 println(s"\n*** Du fick $pts poäng. Tack och hej! ***") 40 } 41 } Dina implementationer av??? i Data och Quiz ska uppfylla följande krav: 1. countries ska innehålla varje sträng i kolumn Col.country i matrisen data. 2. capitolof och areaof ska mappa varje land till motsvarande värde i Col.capitol och Col.area. 3. askcapitol ska använda showcapitolof och askbiggest ska använda showareaof tillsammans med printif för att visa rätt svar för användaren enligt exemplet. 4. askbiggest ska inte kunna dra samma två lika städer i samma ytfråga. 5. askcapitol och askbiggest ska ge ptscapitol respektive ptsbiggest vid rätt svar, annars 0. 6. play ska ställa nbrofquestions frågor och returnera totala poängen. Varje fråga ska antingen vara en huvudstadsfråga med sannolikheten probabilityofcapitolquestion eller en ytfråga. 7. Huvudstadssvarens korrekthet bedöms utan att beakta skillnad mellan små och stora bokstäver. 8. Utskrifter ska ske exakt på samma form som i exemplet.
8(9) Uppgift B3. Linjärsökning, Java, 20p. ( konstruktor 4p, move 3p, pay 3p, findhighestdebt 10p ) Nystartade webbshoppen ubersquare säljer flyttbara kvadrater. I affärsmodellen ingår att ta betalt per förflyttning. Du ska hjälpa ubersquare att utveckla en enkel prototyp för att imponera på riskkapitalister. För detta ändamål har utvecklingen av Java-programmet på nästa sida påbörjats, vilket du ska färdigställa. Klassen UbersSquare ska kunna köras med nedan testprogram, ge följande utskrift och uppfylla kraven: 1 object TestUberSquare { 2 import scala.collection.javaconverters._ 3 4 def main(args: Array[String]): Unit = { 5 println("*** TESTING THE BUSINESS MODEL OF ubersquare ***") 6 UberSquare.setLogMode(true) 7 val us = Seq(new UberSquare(0,0,1), new UberSquare(0,1,1), new UberSquare(4,0,1)) 8 us foreach println 9 us(0).move(6, 8) 10 us(1).move(2, 0).scale(2).move(0, 4) 11 val sum = us(2).move(-4, 3).pay 12 println(s"debt PAID: $sum BY ${us(2)}") 13 println("fine TOKEN TO GREAT DEBT GOES TO:") 14 UberSquare.findHighestDebt.asScala foreach println 15 } 16 } 1 *** TESTING THE BUSINESS MODEL OF ubersquare *** 2 LOG MESSAGE: Log mode is on. 3 UberSquare[id: 1, pos: (0,0), side: 1, debt: 0.0] 4 UberSquare[id: 2, pos: (0,1), side: 1, debt: 0.0] 5 UberSquare[id: 3, pos: (4,0), side: 1, debt: 0.0] 6 LOG MESSAGE: move(6, 8) => UberSquare[id: 1, pos: (6,8), side: 1, debt: 11.0] 7 LOG MESSAGE: move(2, 0) => UberSquare[id: 2, pos: (2,1), side: 1, debt: 3.0] 8 LOG MESSAGE: scale(2) => UberSquare[id: 2, pos: (2,1), side: 2, debt: 3.0] 9 LOG MESSAGE: move(0, 4) => UberSquare[id: 2, pos: (2,5), side: 2, debt: 11.0] 10 LOG MESSAGE: move(-4, 3) => UberSquare[id: 3, pos: (0,3), side: 1, debt: 6.0] 11 LOG MESSAGE: pay(6.0) => UberSquare[id: 3, pos: (0,3), side: 1, debt: 0.0] 12 DEBT PAID: 6.0 BY UberSquare[id: 3, pos: (0,3), side: 1, debt: 0.0] 13 FINE TOKEN TO GREAT DEBT GOES TO: 14 UberSquare[id: 1, pos: (6,8), side: 1, debt: 11.0] 15 UberSquare[id: 2, pos: (2,5), side: 2, debt: 11.0] Krav 1. Varje förflyttning kostar r + A, där r är förflyttningsavståndet och A är kvadratens area. Krav 2. Det är gratis att skala om kvadraterna i storlek via den givna metoden scale 1. Krav 3. Utvecklarna på ubersquare vill skriva färre kodrader. Därför returnerar vissa metoder som ändrar kvadratens tillstånd en självreferens, vilket möjliggör kedjad punktnotation. Krav 4. ubersquare vill i samarbete med ett SMS-låneföretag tjäna pengar på ockerränta och önskar därför uppmuntra skuldsättning genom ett fashionabelt vandringspris till dem som har den för tillfället högsta obetald skulden och vill därför att du ska implementera metoden findhighestdebt. Tips: Gör först linjärsökning efter maxskuld och sedan linjörsökning efter alla kvadrater som har denna maxskuld. Krav 5. Varje kvadrat ska vid konstruktion få ett unikt id från 1 och uppåt. Krav 6. Med setlogmode ska loggning kunna aktiveras (se utskrift ovan). Anropa log på rätt ställen. Fler instruktioner finns i kommentarerna i koden på nästa sida. Saknade delar är märkta med /*??? */. 1 För att framstå som goda berättar man i sin marknadsföring att det är gratis att skala. Planen är sedan att sponsra coola youtubers som skalar upp kvadrater stort och sedan flyttar dem långt och ofta i sina populära vloggar...
1 import java.util.arraylist; 2 3 public class UberSquare { 4 private double debt = 0.0; 5 private int x; 6 private int y; 7 private int side; 8 private int id; 9 static private int nextid = 1; 10 static private boolean islogmode = false; 11 static private ArrayList<UberSquare> allsquares = new ArrayList<UberSquare>(); 12 13 public UberSquare(int x, int y, int side){ /*??? */ } // konstruktor 14 15 public int getx(){ 16 return x; 17 } 18 19 public int gety(){ 20 return y; 21 } 22 23 public int area(){ 24 return side * side; 25 } 26 27 public UberSquare scale(int factor){ 28 side *= factor; 29 log("scale(" + factor + ") => " + tostring()); 30 return this; 31 } 32 33 public UberSquare move(int dx, int dy){ /*??? */ } // flytta kvadrat, öka skuld 34 35 public double pay(){ /*??? */ } // returnera och nollställ skulden 36 37 public String tostring(){ 38 return "UberSquare[id: " + id + ", pos: (" + x + "," + y + "), " + 39 "side: " + side + ", debt: " + debt + "]"; 40 } 41 42 public static ArrayList<UberSquare> findhighestdebt(){ 43 /*??? */ // ge kvadrat(er) med högst skuld, tom lista om kvadrater saknas 44 } 45 46 static public void setlogmode(boolean on) { 47 islogmode = on; 48 log("log mode is on."); 49 } 50 51 static private void log(string msg) { 52 if (islogmode) { 53 System.out.println("LOG MESSAGE: " + msg); 54 } 55 } 56 } 9(9)