Dugga 2 i TDP007 torsdag 7 mars 2013 kl 15.15-17.15 --------------------------------------------------- Duggan startar kl. 15:15 och pågår till 17:15. Ni kommer att få köra på begränsade datortentakonton. Instruktioner för att logga in på dessa lämnas i salen. Alla filer som ni behöver för att lösa uppgifterna finns i hemkatalogen när ni har loggat in. Där finns också en PDF-version av en tidig utgåva av kursboken som ni gärna får använda er av. Öppna den med kommandot acroread. Svar på frågor skrivs i vanliga textfiler och programkod skrivs i rb-filer. En fil per huvuduppgift ska produceras, totalt alltså fyra filer. Ingen särskild inlämning behöver göras. När man är färdig loggar man ut och lämnar salen. Duggan består av fyra uppgifter (några indelade i deluppgifter) som totalt kan ge 25 poäng. Dessa poäng summeras ihop med poängen från den förra duggan. Uppgift 1: Teorifrågor (7p) --------------------------- a) Beskriv kortfattat en faktor som kan bidra till att ett datorspråk blir populärt och motivera varför. (1p) b) Hur fungerar parsern som finns i filen rdparse.rb? Förklara i grova drag hur den arbetar, t.ex. med utgångspunkt i exemplet DiceRoller. (2p) c) Vad är ett domänspecifikt språk? Beskriv kortfattat en typisk situation där man kan vilja använda ett domänspecifikt språk och motivera varför. (2p) d) Vad är en continuation och vad innehåller den? Ge ett kort kodexempel som illustrerar hur en continuation kan användas. (2p) Uppgift 2: Constraint-nätverk (6p) ---------------------------------- I filen constraint_networks.rb finns samma restriktionsnät som inför det fjärde seminariet. Skapa en ny typ av constraint Squarer som har två in- och utgångar som vi kan kalla n och sq_n. Om man ger n ett värde ska sq_n sättas till kvadraten av n. Om man ger sq_n ett värde ska n sättas till (den positiva) kvadratroten av n. Squarer ska inte implementeras genom att bygga ihop andra constraints, utan ska skrivas från grunden som en ny typ av constraint. Använd därefter denna nya constraint för att bygga upp ett nätverk som kan beräkna arean av en cirkel. Den är som bekant pi gånger radien i kvadrat. Denna del av uppgiften kan lösas i teorin, men inte testas, utan att Squarer finns. Ett bra värde på pi kommer man åt via Math::PI och kvadratroten kan beräknas med Math::sqrt. Du behöver inte fixa buggen i constraint- nätverket som skulle fixas till fjärde seminariet. Uppgift 3: DSL (6p) ------------------- Affären Foo Bar vill ha ett enkelt domänspecifikt språk för att på ett dynamiskt sätt specificera vad som ska stå på kvitton som kunderna får. I filen receipt.rb finns ett exempel på hur språket ser ut. Kommandona header respektive footer specificerar en sträng som ska skrivas ut först respektive sist på kvittot. Vissa av dessa strängar skrivs bara ut om man köper vissa sakar. Kommandot discount talar om hur stor rabatt man får. I exemplet finns tre rabattsatser, som blir större ju mer man köper. Här är ett körexempel som visar hur det är tänkt att fungera om man testkör: >> s = Sale.new >> s.add("Foo", 4, 45) >> s.add("Bar", 3, 129) >> s.finalize("receipt.rb") Thank you for shopping at Foo Bar -------------------- Foo 4 x 45 = 180 Bar 3 x 129 = 387 Subtotal: 567 Discount: 85.05 Total: 481.95 -------------------- Good luck with your new Foo! Din uppgift är att skapa en klass Sale som lagrar information om ett köp. Den ska ha en metod add som lägger till saker till köpet (namn, antal och styckpris) och en metod finalize som används för att skriva ut kvittot. Denna metod ska köra DSL- koden i den angivna filen för att anpassa kvittot. Det är alltså metoden finalize som summerar ihop och skriver ut de saker som köps, medan koden i receipt.rb sköter anpassningarna. Uppgift 4: Parsning (6p) ------------------------ I den här uppgiften ska vi konstruera ett litet språk för att rita mycket enkel ASCII-grafik. Varje "program" i språket måste börja med att man anger hur stor yta man ska rita på, t.ex.: init 10 10 Detta gör att vi skapar ett rutnät med 10 x 10 tecken (första talet är bredden och andra talet är höjden). Vi tänker oss att vi har en "penna" som kan rita ett tecken i varje ruta. När vi precis har skapat rutnätet svävar pennan ovanför position (0, 0) vilket motsvarar övre vänstra hörnet av rutnätet. Vi kan "sänka" pennan med kommandot: down W Detta ritar ett "W" på den position som är under pennan. Om vi därefter gör en förflyttning kommer pennan att fortsätta rita i de rutor vi kommer till. Vi kan förflytta oss i fyra olika riktningar, motsvarande väderstrecken. Exempel: e 5 s 2 w 2 n 1 Ovanstående kommandon flyttar pennan österut (höger) fem steg, därefter söderut (nedåt) två steg, o.s.v. Om pennan är nedsänkt ritas det tecken vi har angivit, annars händer inget annat än att vi förflyttar oss. Vi kan lyfta pennan med kommandot: up Uppgiften går ut på att implementera en parser för detta lilla språk med utgångspunkt i parsern i filen rdparse.rb. Tanken är att parsern bygger upp en datastruktur som motsvarar ritningen, och när parsningen är klar ska denna ritas ut. Exempel: >> dp = DrawParser.new >> dp.draw("init 10 10 down # e3s3w3n3 up s4 down * e3s3w3n3") ------------ |#### | |# # | |# # | |#### | |**** | |* * | |* * | |**** | | | | | ------------ Ett fullständigt program börjar alltid med init-kommandot och hela programmet är på endast en rad. Som framgår av exemplet behöver man inte använda mellanslag mellan kommandon och argument om det inte är nödvändigt för tolkningen. De yttre strecken i exemplet hör inte till själva "ritningen" utan är bara en ram för att vi lättare ska se vad vi ritat. Definiera klassen DrawParser som, med hjälp av rdparse.rb, kan tolka vårt enkla rit-språk och producera ASCII-grafik enligt ovanstående exempel.