LULEÅ TEKNISKA UNIVERSITET Tentamen i Objektorienterad design Totala antalet uppgifter: 6 Lärare: Håkan Jonsson, Tomas Johansson, 491000, 491465 Resultatet anslås senast 2006-04-01 i A-huset. Tillåtna hjälpmedel: Inga. Kurskod SMD167 Datum 2006-03-18 Skrivtid 5 tim 1. Teori a) Vad menas helt kort med (a) (beräknings-) problem? (b) algoritm? b) Vilken eller vilka av de i grunden ganska effektiva sorteringsalgoritmerna Mergesort, Heapsort, Quicksort och Insertionsort (även kallad insättningssortering) tar i värsta fallet O(n log n) beräkningssteg för att ordna en sekvens med n element? Rättningen av denna deluppgift går till så att korrekta svar kvittas mot felaktiga. c) Algoritmer vars tidskomplexitet bara kan begränsas till O(2 n ) och O(n!) är ineffektiva. Bevisa med induktion över n att 2 n < n! för alla n 4. (2p) 2. Programspråksteori a) Antag att man i C skriver en funktion void swap(int a,int b) med syftet att två heltalsvariabler ska byta värden med varandra. (a) Varför är detta meningslöst? (0.5p) (b) Hur skulle man istället gjort för att metoden skulle ha avsedd effekt? (0.5p) b) Antag att p är en int-pekare i C. Då kommer p++ att öka värdet på p. Vad är det som avgör hur stor denna ökning blir? c) Förklara skillnaden mellan en klass och ett objekt? d) Vad är den största skillnaden mellan ett gränssnitt och en abstrakt klass? e) Vilken synlighet har en variabel om man inte använder något av nyckelorden public, private, eller protected? 3. Acmepod Det globala mjukvaruföretaget Acme Programming, Inc. har bestämt sig för att ge sig in i den kraftigt växande mp3-spelarmarknaden. Genom att skapa en simulerad mp3-spelare i Java ska nu en helt vanlig hemdator få precis samma funktionalitet som en riktig mp3-spelare. (Spelaren har ännu inte fått något pris, men man tror att den kommer att förhärliga Acme på alla vis.) Du har blivit utsedd att skriva klassen som representerar själva spelaren, AcmePod. mp3-spelaren ska kunna lagra låtar, och spela upp dem i slumpmässig såväl som sekventiell ordning. För att slumpa fram ett låtnummer finns det en tillgänglig metod public static int nextint(int n) i klassen AcmeRandom som varje gång den anropas ger tillbaka ett slumptal i intervallet 0... n 1. För att lagra låtarna internt ska du använda dig av listklassen som beskrivs i uppgift 4. Du får anta att den redan är färdigskriven. Du får även anta att klassen Song, (5p)
som representerar indivuduella låtar, är färdigskriven. Följande publika metoder och konstruktorer ska ingå i klassen AcmePod: AcmePod(int maxcapacity): Skapar en spelare som rymmer maxcapacity antal låtar. Från början är låtlistan tom och spelaren i stoppläge, dvs ingen låt spelas just nu. Uppspelningsordningen är sekventiell. void add(song s): Lägger till låten s sist i låtlistan. Om det inte finns plats att lägga till en till låt ska istället ett AcmePodException kastas. Du behöver inte själv skriva undantagsklassen. void remove(): Tar bort låten som just nu spelas från låtlistan, och spelaren går in i stoppläge. Om spelaren redan är i stoppläge ska ingenting hända. void setrandomorder(boolean b): Bestämmer uppspelningsordningen, dvs om sångerna ska spelas i slumpmässig ordning eller inte. Om b är true kommer låtordningen att slumpas, är b false kommer låtarna att spelas i den ordning de finns i listan. Song play(): Spelaren ändras från stoppläge till uppspelningsläge och returnerar den låt som ska börja spelas enligt vald uppspelningsordning (om låtarna ska spelas sekventiellt så ska den första låten i listan börja spelas, annars väljs låten slumpmässigt). Om uppspelning redan pågår, eller om det inte finns några låtar, ska ingenting hända. void stop(): Spelaren går till stoppläge. Om den redan är i stoppläge ska ingenting hända. Song next(): Hoppar till nästa låt, och returnerar den låt som börjar spelas. Om sångerna spelas i slumpmässig ordning ska nästa låt slumpas fram. Om sångerna spelas i sekventiell ordning ska nästa låt i listan väljas. Om uppspelningsordningen är sekventiell, och nuvarande låt är den sista i listan, ska den första låten i listan börja spelas. Om spelaren är i stoppläge ska ingenting hända. 4. Listigt Implementera den listklass som specificeras i Bilaga 1. Internt ska listan representeras med en array Object store[]. I konstruktorn tilldelas store en nyskapad array med en längd som ges av konstruktorns argument. Notera att en listas längd varierar dynamiskt mellan 0, då den är tom, och längden på store, då den är full. Du behöver inte skriva javadoc-kommentarer i ditt svar. 5. Funktionsanrop (5p) De aritmetiska uttryck som du implementerat under kursens gång behöver utökas med funktionsarop dvs ett uttryck ska nu kunna innehålla även deluttryck f(v 1,v 2,...,v n ) där f är namnet på en funktion, v 1,v 2,...,v n är funktionens argument och är en symbol som talar om att f är en funktion (och inte bara en variabel). Grammatiken för aritmetiska uttryck blir då (5p)
<expr> ::= <term> <expr > <expr > ::= "+" <term> <expr > "-" <term> <expr > empty <term> ::= <factor> <term > <term > ::= "" <factor> <term > "/" <factor> <term > empty <factor> ::= "-" <factor> "(" <expr> ")" num word "#" word "(" <argseq> ")" <argseq> ::= expr <argseq > empty <argseq >::= "," expr <argseq > empty där det nya lagts till på de 3 sista raderna. Ett exempel på en korrekt mening är 12+8-#area(10,max)/#length(#median(1,2+5,320),n+1) Uppgift: Gör om den bifogade funktionen factor ur klassen Expression i Bilaga 3 så att parsern klarar av att kontrollera uttryck innehållande funktionsanrop. Använd tokenizern i Bilaga 2. Ta med hela factor i ditt svar och inte bara de delar som du lagt till/ändrat. Du får gärna deklarera egna metoder, om du skulle vilja det, bara de görs oåtkomliga för andra klasser. 6. UML forever! Biltvättssimulatorn som producerades av Acme Programming Inc. blev inte den succe som företaget hade hoppats på, men man har stora förhoppningar på nästa simuleringsprojekt. Acme har fått i uppgift av The northernmost university in Scandinavia (except Tromsø) att simulera universitetets alla lokaler. Skriv följande klasser baserat på UML-diagrammet i figur 1 på nästa sida: (5p) Gränssnitten Cleanable och Bookable. Cleanable specifierar metoden void clean(), som representerar att en lokal städas. Bookable specifierar metoden int book(), som representerar en bokning av en lokal. book() returnerar priset för att boka den lokalen. Klassen Universitetslokal, som har ett rumsnummer i form av ett int, och en yta i form av en double (i enheten kvadratmeter). Dessa värden anges när klassen skapas. Klassen implementerar gränssnittet Cleanable. När en lokal städas ska texten"nu är rum XXX städat" skrivas ut, där XXX är rumsnumret. Klassen Klassrum, som ärver Universitetslokal. Ett klassrum har ett visst antal platser, vilket anges i konstrueraren. Klassrummet implementerar gränssnittet Bookable. Priset för att boka ett klassrum är lika med antal sittplatser 40 kr. Klassen Föreläsningssal, som ärver Universitetslokal. En föreläsningssal har, precis som ett klassrum, ett visst antal platser, vilket anges i konstrueraren. Klassen implementerar gränssnittet Bookable. Priset för att boka en föreläsningssal är lika med antal sittplatser 30 kr, om inte ytan överstiger 50 kvadratmeter. Om den gör det är priset alltid 2500 kr, oavsett antal platser. Klassen Kontor, som ärver Universitetslokal. Klassen fungerar precis som sin superklass, med undantaget att vid städning av ett kontor ska, förutom den redan nämnda utskriften, texten "Ska jag tömma papperskorgen?" skrivas ut.
<<interface>> Bookable <<interface>> Cleanable +book(): int Universitetslokal +Universitetslokal(rumsnr:int,yta:double) Klassrum +Klassrum(rumsnr:int,yta:double,antalplatser:int) +book(): int Föreläsningssal +Föreläsningssal(rumsnr:int,yta:double, antalplatser:int) +book(): int Kontor +Kontor(rumsnr:int,yta:double) Figure 1: UML diagram for Problem 6.
1 Specifikation av listklass BILAGOR / A class for lists of bounded size containing elements of typ Object. A list has a pointer that at each moment of time points to a specific element or is invalid (does not point at all). @author N. Ron Adelphia / public class AcmeList { // Your declarations here, if needed. / Creates a list with a certain maximum length. Initially, the list is empty and, hence, its length is zero. @param maxsize The maximum number of elements the list should be able to contain. / public AcmeList(int maxsize) { / Inserts an object into the list so that the inserted object ends up as the new last element of the list. The pointer is not effected by this operation. @param x An element that should be inserted. / public void add(object x) { / Removes the element pointer at by the pointer and shrinks the size of the list by 1. Resets the pointer to the first element unless the removed element was the last one in which case the pointer becomes invalid. @throws java.util.nosuchelementexception if called when the list is empty. @see atpointer() / public void remove() { / The current number of elements in the list. If this number is zero, the list is empty. @return Returns the number of elements currently in the list. / public int size() {
/ Returns the maximum number of elements the list can contain. @return Returns the maximum number of elements the list can contain. / public int capacity() { / A reference to the element in the list pointed at by the list s pointer. @return A reference to the element pointed at by the pointer. @throws java.util.nosuchelementexception if called when the list is empty or the pointer is invalid. / public Object elementpointedat() { / Moves the pointer from the current element to the next element in the list. @throws java.util.nosuchelementexception if called when the list is empty or the current element is the last one. / public void advancepointer() { / Returns true if and only if the pointer points to the last element of the list. @return This method returns true if and only if the list contains at least one element and the pointer points to the last element of the list. Otherwise, it returns false. @throws java.util.nosuchelementexception if the list is empty or the pointer is invalid. / public boolean atend() { / Points the pointer at the first element of the list. @throws java.util.nosuchelementexception if called when the list is empty. / public void resetpointer() {
2 Tokenizer package tokenizer; import java.io.; public class Tokenizer { private StreamTokenizer tokens; protected int type; public static final int ID = 1, NUMBER = 2, RIGHTPAREN = 3, LEFTPAREN = 4, ENDOFFILE = 5, PLUS = 6, MINUS = 7, ASTR = 8, SLASH = 9, AMPERSAND = 10, EXCLAMATION = 11, QUESTION = 12, VERTICAL = 13, HASH = 14, EQUAL = 15, LESS = 16, MORE = 17, PERCENT = 18, ILLEGAL = -1; public Tokenizer(Reader input) { tokens = new StreamTokenizer(input); tokens.ordinarychar( - ); tokens.ordinarychar( / ); tokens.ordinarychar( % ); tokens.ordinarychar( # ); public void get() { try { type = tokens.nexttoken(); catch (IOException e) { throw new RuntimeException(e.toString()); public int kind() { switch (type) { case StreamTokenizer.TT_WORD: return ID; case StreamTokenizer.TT_NUMBER: return NUMBER; case StreamTokenizer.TT_EOF: return ENDOFFILE; default: switch ((char) type) { case ( : return LEFTPAREN; case ) : return RIGHTPAREN; case + : return PLUS; case - : return MINUS; case : return ASTR; case / : return SLASH; case & : return AMPERSAND; case! : return EXCLAMATION; case? : return QUESTION; case : return VERTICAL; case # : return HASH; case = : return EQUAL; case < : return LESS;
case > : return MORE; case % : return PERCENT; default: return ILLEGAL; public String ident() { return tokens.sval; public double numbervalue() { return (double) tokens.nval; public String tostring() { return tokens.tostring(); protected void ordinarychar(char c) { tokens.ordinarychar(c);
3 Expression import java.io.; import tokenizer.tokenizer; public abstract class Expression { private static Tokenizer token; public static boolean accept(reader input) { token = new Tokenizer(input); token.get(); return expr(); private static boolean expr() { // code omitted private static boolean term() { // code omitted private static boolean factor() { boolean expr; if (token.kind() == Tokenizer.MINUS) { token.get(); expr = factor(); else if (token.kind() == Tokenizer.LEFTPAREN) { token.get(); expr = expr(); if (token.kind()!= Tokenizer.RIGHTPAREN) { return false; token.get(); else if (token.kind() == Tokenizer.NUMBER) { expr = num(); else { expr = word(); return expr; private static boolean num() { // code omitted private static boolean word() { // code omitted