Tenta i TDP007 tisdag 7 juni 2011 kl 08-12 ------------------------------------------ Uppgift 1: Grundläggande Ruby (5p) ---------------------------------- a) Vad innebär det att ett XML-dokument är "well-formed" respektive "valid"? (1p) b) Skriv en funktion som med hjälp av ett reguljärt uttryck kan dela upp en webbadress i server, katalog och fil enligt följande exempel. (2p) >> split-url("http://www.ida.liu.se/~TDP007/material/lectures.sv.shtml") => ["www.ida.liu.se", "/~TDP007/material/", "lectures.sv.shtml"] c) Skapa en klass Switch som representerar en knapp som kan slås på och av. När man skapar ett objekt får man ange ett kodblock som ska köras varje gång knappen slås på. Returvärdet är någon av symbolerna :on eller :off. Se exempel nedan. (2p) >> s=Switch.new { puts "Whoa! The light is on!" } => ... >> s.push Whoa! The light is on! => :on >> s.push => :off Uppgift 2: Textfiler (5p) ------------------------- I filen bnp.txt finns en tabell som visar Sveriges BNP (bruttonationalprodukt) för åren 1958-2008. BNP är värdet av alla produkter och tjänster som producerats i landet under ett givet år. I tabellen visas BNP i miljoner kronor. Det tredje talet per rad är folkmängden i tusental personer. Skriv ett Ruby-program som läser in tabellen och lagrar den i en lämplig datastruktur. Inläsningen ska vara såpass generell att den fungerar även om mindre justeringar i formatet görs. Beräkna därefter BNP per capita, d.v.s. per person, för varje år i tabellen. Definiera sedan två olika funktioner som kan hämta ut information ur tabellen enligt följande: Den första funktionen ska skriva ut den procentuella ökningen av BNP per capita från ett år till ett annat. Det första året i originaltabellen kan alltså inte vara med i det här resultatet. Utskriften ska vara korrekt formatterad enligt följande: 1959 6.0% 1960 8.3% 1961 8.3% 1962 7.8% 1963 7.6% 1964 10.7% Ovanstående utskrift ska tolkas så att BNP per capita ökade med 6.0% från 1958 till 1959. Den andra funktionen ska enbart skriva ut de årtal som BNP per capita var lägre än året innan. Uppgift 3: XML (6p) ------------------- I filen library.xml finns uppgifter om ett antal böcker. Utdata kommer från den svenska nationella biblioteksdatabasen Libris som man hittar på webben på adressen http://libris.kb.se/ och det är i det standardiserade formatet MARC-XML. MARC är ett format som funnits i biblioteksvärlden under lång tid och MARC-XML är helt enkelt denna information överförd till XML. Stora delar av det här formatet är obegripligt för oss som inte är i biblioteksbranschen, men genom att titta lite på filen kan man rätt snart komma fram till huvuddragen. Filen består av en 'collection' som i sin tur är uppbyggd av ett antal 'record'. Varje 'record' motsvarar en bok och innehåller bl.a. ett antal 'datafield' som i sin tur kan bestå av 'subfield'. Varje 'datafield' har en standardiserad 'tag' och varje 'subfield' har en 'code'. Det är dessa siffror och bokstäver som talar om vilken information som faktiskt finns i filen. Följande tabell summerar vad vi behöver veta för att lösa uppgiften. datafield tag subfield code innehåll ------------------------------------------------------- 100 a Författarens namn 245 a Bokens titel 260 c Utgivningsår Uppgiften är att skriva en funktion som kan läsa igenom filen och skriva ut en enkel lista över de fjorton böcker som finns i filen. Det ska alltså se ut ungefär så här: >> print_library("library.xml") Coupland, Douglas (2004) Eleanor Rigby Coupland, Douglas (1992) Generation X ... I uppgiften ingår dessutom att "städa upp" den textinformation som finns i XML-filen, så att inga onödiga tecken (som inte kan antas finnas med i författare, titel och år) skrivs ut. Uppgiften kan lösas antingen med REXML-paketet (trädparsning) eller med strömparsning. Lite hjälp för båda dessa metoder finns i filerna rexml.txt samt sax_example.rb. Samtliga böcker i listan är för övrigt mycket läsvärda. Uppgift 4: Domänspecifika språk (5p) ------------------------------------ I filen my_world.rb finns en karta över en spelvärld uttryckt i ett enkelt domänspecifikt språk. Kartan beskrivs genom att man går runt i världen och observerar olika objekt. Med hjälp av kommandon som motsvarar de fyra väderstrecken flyttas man runt. Om kommandot får en tal som parameter förflyttas man det antal steg som anges, annars endast ett steg. Alla övriga kommandon är namn på saker som finns på den aktuella positionen. Dessa namn kan vara vilket ord som helst. Filen börjar så här: north east house Det innebär alltså att i en punkt nordost om startpunkten finns ett hus. Din uppgift är att skriva ett program som kan läsa in en karta som är beskriven på det här sättet. Kartan ska läsas in till ett format så att den kan skrivas ut med hjälp av funktionen i filen print_map.rb. Tanken är att det ska se ut så här, om allt fungerar: >> print_map(MapReader.load("my_world.rb")) -------- -------- -------- house -------- sword -------- -------- -------- -------- -------- dragon -------- -------- treasure => nil I uppgiften ingår alltså att först förstå hur funktionen print_map fungerar. Jämför det som står i filen med utskriften ovan. Därefter ska du skapa klassen MapReader som kan läsa in en karta i det givna formatet. Du ska använda dig av någon av de tekniker för domänspecifika språk i Ruby som vi har gått igenom i kursen. Uppgift 5: Parsning (6p) ------------------------ En grupp av arkeologer har just upptäckt lämningar efter en utdöd civilisation som man kallar för Cromare. De verkade ha ett annorlunda talsystem, baserat på fyra symboler: Symbol Värde -------------- i 1 x 10 c 100 m 1000 På gamla lertavlor har man funnit matematiska uträkningar med detta konstiga talsystem. Uppenbarligen kunde man blanda dessa "siffror" hur man ville. Ordningen spelade ingen roll, så som det gör i vårt talsystem. Vi tycker ju att 123 inte är samma tal som 321, men för Cromarna var ixi, xii och iix samma tal (tolv enligt vårt sätt att räkna). Istället för att använda våra symboler för de fyra räknesätten har man använt symbolerna P (plus), M (minus), G (multiplikation) och D (division). Deras uträkning "micPcix" betyder alltså 1101 plus 111, vilket borde bli 1212 om man räknar som vi gör. De verkade också kunna kombinera dessa operatorer så att "xiGiiPi" skulle motsvara (11*2)+1 = 23. Din uppgift är att hjälpa arkeologerna genom att skriva en parser som kan tolka matematiska uttryck som är skrivna som Cromarna gjorde. Utgå från parsern som finns i rdparse.rb. Följande är ett exempel på hur det skulle kunna se ut: >> CromanParser.new.start [Croman Calculator] xPiPiPi => 13 [Croman Calculator] iiGxPiii => 23 [Croman Calculator] iiGcDxxxxx => 4 Uppgift 6: Constraints (5p) --------------------------- 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. 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.