Det Matematisk-naturvitenskapelege fakultet

Transcription

Det Matematisk-naturvitenskapelege fakultet
Bokmål
Det Matematisk-naturvitenskapelege fakultet
UNIVERSITETET I BERGEN
Eksamen i emnet INF101/INF101F – Programmering II
Mandag 24. september 2012, kl. 09-14.
Tillatte hjelpemidler: alle skrevne og trykte.
Antall sider: 5.
• Det vil lønne seg å skaffe seg oversikt over hele eksamenssettet før du begynner å løse det.
• Du har lov å bruke resultatene fra andre oppgaver og punkt, selv om du ikke har løst disse når du trenger
resultatene.
• Alle spørsmål skal besvares, og alle svar skal begrunnes.
• Prosentsatsene angir omtrentlig vekt ved sensur.
Oppgave 1 – Teori: Referanser [10%]
a) (5/10) Programmet i Figur 1 implementerer en veldig enkel lenket liste. Studér programmet,
og tegn noen enkle diagrammer som illustrerer hvordan datastrukturene til c, bc, og abc ser
ut etter konstruksjon, og etter tilordningene på linje 31 og 36.
b) (5/10) Hva blir resultatet (utskriften) av programmet? Forklar kort.
Oppgave 2 – Minesweeper [30%]
Det tradisjonelle spillet Minesweeper har røtter helt tilbake til 60-tallet, og var et populært
prosjekt blant hobbyprogrammører på 80-tallet. Spillet foregår på et rutebrett, der det er utplassert
et antall skjulte miner. Målet er å avdekke alle rutene der det ikke er plassert miner. Som hjelp
markerer spillet – for hver avdekket rute – hvor mange av naborutene som inneholder miner.
Vi skal implementere en liten del av funksjonaliteten til Minesweeper.
Vi begynner med å definere en klasse MineSweeper, som bruker IArray2D (se vedlegg) for å
representere spillbrettet. Klassen skal holde rede på
• hvor minene er plassert,
• hvilke felter som er åpnet/klarert, og hvor mange nabominer et åpent felt har
a) (10/30)
Du kan velge å bruke én eller flere IArray2D til å lagre informasjonen.
• Deklarér feltvariablene til klassen MineSweeper.
• Deklarér eventuelle hjelpeklasser (du trenger ikke skrive kode for
konstruktører/get*/set* for disse).
1
1 public class Node<T> {
2
private T data;
3
private Node<T> next;
4
5
/** Construct new list node
6
* @param data Data value
7
* @param next Reference to tail, or null if this is the last element
8
*/
9
public Node(T data, Node<T> next) {
10
this.data = data;
11
this.next = next;
12
}
13
14
/**
15
* @return A dot-terminated, comma-separated representation of
16
* the list, e.g., "A, B, C."
17
*/
18
public String toString() {
19
return data + (next == null ? "." : ", " + next.toString());
20
}
21
22
public static void main(String[] args) {
23
Node<String> c = new Node<String>("C", null);
24
Node<String> bc = new Node<String>("B", c);
25
Node<String> abc = new Node<String>("A", bc);
26
27
System.out.println("c: " + c.toString());
28
System.out.println("bc: " + bc.toString());
29
System.out.println("abc: " + abc.toString());
30
31
bc = new Node<String>("D", new Node<String>("E", null));
32
System.out.println("c: " + c.toString());
33
System.out.println("bc: " + bc.toString());
34
System.out.println("abc: " + abc.toString());
35
36
c.data = "X";
37
System.out.println("c: " + c.toString());
38
System.out.println("bc: " + bc.toString());
39
System.out.println("abc: " + abc.toString());
40
}
41 }
Figur 1: En enkel implementasjon av lenket liste.
2
Skriv (med ord) en datainvariant som beskriver hvilke verdier i datastrukturen som
representerer gyldige brett, i henhold til spillereglene.
b) (10/30)
Skriv en metode
public int mineDetector(Position pos)
som går gjennom alle nabofeltene til pos, og teller hvor mange miner som er plassert der.
Metoden returnerer −1 dersom det er en mine på posisjon pos, ellers 0–8 avhengig av hvor
mange miner nabofeltene har.
Tips: Lag en metode som gitt en posisjon returnerer en liste av (eller iterator over) alle
naboer til posisjonen.
Hint: Husk å ta hensyn til kanten av spillebrettet!
c) (10/30)
Skriv en metode
public void clear(Position pos)
som
• Kaster MineExplodedException dersom pos inneholder en mine.
• Teller nabominene, og merker brettet med hvor mange naboer som ble funnet.
• Dersom antall naboer er 0, skal metoden fortsette på alle de omkringliggende feltene,
og klarere dem. Dette gir effekten med et stort område som åpner seg hvis man er
‘heldig’ og klikker et sted uten miner i nærheten.
Hint: Husk å ta hensyn til kanten av spillebrettet!
Oppgave 3 – Temperaturproblemer [60%]
Studenten Peer holder på å lage et lite programbibliotek for å håndtere temperaturer, og han
begynner med følgende kode.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/** Temperatur i grader Celsius.
Alltid større enn det absolutte nullpunkt, -273,15◦ C.
*/
public class Celsius implements Comparable<Celsius> {
protected double g;
public boolean datainvariant () { return g >= -273.15; }
public Celsius ( double x ) { g = x; }
public double toKelvin () { return g+273.15; }
public int
if ( g
if ( g
return
}
compareTo ( Celsius x ) {
< x.g ) return -1;
> x.g ) return +1;
0;
}
Han passer nøye på å definere datainvarianten som sikrer at temperaturen ikke skal bli lavere enn
det absolutte null-punkt (den teoretisk laveste temperaturen som finnes). Kelvin er en
temperaturskala som starter på det absolutte nullpunkt, og som ellers følger Celsius-skalaen.
For å sjekke implementasjonen skriver Peer følgende aksiom.
3
public static void compareKelvin ( Celsius a, Celsius b ) {
assertEquals( a.compareTo(b) <= 0, a.toKelvin() <= b.toKelvin() );
}
Aksiomet bruker den statiske metoden assertEquals fra pakken junit.framework.Assert
til å utføre selve testen. Det sjekker at det er en sammenheng mellom ordningen på temperaturene
og deres verdi i Kelvin.
Etter å ha studert temperatur-begrepet noe mer, finner Peer ut at han også vil lage klasser for
måleskalaene Fahrenheit og Kelvin. Disse organiserer han som subklasser til klassen Celsius:
1
2
3
4
5
6
7
/** Temperatur i Kelvin, en absolutt skala som følger Celsius.
*/
public class Kelvin extends Celsius {
public boolean datainvariant () { return g >= 0; }
public Kelvin ( double x ) { super(0); g = x; }
public double toKelvin () { return g; }
}
1
2
3
4
5
6
7
/** Temperatur i Fahrenheit.
*/
public class Fahrenheit extends Celsius {
public boolean datainvariant () { return g >= -459.67; }
public Fahrenheit ( double x ) { super(0); g = x; }
public double toKelvin () { return (g + 459.67) * 5/9; }
}
a) (10/60)
Slik koden nå er skrevet er det fullt mulig å sette data som bryter de deklarerte
datainvariantene. Hvilke rutiner (konstruktører og metoder) må modifiseres for å sikre oss
mot dette?
Skriv om de aktuelle rutinene slik at de kaster et passende, egendefinert unntak (exception)
dersom en ulovlig temperatur blir satt.
b) (10/60)
Anta at det finnes et testdatasett i tabellen
Celsius[] dataCelsius;
Lag en enhetstest (JUnit-testmetode) som bruker disse dataene til å sjekke aksiomet
compareKelvin.
c) (10/60)
Initialiser tabellen dataCelsius med et testdatasett som gjør at aksiomet compareKelvin
avslører en alvorlig feil i koden.
Forklar hvordan problemet oppstår.
Hint: aksiomet skal være korrekt også for temperaturer representert i ulike
temperaturskalaer.
d) (10/60)
Peer forsøker nå å reparere koden ved å føye følgende kode til klassen Kelvin (og lage
tilsvarende metoder for de andre subklassene).
public int compareTo ( Kelvin x ) {
if ( g < x.g ) return -1;
if ( g > x.g ) return +1;
return 0;
}
4
public int compareTo ( Celsius x ) {
if ( g < x.g+273.15 ) return -1;
if ( g > x.g+273.15 ) return +1;
return 0;
}
public int compareTo ( Fahrenheit x ) {
if ( g < x.toKelvin() ) return -1;
if ( g > x.toKelvin() ) return +1;
return 0;
}
Hvorfor vil ikke dette hjelpe på problemet?
Hint: Hva er typen til a og b i compareKelvin?
Utfør en endring i den opprinnelige koden som vil løse problemet oppdaget i c).
e) (10/60)
Hva vil det si at metoden equals er kompatibel med den naturlige ordningen (compareTo)?
Er dette tilfelle med koden slik den er?
Hvis ikke, lag en implementasjon av equals som er korrekt mht. dette kravet.
f) (10/60)
Synes du at Peer har gjort gode valg når han har designet temperatur-klassene? Hva er
eventuelt problematisk? Forklar hvordan du selv ville løst oppgaven å lage et
programbibliotek for å håndtere temperaturer i forskjellige skalaer.
Lykke til!
Anya Helene Bagge
5
Vedlegg
IArray2D.java
1 /**
2 * Grensesnitt for en 2D-tabell.
3 *
4 * Du kan anta at det finnes en
5 * class Array2D<E> implements IArray2D<E>
6 * og at du kan lage en ny tabell, initialisert med null, med
7 * new Array2D<E>(width, height)
8 *
9 * @param <E> Elementtypen
10 */
11 public interface IArray2D<E> {
12
/**
13
* @param pos En posisjon
14
* @return Elementet på posisjonen pos, eller null
15
* @requires isValid(pos)
16
*/
17
E get(Position pos);
18
19
/**
20
* @param pos En posisjon
21
* @param e Nytt element på posisjonen pos, eller null
22
* @requires isValid(pos)
23
*/
24
void set(Position pos, E e);
25
26
/**
27
* @return Høyden
28
*/
29
int getHeight();
30
31
/**
32
* @return Bredden
33
*/
34
int getWidth();
35
36
/**
37
* @param pos En posisjon
38
* @return true hvis posisjonen er gyldig i tabellen
39
*/
40
boolean isValid(Position pos);
41
42
/**
43
* @return en kopi av tabellen
44
*/
45
IArray2D<E> copy();
46 }
i
Position.java
1 /** Koordinater */
2 public class Position {
3
/** X- og Y-Verdiene */
4
private final int x, y;
5
6
/**
7
* Konstruer en ny posisjon
8
*/
9
public Position(int x, int y) {
10
this.x = x;
11
this.y = y;
12
}
13
14
/** @return X-koordinaten */
15
public int getX() {
16
return x;
17
}
18
19
/** @return Y-koordinaten */
20
public int getY() {
21
return y;
22
}
23 }
ii