Tentamen Programmeringsteknik II 2017-05-29 Skrivtid: 0800 1300 Tänk på följande Lägg uppgifterna i ordning. Skriv uppgiftsnummer och din anmälningskod överst i högra hörnet på alla papper. Fyll i försättssidan fullständigt. Såvida inget annat anges får man bygga på lösningar till föregående uppgifter även om dessa inte har lösts. Det är tillåtet att införa hjälpmetoder och hjälpklasser. Uttrycket skriv en metod som skall alltså inte tolkas så att lösningen inte får struktureras med hjälp av flera metoder. Alla uppgifter gäller programmeringsspråket Java och programkod skall skrivas i Java. Koden skall vara läslig dvs den skall vara vettigt strukturerad och indenterad. Namn på variabler, metoder, klasser etc skall vara beskrivande men kan ändå hållas ganska korta. Observera att betyget påverkas negativt bland annat av: icke-privata eller onödiga instansvariabler, dålig läslighet, upprepning av identisk kod, underlåtenhet att utnyttja given eller egen tidigare skriven metod, underlåtenhet att utnyttja arv och lösningar som har väsentligt sämre komplexitet än nödvändigt t ex Θ(n 2 ) i stället för Θ(n log n). Observera Skrivningen består av två delar. Lösningarna till uppgifterna på del A ska skrivas direkt på skrivningsformuläret och lämnas in. Rutorna är tilltagna i storlek så att de ska rymma svaren. Om du ändå inte får plats får du fortsätta på vanligt skrivblad. Markera gärna detta både på försättsbladet och i rutan. Skriv inte på baksidan. Svaren på del B skrivs på vanligt papper med nytt papper för varje uppgift. Var god vänd!
Bedömning och betygsättning För att bli godkänd (betyg 3) krävs att A-delen i huvudsak är rätt löst. Det betyder inte att varje uppgift behöver vara exakt rätt, men att du ska visa att du i huvudsak uppfyller kursens mål, som säger att studenten efter godkänd kurs ska kunna: använda programspråket Java, både skriva egen kod och förklara vad en given kod utför; redogöra för begreppen arv och polymorfi i ett objektorienterat språk och använda dessa begrepp i egen programutveckling; implementera rekursiva lösningar till olika problem; beskriva principen för analys av algoritmers effektivitet och utföra sådan analys av enklare algoritmer; beskriva, implementera och använda de grundläggande datastrukturerna array, länkad lista, hashtabell och binärt träd samt abstrakta datatyper som stackar, köer och avbildningar; beskriva hur felhantering med hjälp av undantag fungerar och kunna använda detta i egna program. För betyget 4 krävs dessutom att minst hälften, och för betyg 5 alla, uppgifterna på B-delen är i stort sett rätt lösta. Vid bedömning för dessa betyg tas även hänsyn till kvaliteten på lösningarna på A-delen. Observera att B-delen inte rättas om inte A-delen är godkänd. Lycka till! Inledning I bilagorna finns ett antal mer eller mindre ofullständiga klasser. Klassen BST innehåller en variant av binära sökträd med tal som nycklar. Klassen ExamException är en liten undantagsklass. Klassen List innehåller några grundläggande komponenter för att skapa och hantera enkellänkade listor av Student-objekt. Varje student kan förekomma flera gånger. Klassen Misc innehåller dels stommen till ett händelsedrivet program, dels en metod för att matcha uttryck med jokertecken mot text. Klassen Parser är ett utdrag ur en klass för att tolka aritmetiska uttryck. Klassen Student representerar en student med för- och efternamn. Den är oföränderlig, det går inte att byta namn efter att en student har skapats. Notera att även de metoder som inte är implementerade oftast har giltig Javadoc-dokumentation, som kan ge värdefull information. Utöver detta finns delar av den officiella Java-dokumentationen för ArrayList, Comparable, Map och String bifogad.
Anmälningskod: Del A (obligatorisk för alla) A1. Ringa in rätt svarsalternativ (om flera alterantiv kan vara rätt anges detta)! a) Vilket/vilka av följande behövs för att undvika oändlig rekursion? 1) Ett basfall 2) Ett induktionssteg 3) En retursats b) För att rita geometriska figurer i Java anropar man metoder i en instans av vilken klass? c) Vilken typ av objekt reagerar på händelser i ett händelsestyrt Swingprogram? d) Vilket eller vilka av följande påståenden gäller om en klass X ärver från en annan klass Y? 1) JPanel 2) JFrame 3) Graphics 4) DrawingTools 1) GUI 2) EventHandler 3) ActionListener 4) JPanel 1) Alla Y:s publika metoder kan anropas för ett objekt av typen X 2) Alla X:s publika metoder kan anropas för ett objekt av typen Y 3) Y måste implementera alla metoder i X 4) X måste implementera alla metoder i Y 5) En instans av Y kan deklareras som X 6) En instans av X kan deklareras som Y e) Vilken tidskomplexitet har en effektiv sorteringsalgoritm? n anger antalet element som ska sorteras. 1) Θ(1) 2) Θ(log n) 3) Θ(n) 4) Θ(n log n) 5) Θ(n 2 ) 6) Θ(2 n ) f) Hur beror antalet operationer för att hämta ett värde ur en sorterad lista på listans längd n? 1) Θ(1) 2) Θ(log n) 3) Θ(n) 4) Θ(n log n) 5) Θ(n 2 ) 6) Θ(2 n ) g) Hur beror antalet operationer för att hämta ett värde ur ett balanserat binärt sökträd på antalet noder n? 1) Θ(1) 2) Θ(log n) 3) Θ(n) 4) Θ(n log n) 5) Θ(n 2 ) 6) Θ(2 n )
Anmälningskod: h) Hur beror antalet operationer för att hämta ett värde ur en hashtabell på antalet element i tabellen, n? Antag en bra implemenation av hashtabell och hashfunktion. i) Vilket nyckelord användar man för att i en metod m1 rapportera ett undantag till den metod som anropar m1? j) Vad innebär det att en metod överskuggar en annan metod? 1) Θ(1) 2) Θ(log n) 3) Θ(n) 4) Θ(n log n) 5) Θ(n 2 ) 6) Θ(2 n ) 1) super 2) throw 3) return 4) new 1) Metoderna har samma namn, men olika antal parametrar 2) Metoderna har samma namn, men deras parametrar har olika namn 3) Metoden i fråga har samma namn och parametrar som metoden den överskuggar, men ligger längre upp i klasshierarkin 4) Metoden i fråga har samma namn och parametrar som metoden den överskuggar, men ligger längre ned i klasshierarkin k) Vad är ett undantag? 1) Ett objekt 2) En abstrakt klass 3) Ett interface 4) En metod
Anmälningskod: A2. List.getStudent letar efter en student med ett visst namn. Den kastar undantaget ExamException om studenten inte finns. Implementera metoden ensurestudent. Den ska använda getstudent och lägga till och returnera ett nytt studentobjekt med detta namn om det inte redan finns. public Student ensurestudent(string firstname, String surname) { try { return getstudent(firstname, surname); catch (ExamException) { Student s = new Student(firstname, surname); add(s); return s; A3. Det är förhållandevis normalt att ett visst element inte finns i en lista. Undantag ska normalt bara användas för undantagsfall (fel). Hur borde getstudent fungera i stället, utifrån hur exempelvis Map fungerar? Returnera null för att indikera att elementet saknas. A4. Javadoc-kommentaren för List.equals saknar giltiga kommentarrader för parametern och returvärdet. Skriv ett förslag på hur de kunde se ut med giltig syntax. @param other The object to compare against. @return Whether this and other are equal. A5. När man skriver sin egen klass, som Student, kan vissa standardmetoder behöva implementeras. Implementera tostring så att resultatet får formatet Förnamn Efternamn. public String tostring() { return firstname + " " + surname; A6. Implementera metoden Student.equals. public boolean equals(object other) { if (other == this) { return true; if (other == null other.getclass!= getclass()) { return false; Student s = (Student) other; return s.firstname.equals(firstname) && s.surname.equals(surname);
Anmälningskod: A7. Om ett objekt ska läggas i en TreeSet eller som nyckel i en TreeMap måste det finnas en sorteringsregel för hur objekten ska ordnas. En klass kan styra sin ordning genom att implementera interfacet Comparable. String är ett exempel på en standardklass som implementerar Comparable. Implementera metoden Student.compareTo. Studenter ska sorteras i stigande alfabetisk ordning, i första hand på efternamn och i andra hand på förnamn. public int compareto(student s) { int comp = surname.compareto(s.surname); if (comp == 0) { comp = firstname.compareto(s.firstname); return comp; A8. Titta på implementationen av List.add. Vilken tidskomplexitet har denna operation i relation till listans totala storlek n? Ge en kort motivering och svara i termer av Θ. I och med att det nya elementet läggs till först i listan görs alltid samma konstanta antal operationer, Θ(1). A9. Titta på implementationen av List.remove. Vilken tidskomplexitet har denna operation i relation till listans totala storlek n, i fallet då studenten inte visar sig finnas i listan? Ge en kort motivering och svara i termer av Θ. För att avgöra att elementet inte finns i listan måste remove ändå besöka varje element. Varje besök tar kosntant tid. Sammantaget alltså Θ(n). A10. Implementera BST.size, som returnerar det totala antalet noder i trädet. public int size() { return size(root); private static int size(node n) { if (n == null) return 0; return 1 + size(n.left) + size(n.right); A11. Implementera hashcode för Student. Se till att de hypotetiska personerna Sten Björn och Björn Sten inte får samma hashvärde. Du får utnyttja befintliga hashcode-metoder i andra klasser. public int hashcode() { return firstname.hashcode() + 3 * surname.hashcode();
Anmälningskod: A12. Vi har följande klasser: public class Person { public void work() { System.out.println("Struggling..."); public class Professor extends Person { public void work() { System.out.println("Supervising students..."); public class UniStudent extends Person { public void work() { System.out.println("Studying for exam!"); public class PhDStudent extends UniStudent { public void work() { System.out.println("Reading a paper..."); public class Undergrad extends UniStudent { Ange i rutan på nästa blad vilken/vilka av följande kodrader som inte kommer att kunna kompileras och varför. Ange sedan vad den kvarvarande koden skriver ut. 01 Person p1 = new Professor(); 02 p1.work(); 03 Professor p2 = new Person(); 04 p2.work(); 05 PhDStudent p3 = new UniStudent(); 06 p3.work(); 07 UniStudent p4 = new PhDStudent(); 08 p4.work(); 09 Person p5 = new PhDStudent(); 10 p5.work(); 11 PhDStudent p6 = new Person(); 12 p6.work(); 13 Person p7 = new Undergrad(); 14 p7.work(); 15 Undergrad p8 = new UniStudent(); 16 p8.work(); 17 Object p9 = new Person(); 18 p9.work();
Anmälningskod: 03, 05, 11, 15 försöker tilldela variabler deklarerade av underklasstyp med basklasser. 04, 06, 12, 16 ger följdfel beroende på den misslyckade tilldelningen. Rad 18 försöker anropa metoden work på en Object-referens. Object har ingen workmetod. (02:) Supervising students... (08:) Reading a paper... (10:) Reading a paper... (14:) Studying for exam!
Del B (för betyg 4 och 5) Svaren till dessa uppgifter skrivs på vanligt skrivningspapper. Se till att skriva varje uppgift på ett eget blad. B1. Alla Object har en standardversion av hashcode färdig som beräknar hashvärde utifrån referenslikhet ( == ). Varför måste du överskugga hashcode om du har valt att överskugga equals och vill använda klassen som nyckel i HashMap? Om två objekt är identiska enligt equals förväntas de representera samma nyckel, men om man inte överskuggar hashcode kan de två objekten få olika hashvärden. De kommer då att placeras i olika hinkar och aldrig jämföras, så det blir i praktiken möjligt att ha samma nyckel flera gånger i samma HashMap och att missa att en nyckel finns vid sökningar. B2. Skriv den nödvändiga koden för metoden List.getSurnameList, som ska returnera en List med alla studenter i listan som har ett visst efternamn. public List getsurnamelist(string surname) { \\ List newlist = new List(); newlist.first = getsurnamelist(root, surname); return newlist; private static Node getsurnamelist(node n, String surname) { if (n == null) { return null; Node next = getsurnamelist(n.next, surname); if (surname.equals(n.student.getsurname())) { next = new Node(next, n.student); return next; B3. I program som bearbetar text är det vanligt att kunna hantera så kallade jokertecken, eller wildcards. Skriv klart metoden Misc.matchWildcards. * (asterisk) ska tolkas som noll eller fler tecken,? (frågetecken) ska tolkas som exakt ett tecken. Se kommentarer i bilagan för några exempel på förväntade resultat. public static boolean matchwildcards(string text, String pattern) { return matchwildcards(text, 0, pattern, 0); private static boolean matchwildcards(string text, int textpos, String pattern, int patternpos) { if (patternpos == pattern.length()) { // At the end of both? return textpos == text.length(); boolean anytext = textpos < text.length(); char patternchar = pattern.charat(patternpos); switch (patternchar) { case? : return anytext && matchwildcards(text, textpos + 1, pattern, patternpos + 1);
case * : // Move one step in one or the other. // * needs to test both options. return (anytext && matchwildcards(text, textpos + 1, pattern, patternpos)) matchwildcards(text, textpos, pattern, patternpos + 1); default: // Normal character return anytext && text.charat(textpos) == patternchar && matchwildcards(text, textpos + 1, pattern, patternpos + 1); B4. Klassen BST har stöd för att skapa en ArrayList med talen in-order. a) Skriv koden för den konstruktor som tar en ArrayList för att skapa ett träd från en sådan sorterad ArrayList. Skriv ytterligare hjälpmetoder vid behov. Det resulterande trädet får maximalt ha höjden log 2 n, där n är antalet element. Tänk efter hur du kan göra processen så tidseffektiv som möjligt. public BST(ArrayList<Integer> list) { root = fromarraylist(list, 0, list.size()); private static Node fromarraylist(arraylist<integer> list, int start, int end) { if (end <= start) { return null; int mid = start + (end - start) / 2; return new Node(list.get(mid), fromarraylist(list, start, mid), fromarraylist(list, mid + 1, end)); b) Motivera vilken tidskomplexitet konstruktorn har. Hur förhåller den sig till tidskomplexiteten för att lägga till samma element i trädet ett i taget i slumpmässig ordning? Hjälpmetoden gör konstant arbete och anropas en gång för varje element, plus en gång extra för varje tomt löv. Det senare skalar mindre än linjärt med totala antalet element. Alltså är det totala arbetet för konstruktorn Θ(n). Vid slumpmässig inläggningsordning skulle add användas och trädet skulle i genomsnitt bli balanserat, vilket skulle innebära n operationer med komplexitet Θ(log n), d.v.s. Θ(n log n). B5. Tänk dig att du har koden i Misc.main i bilagan. Syftet är att ha ett händelseorienterat system som läser nya kommandon och hanterar dem. Vissa kommandon tar lång tid. För att nya kommandon ska kunna ges under tiden sker lite bearbetning i varje anrop till process. När det inte finns fler kommandon från parsern och inga kommandon har mer arbete att göra (men endast då) ska programmet avslutas. Om bearbetningen av något kommando misslyckas ska efterföljande kommandon ändå köras. Nu har vi fått veta att Command.process kan kasta ett CommandException. a) Vad händer när det kastas, som koden ser ut nu? Hela programmet avslutas med en standardfelutskrift från Java som beskriver undantaget. b) Ange vilka ändringar du skulle göra för att få programmet att fungera i enlighet med beskriv-
ningen ovan, även när något kommando kastar undantag. for (Command c : cs) { try { c.process(); if (c.isdone()) { donecs.add(c); catch (CommandException e) { System.err.println("Error processing command: " + e); donecs.add(c); B6. Så här ser klassdesignen för den symboliska kalkylatorn i den sista obligatoriska uppgiften ut: Sexpr abstract String getname() abstract Sexpr eval(map) abstract int priority() Sexpr diff(sexpr) double getvalue() boolean isconstant() boolean isconstant(double) Atom abstract Unary abstract Binary abstract String tostring() Sexpr operand Sexpr left Sexpr right String tostring() Variable String ident Sexpr eval(map) String getname() Sexpr diff(sexpr) Operator abstract String tostring() int priority()? Function abstract String tostring() Addition Subtraction Multiplication Division Constant Evaluation Sin Assignment double value Quotation Cos Differentiation Sexpr eval(map) String getname() Sexpr diff(sexpr) boolean isconstant() boolean isconstant(double) double getvalue() Negation Sexpr eval(map) String getname() Sexpr diff(sexpr) Exp Log Sexpr eval(map) Abs String getname() Sexpr eval(map) Sexpr diff(sexpr) String getname() Sexpr eval(map) String getname() int priority() En förkortad version av koden finns i bilagan. Du vill lägga till operatorn ˆ, som står för upphöjt till. xˆy=z ska tolkas som (xˆz)=z x+yˆz ska tolkas som x+(yˆz) x*yˆz ska tolkas som x*(yˆz) xˆy ska tolkas som ( x)ˆy xˆyˆz ska tolkas som (xˆy)ˆz xˆyˆzˆw ska tolkas som ((xˆy)ˆz)ˆw o.s.v. a) I vilken metod i parsern ska hanteringen av ˆ läggas till? Skriv den kod som behövs och ange vilka eventuella övriga ändringar som behöver göras i parsern! Eftersom ˆ har högre prioritet än multiplikation och division, men lägre än variabler och unära
operatorer, ska hanteringen ligga i factor. while-loopen i factor ändras till: char c; while((c=tokenizer.getchar()) == \ c == ^ ) { tokenizer.nexttoken(); if( ^ == c) { result = new Power(result, primary()); else { <Existerande kod för att skapa en Differentiation> Inga andra ändringar behövs i Parser. b) Upphöjt till ska representeras av en klass som heter Power. Var i klassdiagrammet ska den klassen ligga? Motivera! Den ska ligga under Binary eftersom en Power-nod har två barn. c) Vilka metoder behöver implementeras i Power? Motivera! Metoderna getname() och eval() behöver implementeras eftersom de är abstrakta. Metoderna priority() och diff() behöver implementeras eftersom vi inte vill ha det beteende som definieras i Sexpr. (Prioriteten ska vara lägre än defaultvärdet och en Power ska gå att derivera.)