Lösningsförslag till tentamen i EDA011 Programmeringsteknik för F, E, I, π och N Måndagen den 24 april 2006 Del 1 Uppgift 1 Klassen VotingResult innehåller förhoppningsvis inga större överraskningar, operationen add underlättas väsentligt av att vi kunde förutsätta att indata var korrekta, och getvote-operationen blir lättare om vi kan förutsätta att rösterna har lagts in i poängordning: class VotingResult { private Country country; // vilket land som ger rösterna private Vote[] votes = new Vote[10]; private int nbrofvotes = 0; public VotingResult(Country country) { this.country = country; public Country getcountry() { return this.country; public void add(country country, int points) { this.votes[this.nbrofvotes++] = new Vote(country, points); public boolean containsvotefor(country country) { for (int i = 0; i < this.nbrofvotes; i++) { if (this.votes[i].getcountry() == country) { return true; return false; public Vote getvote(int points) { if (points < 1 points > 10) { return null; return this.votes[points-1]; 1
Eftersom vi kunde förutsätta att rösterna hade lagts in i poäng-ordning så vet vi precis var Voteobjektet med ett givet antal poäng finns. Det är ändå helt OK att göra en sökning (denna lösning är i själva verket mer generell): public Vote getvote(int points) { for (int p = 0; p < 10; p++) { if (this.votes[p].getpoints() == points) { return this.votes[p]; return null; Uppgift 2 Denna uppgift gav 27 poäng, och utgör därför mer än hälften av del 1. class Contest { private Country[] countries = new Country[32]; private int nbrofcountries; private VotingResult[] results = new VotingResult[32]; private int nbrofvotingresults; public Country findcountry(string countrycode) { for (int i = 0; i < this.nbrofcountries; i++) { if (this.countries[i].getcode().equals(countrycode)) { return this.countries[i]; return null; public void inputcountries() { for (;;) { String code = Keyboard.nextLine("Landskod: "); if (code.length() == 0) { return; Country country = this.findcountry(code); if (country!= null) { System.out.printf("Du har redan lagt in %s.\n", country.getname()); else { String name = Keyboard.nextLine("Landsnamn: "); this.countries[this.nbrofcountries++] = new Country(name,code); public boolean hasvoted(country country) { for (int i = 0; i < this.nbrofvotingresults; i++) { if (results[i].getcountry() == country) { return true; 2
return false; public VotingResult inputvotingresult(country country) { VotingResult result = new VotingResult(country); for (int points = 1; points <= 10; ) { String code = Keyboard.nextLine("Vem skall få " + points + " poäng: "); if (code.equals(country.getcode())) { System.out.println("Ni får inte rösta på er själva!"); else { Country c = this.findcountry(code); if (c == null) { System.out.println("Det finns inget sådant land!"); else if (result.containsvotefor(c)) { System.out.println("Ni har redan gett poäng till " + c.getname()); else { result.add(c, points++); return result; public void apply(votingresult result) { this.results[this.nbrofvotingresults++] = result; for (int i = 1; i <= 10; i++) { Vote p = result.getvote(i); p.getcountry().addpoints(p.getpoints()); public void printresults() { for (int i = 0; i < this.nbrofcountries; i++) { Country country = this.countries[i]; System.out.printf("%-20s:%4d\n", country.getname(), country.getpoints()); Vi hade naturligtvis kunnat ge länderna sina poäng redan i inputvotingresult, och lagt in det nya VotingResult-objektet samtidigt uppdelningen på tentan var bara för att ge er chansen att visa att ni kan hämta värden ur olika slags objekt. Vi kan förenkla apply-operationen på olika sätt, exempelvis till: public void apply(votingresult result) { this.results[this.nbrofvotingresults++] = result; for (int i = 1; i <= 10; i++) { Vote p = result.getvote(i); p.getcountry().addpoints(i); Uppgift 3 public static void main(string[] args) { 3
Contest contest = new Contest(); mainloop: for (;;) { System.out.println("Vad vill du göra:"); System.out.println("1. Mata in länder."); System.out.println("2. Mata in resultat."); System.out.println("3. Skriva ut resultat."); System.out.println("0. Avsluta."); int cmd = Keyboard.nextInt("Ange kommando (0-3): "); Keyboard.nextLine(); // Läser bort radslut, behövde ni inte göra switch (cmd) { case 1: contest.inputcountries(); case 2: String countrycode = Keyboard.nextLine("Vem skall rösta (landskod): "); Country country = contest.findcountry(countrycode); if (country == null) { System.out.println("Något sådant land deltar inte."); else if (contest.hasvoted(country)) { System.out.printf("%s land har redan röstat.\n", country.getname()); else { VotingResult result = contest.inputvotingresult(country); contest.apply(result); case 3: contest.printresults(); case 0: break mainloop; System.out.println("Tack för denna gång..."); Del 2 Uppgift 4 Det är långtifrån självklart vilka klasser och operationer som man skall använda, men ett förslag är följande: Schedule, som beskriver ett dagsschema, Appointment, som beskriver en aktivitet i schemat, och Time, som beskriver en tidpunkt. Enklast är nog att skriva huvudprogrammet, och använda önsketänkande (dvs använda operationer som vi önskar att vi hade): class NapFinder { public static void main(string[] args) { new NapFinder(); 4
public NapFinder() { Schedule schedule = new Schedule(); for (;;) { int starthour = Keyboard.nextInt(); if (starthour == 0) { Time start = new Time(startHour, Keyboard.nextInt()); Time finish = new Time(Keyboard.nextInt(), Keyboard.nextInt()); Keyboard.nextLine(); // läs bort radslut efter sluttid schedule.add(start, finish, Keyboard.nextLine()); Appointment nap = schedule.findnap(); if (nap.getlength().equals(new Time(0,0))) { System.out.println("Det finns inte tid för någon tupplur idag."); else { System.out.printf("Du kan boka in en tupplur från %s till %s,\n", nap.getstart(), nap.getfinish()); System.out.printf("%s.\n", nap.getcomment()); System.out.printf("Längden på tuppluren blir %s.\n", nap.getfinish().minus(nap.getstart())); Vi börjar alltså med att skapa ett schema, och lägger in arbetsdagens början och slut med en gång. Därefter läser vi in dagens aktiviteter, och lägger in dem i schemat. När detta är gjort kan vi be schemat att plocka fram en lämplig ledig plats om vi är lite fiffiga kan vi låta denna plats vara en Appointment (som ovan). Klassen Schedule behöver hålla reda på sina aktiviteter, och vi kan lägga dem i en vektor eller en lista (på tentan räknas de som helt likvärdiga jag använder en lista nedan). Att leta upp den största luckan blir betydligt enklare om våra aktiviteter är sorterade, så vi gör klassen Appointment sorterbar (dvs den får implementera Comparable<Appointment>) vi kan på så vis anropa Collections.sort för att sortera våra aktiviteter. När detta är gjort är det rätt enkelt att hitta den största luckan, om vi har ett enkelt sätt att mäta tiden mellan två aktiviteter. Ett sätt är att låta klassen Time ha en minus-operation, som ger längden av tidsintervallet mellan två aktiviteter: class Schedule { private List<Appointment> appointments = new ArrayList<Appointment>(); public Schedule() { this.add(new Time(8,0), new Time(8,0), "arbetsdagens början"); this.add(new Time(17,0), new Time(17,0), "arbetsdagens slut"); public void add(time start, Time finish, String comment) { this.appointments.add(new Appointment(start, finish, comment)); public Appointment findnap() { Collections.sort(this.appointments); // Vi vet att det finns minst två appointments (arbetsdagens början och slut) 5
int best = 0; // index för den händelse som föregår tuppluren for (int i = 1; i < this.appointments.size()-1; i++) { Appointment before = this.appointments.get(i); Appointment after = this.appointments.get(i+1); if (after.getstart().minus(before.getfinish()).compareto( this.appointments.get(best+1).getstart().minus( this.appointments.get(best).getfinish())) > 0) { best = i; Appointment before = this.appointments.get(best); Appointment after = this.appointments.get(best+1); return new Appointment(before.getFinish(), after.getstart(), String.format("mellan %s och %s ", before.getcomment(), after.getcomment())); class Appointment implements Comparable<Appointment> { private Time start, finish; private String comment; public Appointment(Time start, Time finish, String comment) { this.start = start; this.finish = finish; this.comment = comment; public int compareto(appointment other) { return this.start.compareto(other.start); public Time getlength() { return this.finish.minus(this.start); public Time getstart() { return this.start; public Time getfinish() { return this.finish; public String getcomment() { return this.comment; public String tostring() { return String.format("%s -> %s: %s",this.start, this.finish, this.comment); 6
Klassen Time kan skrivas på många sätt, detta är ett: class Time implements Comparable<Time> { private int hour, minute; public Time(int hour, int minute) { this.hour = hour; this.minute = minute; this.normalize(); private Time(int minutes) { this(0,minutes); private void normalize() { this.hour += CSMath.div(this.minute,60); this.minute = CSMath.mod(this.minute,60); public int gethour() { return this.hour; public int getminute() { return this.minute; public String tostring() { if (this.minute < 10) { return String.format("%2d.0%d", this.hour, this.minute); return String.format("%2d.%2d", this.hour, this.minute); public Time minus(time other) { return new Time(60*this.hour + this.minute - (60*other.hour + other.minute)); public int compareto(time other) { return 60*this.hour + this.minute - (60*other.hour + other.minute); public boolean equals(time other) { // Obs! problem med överlagring från equals i Objec return this.compareto(other) == 0; 7