Programmeringsteknik för D - Institutionen för datavetenskap

}
Programmeringsteknik för D
KURSKOMPENDIUM
}
EDA016
http://cs.lth.se/eda016
2015/2016
EDA016 Programmeringsteknik,
godkända obligatoriska uppgifter 2015/2016
Skriv ditt namn och din namnteckning nedan:
Namn: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Namnteckning: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Godkända laborationsuppgifter
Datum
Laborationsledarens namnteckning
Datum
Laborationsledarens namnteckning
Lab1-quiz
Lab2-eclipse
Lab3-anv-square
Lab4-impl-square
Lab5-gissa-tal
Lab6-turtle
Lab7-maze
Lab8-vektor
Lab9-teamlab-turtlerace
Lab10-life
Lab11-teamlab-imagefilters
Godkänd inlämningsuppgift (välj en)
( ) bank ( ) mandelbrot ( ) draw ( ) egendef.
För att bli godkänd på uppgifterna måste du lösa deluppgifterna och diskutera dina lösningar
med en labhandledare. Denna diskussion är din möjlighet att få feedback på ditt program. Ta
vara på den!
Se till att labhandledaren noterar dina uppgifter som godkända på detta godkännandeblad.
Dessa underskrifter är ditt kvitto på att du är godkänd på laborationerna. Spara dem tills du
fått slutbetyg i kursen.
LUNDS TEKNISKA HÖGSKOLA
Institutionen för datavetenskap
EDA016 Programmeringsteknik
2015/2016
Innehåll
1
2
Övningar – anvisningar
Övning 1 – Hello World . . . . . . . . . . . . .
Övning 2 – Paket, kodfiler och dokumentation
Övning 3 – beräkningar, klasser och objekt . .
Övning 4 – Aritmetik, Logik . . . . . . . . . . .
Övning 5 – Klasser, slumptal . . . . . . . . . .
Övning 6 – vektorer och registrering . . . . . .
Övning 7 – registrering . . . . . . . . . . . . . .
Övning 8 – matriser, String och StringBuilder .
Övning 9 – ArrayList . . . . . . . . . . . . . . .
Övning 10 – arv . . . . . . . . . . . . . . . . . .
Övning 11 – sortering, objekt . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
8
11
15
17
18
20
22
24
26
28
30
Laborationer – anvisningar
Laboration 1 – Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Laboration 2 – Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Laboration 3 – använda färdigskrivna klasser, kvadrat . . . . . . . . .
Laboration 4 – implementera klasser (Square), samt felsökning . . . .
Laboration 5 – implementera klasser, gissa tal . . . . . . . . . . . . . .
Laboration 6 – implementera och ärva klasser, Turtle och ColorTurtle .
Laboration 7 – Maze . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Laboration 8 – vektorer, simulering av patiens . . . . . . . . . . . . . .
Grupplaboration 9 – TurtleRace, ArrayList och ärvning . . . . . . . . .
Laboration 10 – matris, spelet life . . . . . . . . . . . . . . . . . . . . . .
Grupplaboration 11 – Matriser, bilbehandling ImageFilter . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
33
34
38
43
49
55
57
63
66
70
74
79
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
Inlämningsuppgift – anvisningar
89
Inlämningsuppgift alternativ 1 – bankapplikation . . . . . . . . . . . . . . . . . . . . . . 90
Inlämningsuppgift alternativ 2 – Mandelbrot . . . . . . . . . . . . . . . . . . . . . . . . . 99
Inlämningsuppgift alternativ 3 – Draw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
4
Eclipse – en handledning
109
5
7
1
Övningar – anvisningar
1. Övningarna är förberedelser inför laborationerna och ger viktig träning inför den skriftliga
tentamen.
2. Du får hjälp med övningarna av handledare på resurstiderna.
3. Vilken övning som är lämplig att göra vilken vecka framgår av kursprogrammet. Försök
ligga i fas, så att du är redo inför laborationerna.
4. Du löser övningsuppgifterna tillsammans med en kamrat eller självständigt. Prova först
att lösa övningen självständigt och diskutera gärna sedan din lösning med en kamrat.
5. Lösningsförslag finns på kurshemsidan. Titta på lösningarna efter att du själv löst uppgiften och jämför med din egen lösning. I allmänhet kan programmeringsproblem lösas
på många olika sätt; vissa är kanske bättre än andra, medan vissa kan vara bra ur olika
aspekter beroende på hur programmet används.
6. Förutom uppgifterna i detta häfte finns övningsuppgifter i läroboken och extra programmeringsuppgifter på kurshemsidan.
Övning 1 – Hello World
8
Övning 1 – Hello World
Mål: Under denna övning lär du dig att köra igång ett program och att resonera kring variabler
och tilldelning. Det viktiga med denna övning är att du bekantar dig med programkod, men du
behöver inte förstå alla detaljer. Senare i kursen kommer vi att på djupet reda ut till exempel
public, class, static, void, main, String, [], new, Scanner, och andra begrepp som ännu inte
fått någon djupare förklaring.
Förberedelser
Läs i läroboken: avsnitt 1, 3.2 (samt 7.8 för mer om Scanner)
Välj editor. Undersök vilka kodeditorer som finns och välj en som du vill lära dig. 1 Nedan finns
en lista på populära editorer. Om du inte vet vilken du ska välja, testa först gedit.
• gedit – öppen, fri och gratis; lätt att lära men inte så avancerad; finns för Windows, Mac
och Linux. Editorn gedit finns på LTH:s Linux-datorer och startas med kommandot gedit
Ladda ner till din egen dator här: https://wiki.gnome.org/Apps/Gedit
• Sublime Text 3 – gratis att prova på, men programmet föreslår då och då att du köper en
licens; finns för Windows, Mac, Linux.
http://www.sublimetext.com/3
• Notepad++ – öppen, fri och gratis; finns endast för Windows.
https://notepad-plus-plus.org/
• Textwrangler – gratis; lätt att lära men inte så avancerad; finns endast för Mac.
http://www.barebones.com/products/textwrangler/
• Atom – öppen, fri och gratis; finns för Windows, Mac, Linux. Utvecklas av GitHub; lovande
men ännu inte lika mogen som övriga.
https://atom.io/
• Vim – öppen, fri och gratis; hög inlärningströskel; finns för Windows, Mac och är förinstallerad på LTH:s Linux-datorer i E-huset.
http://www.vim.org/
• Emacs – öppen, fri och gratis; hög inlärningströskel; finns för Windows, Mac och är
förinstallerad på LTH:s Linux-datorer i E-huset.
http://www.gnu.org/software/emacs/
Editera, kompilera, exekvera
a) Öppna din editor och skriv in nedan program och spara det i en fil med namnet HelloWorld.java
i en ny katalog:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hej!");
}
}
1 Gör till exempel en nätsökning på ”best free code editor” eller se här: http://en.wikipedia.org/wiki/Comparison_
of_text_editors.
Övning 1 – Hello World
9
b) Öppna ett kommandofönster (Terminal i Linux/Mac eller Powershell i Windows) och navigera vid behov med kommandot cd till katalogen där din fil befinner sig.
c) Kompilera ditt program med kommandot javac HelloWorld.java och kontrollera med kommandot ls att en fil med namnet HelloWorld.class skapats.
d) Kör ditt program med kommandot java HelloWorld och kontrollera att utskriften blir som
förväntat.
e) Ändra textsträngen som skrivs ut till något annat. Spara, kompilera om och kör igen.
f) Skapa fel i koden så att kompilatorn skriver ut nedanstående felmeddelanden. Kompilera om
efter varje infört fel. Rätta felet igen innan du inför nästa fel.
(i) HelloWorld.java:3: error: ’;’ expected
(ii) HelloWorld.java:3: error: unclosed string literal
(iii) HelloWorld.java:3: error: ’)’ expected
(iv) HelloWorld.java:3: error: cannot find symbol
symbol:
method skrivRad(String)
location: variable out of type PrintStream
Uppgifter på papper
1.
Skriv kod som beräknar summan av talen 3, 8 och 12 och skriver ut resultatet.
2.
Betrakta följande programavsnitt:
int nbrA = 2;
int nbrB = nbrA + 3;
int nbrC = 3 * (nbrA + nbrB) - 1;
nbrA = nbrC / 5;
nbrC = 0;
En bild av minnessituationen (vilka variabler som finns och variablernas värden) ska ritas
efter varje kodrad. Efter första tilldelningssatsen ser det ut så här:
nbrA
2
Rita de fyra övriga bilderna för varje rad.
3.
Betrakta följande program, där fyra heltal läses från tangentbordet och några (ointressanta) beräkningar görs:
public class Example1 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int a = scan.nextInt();
int b = scan.nextInt();
int c = scan.nextInt();
int d = scan.nextInt();
int e = (c + d) - (a + b);
c = c + 2;
a = (2 * e + c) / 4;
System.out.println(a + " " + b + " " + c + " " + d + " " + e);
}
}
Övning 1 – Hello World
10
a) En av parenteserna i programmet är onödig. Vilken? Varför?
b) Vilka värden skrivs ut när talen 1, 2, 4 och 8 läses in från tangentbordet? Använd en
tabell där du noterar de successiva variabelvärdena.
c) Vilka värden skrivs ut när talen 1, 2, 8 och 4 läses in från tangentbordet?
4.
Två heltalsvariabler a och b har deklarerats och fått värden. Skriv satser som byter värde
på a och b (”swappar” värdena). Exempel:
int a = 10;
int b = 25;
// ... dina satser
System.out.println(a + " " + b); // ger utskriften 25 10
Extrauppgift vid datorn
Skriv följande kod i en fil med namnet HelloArgs.java:
public class HelloArgs {
public static void main(String[] args) {
System.out.println("Argument noll: " + args[0]);
if (args[0].equals("42")) {
System.out.println("Du har upptäckt hemligheten!");
} else {
System.out.println("Tack och hej!");
}
}
}
a) Spara, kompilera och kör med kommandot java HelloArgs gurka
b) Vad skriver programmet ut?
c) Vad händer om du byter ut gurka mot 42?
d) Vad händer om du utelämnar argumentet?
e) Vad händer om du ger mer än ett argument?
f) Ändra i programmet så att det hemliga meddelandet skrivs ut när det andra argumentet
innehåller 42.
g) Kompilera om och kör programmet upprepade gånger så här:
java HelloArgs gurka 42
java HelloArgs tomat 43
och kontrollera att programmet fungerar som förväntat.
Övning 2 – Paket, kodfiler och dokumentation
11
Övning 2 – Paket, kodfiler och dokumentation
Mål: Du lär dig skriva ett program med ett simpelt grafiskt fönster. Du lär dig hur kodfiler och
klassfiler kan organiseras i paket och sammanföras i jar-filer. Då använder import för att göra
klasser i paket tillgängliga. Denna övning visar hur man manuellt skapar en katalogstruktur,
kompilerar, bygger och kör en applikation från olika kod-filer; detta sköts automatiskt av en
integrerad utvecklingsmiljö, som vi kommer att använda under kommande laborationer.
Förberedelser
Läs i läroboken: avsnitt Appendix B.1 och B.2
Uppgifter vid dator
1.
Skapa ett paket. När man har stora program kan det bli många .java- och .class-filer.
Det blir då lätt rörigt att ha alla filer i samma bibliotek. Man brukar därför skapa en
katalogstruktur för kodfiler.
a) Skapa en katalog ovn2 och skapa i den en underkatalog med namnet src som i sin
tur innehåller katalogen greeting med dessa kommandon i terminalen (Linux/Mac)
eller powershell (Windows):
mkdir ovn2
mkdir ovn2/src
mkdir ovn2/src/greeting
cd ovn2
b) Skapa med en editor nedanstående program.
package greeting;
public class Hello {
public static void main(String[] args) {
String name = javax.swing.JOptionPane.showInputDialog("What name?");
javax.swing.JOptionPane.showMessageDialog(null, "Hello " + name);
}
}
Spara programmet i filen ovn2/src/greeting/Hello.java.
c) Nyckelordet package anger i vilket bibliotek som kompilatorn ska lägga .classfilerna. Man brukar placera .class-filer i ett bibliotek med namnet bin. Skapa en
katalog bin i katalogen ovn2.
d) Prova kommandot javac -help och studera utskriften. Vad gör optionen -d och
vilken typ av argument ska man ge efter -d?
e) Kompilera ditt program med javac -d bin src/greeting/Hello.java
f) Var har kompilatorn lagt Hello.class-filen?
g) Kör ditt program med java -classpath bin greeting.Hello där punkten används
för att ”gå in i” paketet greeting och ange att main-metoden ligger i klassen Hello.
Vad händer? Vad gör metoderna showInputDialog och showMessageDialog?
h) Kör kommandot java -help och studera utskriften. Vad används optionen -classpath
till? Vad har optionen för kortare motsvarighet?
i) Ändra i ditt program så att ledtexten i frågan och utskrifttexten i meddelandet blir
något annat. Kompilera om och kör på nytt.
j) Lägg till fler frågor och svar i ditt program.
12
Övning 2 – Paket, kodfiler och dokumentation
2.
Använda färdiga paket. När man installerar Java medföljer en stor mängd färdiga standardklasser. De är så många att man har paket inuti paket i flera steg. Ett exempel på
detta ser du i programmet ovan: Paketet javax innehåller en mängd olika paket varav ett
heter swing och innehåller klasser för att skapa grafiska användargränssnitt. Programmet
ovan använder klassen JOptionPane och kompilatorn hittar den tack vare att paketen den
ligger i anges i tur och ordning med punkt emellan: javax.swing.JOptionPane
a) Det blir mycket att skriva om en klass ligger djupt nere i en paketstruktur. Därför
finns nyckelordet import. Lägg till denna rad i ditt program efter paket-deklarationen:
import javax.swing.JOptionPane;
b) Nu kan du direkt använda klassen JOptionPane utan att ange paketvägen javax.swing.
före klassnamnet. Ändra i ditt program och kontrollera att så är fallet.
3.
Använda jar-fil. Du kommer lära dig mer om att programmera grafiska användargränssnitt i senare kurser. För att göra det enkelt i denna kurs finns bl.a. en färdig klass
SimpleWindow som är utvecklad vid cs.lth.se. Denna och andra klasser finns paketerade i
en så kallad jar-fil. Jar-filer används för att samla många klassfiler i en enda fil.
a) Ladda ner filen http://fileadmin.cs.lth.se/cs/Education/EDA016/cs_pt.jar
och lägg den i bilioteket ovn2. I Linux kan du använda detta kommando för att ladda
ner filen i aktuell katalog:
wget http://fileadmin.cs.lth.se/cs/Education/EDA016/cs_pt.jar
b) Skapa med en editor nedanstående program.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package greeting;
import se.lth.cs.pt.window.SimpleWindow;
public class WindowApp {
public static void main(String[] args) {
SimpleWindow w = new SimpleWindow(200, 200, "App");
w.setLineColor(java.awt.Color.PINK);
w.moveTo(100,100);
w.writeText("Hello Pink Panter!");
while (true) {
w.waitForMouseClick();
w.lineTo(w.getMouseX(), w.getMouseY());
w.writeText("Hello Pink Panter!");
}
}
}
Spara programmet i filen ovn2/src/greeting/WindowApp.java.
c) När du kompilerar programmet behöver du se till att classpath innehåller jar-filen
där SimpleWindow ligger:
javac -cp cs_pt.jar -d bin src/greeting/WindowApp.java
d) När du kör programmet behöver du ha både katalogen bin och jar-filen med SimpleWindow
på classpath. I Linux/Mac-terminal, kör programmet med (notera kolon):
java -cp "cs_pt.jar:bin" greeting.WindowApp
I Windows powershell, byt ut kolon mot semikolon:
java -cp "cs_pt.jar;bin" greeting.WindowApp
4.
Använda dokumentation. För att det ska bli lättare att använda färdiga klasser finns ofta
dokumentation som kan öppnas i en webbläsare.
Övning 2 – Paket, kodfiler och dokumentation
13
a) Läs dokumentationen om SimpleWindow på kursens hemsida. Ta reda på vad metoderna som anropas på objektet w i programmet ovan gör. Ändra linjebredden så att
ett tjockare rosa streck ritas. Kompilera om och kör.
b) Gör en nätsökning på java.awt.Color.PINK och ta reda på med hjälp av dokumentationen vilka fler fördefinierade färger som finns. Ändra i programmet så att
hälsningstexten får en annan färg. Kompilera om och kör.
Extrauppgifter
5.
Skapa jar-fil. Med hjälp av kommandot jar kan man skapa och packa ihop filer i en egen
jar-fil, som även komprimeras så att den ska ta mindre plats (i likhet med s.k. zip-filer).
a) Undersök vad jar har för optioner med kommandot jar -help
b) Skapa en jar-fil med kommandot jar cvf mygreet.jar -C bin greeting
c) Lista innehållet i din jar-fil med kommandot jar tf mygreet.jar och titta även i
innehållet genom att dubbelklicka på jar-filen (i Windows och Mac behöver du döpa
om filen så att den slutar på .zip för att dubbelklick ska öppna filen som en katalog
med underkataloger).
d) Kör de olika main-metoderna i din jar-file med resp. java-kommando:
java -cp mygreet.jar greeting.Hello
java -cp mygreet.jar greeting.WindowApp
Det senare fungera inte. Vad får du för felmeddelande? Varför? Hur kan du göra så
att WindowApp kan hitta SimpleWindow?
6.
Skapa dokumentation. Med hjälp av kommandot javadoc kan man skapa webbsidor
med dokumentation av klasser.
a) Skapa med en editor nedanstående program. Kommentarer som börjar med /** är
dokumentationskommentarer (se läroboken appendix B.2).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package greeting;
import se.lth.cs.pt.window.SimpleWindow;
import java.awt.Color;
/**
* A collection of greeting methods.
*/
public class Greet {
/**
* Asks for a name and then greets politely.
*/
public static void swingGreeting(){
String name = javax.swing.JOptionPane.showInputDialog("What name?");
javax.swing.JOptionPane.showMessageDialog(null, "Hello " + name);
}
/**
* Greeting of pink creature. Allows user to spam click but not too far down.
* Each click is awarded one point.
*
* @param
color
the color of the greeting
* @return
the number of points earned
*/
public static int panterGreeting(Color color){
SimpleWindow w = new SimpleWindow(200, 200, "App");
w.setLineColor(color);
w.moveTo(100,100);
w.writeText("Hello Pink Panter!");
int points = 0;
while (w.getMouseY() < 180) {
w.waitForMouseClick();
points = points + 1; //earn a point for each click
Övning 2 – Paket, kodfiler och dokumentation
14
35
36
37
38
39
40
w.lineTo(w.getMouseX(), w.getMouseY());
w.writeText("Your hello-points: " + points);
}
return points;
}
}
b) Spara programmet i filen ovn2/src/greeting/Greet.java
c) Kompilera programmet med javac -cp cs_pt.jar -d bin src/greeting/Greet.java
d) Undersök vad javadoc-kommadot har för optioner med javadoc -help
e) Skapa katalog doc med mkdir doc i katalogen ovn2
f) Kör kommandot javadoc -d doc src/greeting/*
g) Undersök vad som ligger i doc-katalogen och öppna filen index.html genom att
dubbelklicka på den.
h) Skapa nedan huvudprogram och spara det i filen GreetApp.java
1
2
3
4
5
6
7
8
9
10
11
package greeting;
/** A polite Greetings App. */
public class GreetApp {
/** Starts the App. */
public static void main(String[] args) {
Greet.swingGreeting();
int pts = Greet.panterGreeting(java.awt.Color.PINK);
System.out.println("You earned " + pts + " points!");
}
}
i) Kompilera med javac -cp "cs_pt.jar:bin" -d bin src/greeting/GreetApp.java
och kör med java -cp "cs_pt.jar:bin" greeting.GreetApp
(Om du kör detta i Windows powershell ska alla kolon bytas ut mot semikolon)
j) Gör så att din jar-fil även innehåller klasserrna Greet och GreetApp, samt dokumentationen med dessa kommando:
javadoc -d doc src/greeting/*
jar cvf mygreet.jar -C bin greeting doc
k) Undersök innehållet i jar-filen med jar tf mygreet.jar
l) Kör igång dina nya underprogram från jar-filen med
java -cp "cs_pt.jar:mygreet.jar" greeting.GreetApp
(Om du kör detta i Windows powershell ska kolon bytas ut mot semikolon)
m) Undersök hur dokumentationen har utökats med klassen GreetApp genom att dubbelklicka på index.html i katalogen doc
Övning 3 – beräkningar, klasser och objekt
15
Övning 3 – beräkningar, klasser och objekt
Läs i läroboken: avsnitt 2, 6.3, 7.1-7.3 samt 5.1-5.3 för mer om variabler
Inläsning och beräkningar
1.
Skriv ett program (en klass med en main-metod) som först läser en starttid (två tal, timmar
och minuter, till exempel 12 41) och därefter en sluttid (två andra tal, till exempel 16 28)
och därefter beräknar och skriver ut hur många minuter det är mellan tiderna. Du kan
förutsätta att sluttiden är större än starttiden. Ledning: det behövs fyra int-variabler för
de fyra inlästa talen. Ge dessa variabler vettiga namn.
2.
Ändra i programmet från uppgift 1 så att tidsavståndet skrivs ut i timmar och minuter i stället för i minuter. Detta kan man göra på två sätt: antingen genom att använda
en if-sats eller genom att använda heltalsdivision och operatorn %, som ger resten vid
heltalsdivision (avsnitt 6.3 i läroboken).
Läsa specifikationer och använda färdiga klasser
3.
Klasserna SimpleWindow och Square från läroboken antas vara tillgängliga. Vad ritas på
skärmen när nedanstående program exekveras? Visa med en skalenlig figur.
public class SquareExample {
public static void main(String[] args) {
SimpleWindow w = new SimpleWindow(400, 400, "Squares");
Square sq1 = new Square(100, 100, 200);
Square sq2 = new Square(300, 300, 50);
sq1.draw(w);
sq2.draw(w);
}
}
4.
Nedanstående program innehåller tre fel som upptäcks av kompilatorn. Korrigera felen.
public class ErrorTest {
public static void main(String[] args) {
SimpleWindow w = new SimpleWindow(600, 600);
sq = new Square(100, 200, 50);
while sq.getX() < 300 {
sq.draw(w);
sq.move(10, 10);
}
}
}
Övning 3 – beräkningar, klasser och objekt
16
Klasser och objekt
5.
Bilden visar minnessituationen (vilka variabler som finns, variablernas värden, vilka objekt som finns, attributens värden) när följande sats har exekverats:
Square sq1 = new Square(20, 30, 40);
x
y
20
side
40
sq1
30
a) Rita en ny bild som visar hur minnessituationen förändras om man dessutom exekverar följande sats:
Square sq2 = sq1;
b) Ändra bilden i deluppgift a så att den visar minnessituationen om man slutligen
exekverar satsen:
sq1 = null;
6.
Vi brukar för enkelhets skull rita en referens som en pil som pekar på det objekt referensvariabeln refererar till. Egentligen är referensen ett tal, nämligen adressen till den plats i
minnet där objektet är lagrat. Ett exempel (ännu ej fullständigt) på hur referensvariablernas värden och objekten lagras i datorns minne visas här:
...
5200
5204
...
47540
sq1
sq2
...
47540
20
x
47544
30
y
47548
40
side
...
Gör färdigt figuren så att den visar hur det ser ut efter att följande två satser exekverats:
Square sq1 = new Square(20, 30, 40);
Square sq2 = sq1;
Övning 4 – Aritmetik, Logik
17
Övning 4 – Aritmetik, Logik
Läs i läroboken: avsnitt 6 (ej 6.5-6.7), 7 (ej 7.8-7.9 samt 7.12)
Beräkning av maximum och minimum
1.
a) Skriv ett program som från tangentbordet läser ett antal heltal (använd hasNextInt()
för att undersöka om det finns fler tal att läsa). I programmet ska du beräkna och
skriva ut det största av talen.
b) Hur förändras lösningen i deluppgift a om man istället ska söka efter minsta talet?
Aritmetiska uttryck
2.
Skriv följande som tilldelningssatser i Java:
a) y = sin(20◦ )
b) z = ae x + be−cos( x)
√
c) hyp = a2 + b2
3.
4.
Talet x är av typ double. Hur konverterar man värdet av x till närmaste int-tal?
a) Hur kan man med operatorn % avgöra om heltalet n är jämnt?
b) Hur kan man med operatorn % avgöra om heltalet n slutar med en nolla?
c) Talet number är tresiffrigt. Skriv tilldelningar så att digit1, digit2, digit3 tilldelas
värdet av respektive siffra. Använd operatorerna % och /.
Summering
5.
Skriv ett program som läser 30 temperaturvärden och beräknar och skriver ut medeltemperaturen.
6.
Indata består av 101 reella tal. Det första talet kallas limit. Skriv satser som läser talen
och beräknar två summor: dels summan av de tal som är mindre än limit, dels summan
av de tal som är större än limit. Tal som är lika med limit ska inte summeras.
7.
Ett arbete ger lön enligt följande: första dagen är lönen en krona. De följande dagarna får
man dubbelt så mycket betalt som man fick närmast föregående dag. Skriv ett program
som avgör hur många dagar man måste arbeta innan man har tjänat ihop en miljon kronor.
Logiska uttryck
8.
Förenkla följande logiska uttryck (a är en int-variabel, ready är en boolean-variabel):
a) a > 2 && a > 5
b) a > 2 || a > 5
c) !(a > 2)
d) !(a > 2 && a < 9)
e) !(a < 0 || a > 10)
f) ready == true
g) ready == false
Övning 5 – Klasser, slumptal
18
Övning 5 – Klasser, slumptal
Läs i läroboken: avsnitt 3, 6.10-6.11
1.
Implementera färdigt klassen Car:
public class Car {
private String nbr;
private Person owner;
// registreringnummer
// ägare
/** Skapar en bil med registreringsnummer licenseNbr
som ägs av personen owner. */
public Car(String licenseNbr, Person owner) {
// Lägg till egen kod här.
}
/** Tar reda på bilens registreringsnummer. */
public String getLicenseNbr() {
// Lägg till egen kod här.
}
/** Tar reda på bilens ägare. */
public Person getOwner() {
// Lägg till egen kod här.
}
/** Sätter bilens ägare till newOwner. */
public void changeOwner(Person newOwner) {
// Lägg till egen kod här.
}
}
2.
Rita en figur som visar vilka objekt som finns samt värdet av alla variabler och attribut
efter det att följande satser har exekverats.
Person p = new Person("Bo Ek");
Car c1 = new Car("ABC123", p);
p = new Person("Eva Alm");
Car c2 = new Car("XYZ789", p);
Klassen Car finns i föregående uppgift och klassen Person ser ut så här.
public class Person {
private String name;
/** Skapar en person med namnet name. */
public Person(String name) {
this.name = name;
}
/** Tar reda på personens namn. */
public String getName() {
return name;
}
}
Övning 5 – Klasser, slumptal
3.
19
I ett system för bokning av platser på tåg förekommer en klass RailwayCoach som beskriver en vagn i tåget. Klassen har följande specifikation:
/** Skapar en vagn med nbrSeats platser. */
RailwayCoach(int nbrSeats);
/** Tar reda på antalet platser i vagnen. */
int getNbrSeats();
/** Tar reda på antalet lediga (obokade) platser. */
int getNbrFreeSeats();
/** Bokar n platser i vagnen. Det får förutsättas att
n <= antalet lediga platser. */
void makeReservation(int n);
Implementera klassen fullständigt, dvs deklarera attribut och implementera konstruktorn
och operationerna. Observera att man bara är intresserad av antalet lediga platser, inte
numren på de platser som är lediga.
4.
Klassen Person beskriver personer (klassen har fler operationer, men dem är vi inte intresserade av här):
/** Skapar en person med åldern age. */
Person(int age);
/** Undersöker om denna person är äldre än personen p.
Returnerar då true, annars false. */
boolean isOlderThan(Person p);
Implementera klassen.
Slumptal
5.
Skriv ett program som drar två olika slumptal mellan 1 och 100 och skriver ut dessa med
det minsta talet först.
Extrauppgift
6.
Ett heltal a0 är givet. Man beräknar en talföljd med följande formler:
(
a k +1 =
ak /2,
om ak är jämnt
3ak + 1, annars
För alla a0 större än 0 blir ak förr eller senare = 1. (Detta har ingen lyckats bevisa — det
kallas Collatz problem — men de flesta tror att det är så.) Exempel:
a0
a0
a0
a0
a0
=3
=4
=5
=6
=7
ger 10, 5, 16, 8, 4, 2, 1 (8 tal i talföljden)
ger 2, 1 (3 tal)
ger 16, 8, 4, 2, 1 (6 tal)
ger 3, 10, 5, 16, 8, 4, 2, 1 (9 tal)
ger 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1 (17 tal)
Skriv ett program som läser in ett a0 och skriver ut den talföljd som bildas med formlerna,
till och med den avslutande 1-an. Skriv också ut antalet tal i talföljden.
Övning 6 – vektorer och registrering
20
Övning 6 – vektorer och registrering
Läs i läroboken: avsnitt 8.1-8.5, 8.8, 8.10
1.
Implementera följande metoder. Undersök inte fler tal än nödvändigt.
a) /** Hittar minsta talet i vektorn v. */
public static int min(int[] v);
b) /** Undersöker om talen i vektorn v är sorterade i växande ordning. */
public static boolean isSorted(int[] v);
c) /** Undersöker om vektorerna v1 och v2 är lika, dvs. om de är lika långa
och alla v1[i] == v2[i]. */
public static boolean equal(int[] v1, int[] v2);
2.
En vektor a innehåller 100 heltal. Vilken utskrift fås av nedanstående satser om
int i = 0;
while (i < a.length && a[i] != 0) {
i++;
}
System.out.println(i);
a) det finns en nolla i vektorn, i vektorelementet a[33]?
b) det inte finns någon nolla i vektorn?
c) det finns 50 nollor i vektorn, i vektorelementen med udda index dvs. i a[1], a[3],
. . . , a[99]?
Övning 6 – vektorer och registrering
3.
21
En bostadslägenhet beskrivs av följande klass (det finns fler operationer, men de används
inte i denna uppgift):
Apartment
/** Tar reda på lägenhetens nummer */
int getId();
/** Tar reda på antalet rum i lägenheten */
int getNbrOfRooms();
En kommun har ett register över samtliga lägenheter i kommunen. Registret beskrivs av
följande klass:
Register
/** Skapar ett tomt register med plats för maxSize lägenheter */
Register(int maxSize);
/** Lägger in lägenheten a i registret. Det förutsätts att det
finns plats och att lägenheten inte redan finns i registret */
void add(Apartment a);
/** Tar bort lägenheten med nummer id ur registret. Om lägenheten
inte finns ska ingenting inträffa */
void remove(int id);
/** Räknar antalet lägenheter med 1, 2, 3, ... rum, returnerar
antalen i en vektor (antalet 1-rummare i [0], osv) */
int[] countApartments();
Implementera klassen Register. Vektorn som returneras i countApartments ska vara lika
lång som antalet rum i den största lägenheten som finns i registret.
Övning 7 – registrering
22
Övning 7 – registrering
Läs i läroboken: avsnitt 8, speciellt 8.10
1.
Följande klass beskriver en tärning:
Die
/** Skapar en tärning. */
Die();
/** Kastar tärningen, returnerar antalet prickar (1-6). */
int roll();
I ett statistikexperiment vill man kasta två tärningar och undersöka hur många gånger
som summan av prickarna på tärningarna blir 2, 3, 4, . . . , 11, 12. Experimentet beskrivs
av följande klass:
Experiment
/** Skapar ett experiment där tärningarna d1 och d2 kastas. */
Experiment(Die d1, Die d2);
/** Kastar tärningarna n gånger, räknar antalet gånger summan blev
2, 3, 4, ..., 11, 12. Returnerar antalen i en vektor. */
int[] makeExperiment(int n);
Implementera klassen.
2.
Ola Claessons järnhandel är bygdens största leverantör av fasadsiffror. Störst är försäljningen till nybyggda områden där samtliga hus ska ha fasadsiffror. Oftast får de beställningar på formen ”fasadsiffror till samtliga hus på Nygatan 101 till 125, endast udda
nummer”. Skriv följande metod som räknar ut hur många siffror av varje sort som behövs.
/** Returnerar en vektor med antal olika siffror av varje sort som
behövs för nummer mellan start och stop där intervallet mellan
numren är interval. */
int[] nbrDigits(int start, int stop, int interval) {
Inparametrar till metoden är tre positiva heltal: första nummer, sista nummer och intervallet.
Exempel: Följande vektor returneras vid anropet nbrDigits(101, 125, 2):
[0][1][2][3][4][5][6][7][8][9]
5 21 3
3
0
3
0
2
0
2
Övning 7 – registrering
3.
23
I ”handelsresandeproblemet” gäller det för en handelsresande att besöka ett antal städer.
Varje stad ska besökas exakt en gång. Handelsresanden vill att den totala resvägen ska
bli så kort som möjligt. Att lösa detta problem exakt är tidsödande, eftersom man måste
beräkna alla möjliga resvägar för att finna den kortaste vägen. Vi nöjer oss därför i denna
uppgift med att hitta en approximation till den bästa lösningen genom att i varje steg
besöka den stad som ligger närmast den senast besökta staden. Städerna och kartan med
alla städer beskrivs av följande klasser:
Town
/** Skapar en stad med namnet name. */
Town(String name);
/** Tar reda på stadens namn. */
String getName();
/** Tar reda på avståndet till staden t. */
double getDistanceTo(Town t);
/** Undersöker om denna stad är besökt. */
boolean isVisited();
/** Markera att denna stad är besökt/inte besökt (b = true/false). */
void setVisited(boolean b);
// Övriga metoder i klassen
public class Map {
private Town[] towns; // städerna
/** Skapar en karta med städer. Uppgifter om städerna läses in
från en fil med namnet mapFile. */
public Map(String mapFile) {
// Här läses antalet städer från filen mapFile och vektorn
// towns skapas. Därefter läses uppgifter om städerna,
// Town-objekt skapas och läggs in i vektorn towns.
// Konstruktorn är färdigskriven.
}
/** Skriver ut namnet på de städer som besöks, i tur och ordning,
när man påbörjar en resa i staden med namnet startTown. Det
förutsätts att det finns en stad med det namnet. */
public void printTour(String startTown) {
// Markera alla städer som obesökta.
// Skriv ut startstadens namn, markera den som besökt.
// Leta upp närmaste stad som inte är besökt, skriv ut
// dess namn, markera den som besökt.Osv.
}
/* Tar reda på, och returnera, den obesökta stad som ligger närmast staden t.
Om inga obesökta städer finns ska null returneras. */
private Town getNearest(Town t);
}
Implementera metoderna printTour och getNearest i klassen Map.
Övning 8 – matriser, String och StringBuilder
24
Övning 8 – matriser, String och StringBuilder
Läs i läroboken: avsnitt 8.6,8.7, 6.6, 6.7 och 11
Matriser
1.
Klassen Matrix beskriver en kvadratisk matris:
public class Matrix {
private double[][] a;
// talen i matrisen
/** Skapar en matris med n rader och n kolonner. */
public Matrix(int n) {
a = new double[n][n];
}
/** Tar reda på elementet med index i, k. */
public double get(int i, int k) {...}
/** Adderar matrisen m till denna matris (matriserna förutsätts vara lika
stora). */
public void add(Matrix m) {...}
/** Beräknar spåret av matrisen, dvs summan av diagonalelementen från övre
vänstra hörnet till nedre högra hörnet. */
public double trace() {...}
}
Implementera de tre operationerna get, add och trace. Vid addition av matriser adderar
man element för element.
String och StringBuilder
2.
Antag att metoderna equals och compareTo i klassen String inte är tillgängliga. Skriv
följande metod:
/** Undersöker om strängarna s1 och s2 är lika långa och alla
tecknen i s1 är lika med motsvarande tecken i s2. */
public static boolean equals(String s1, String s2);
3.
Skriv följande metod:
/** Bildar en sträng som innehåller tecknen i s i omvänd ordning. */
public String reverse(String s);
Till exempel ska reverse(”asdfg”) ge strängen ”gfdsa”. Använd ett StringBuilderobjekt.
Extrauppgift
4.
Slumptal kan användas för att kryptera texter. Man behöver en slumptalsgenerator som
kan initieras med ett slumptalsfrö så att den kan upprepa följden av slumptal. (Man
måste kunna få exakt samma slumptalsföljd när man senare vill dechiffrera texten.) Klassen java.util.Random är en sådan klass — man använder den konstruktor som har ett
slumptalsfrö som parameter.
Övning 8 – matriser, String och StringBuilder
25
Kryptering av en text går till på följande sätt:
• Välj en krypteringsnyckel, ett long-tal key.
• Skapa slumptalsgeneratorn med key som slumptalsfrö.
• För varje tecken i texten: dra ett slumptal, addera det till tecknet.
Vi förutsätter här att teckenkoderna för tecknen ligger i intervallet [0, 256) och att slumptalen ligger i samma intervall. För att också de krypterade tecknen ska hålla sig inom
intervallet ska additionen göras ”cykliskt”, dvs efter tecknet med koden 255 anses tecknet
med koden 0 komma. Exempel (teckenkoderna har skrivits i decimal form):
Tecken:
Teckenkod:
Slumptal:
Krypterad kod:
Krypterat tecken:
A
65
4
69
E
t
116
207
67
C
t
116
6
122
z
a
97
1
98
b
c
99
12
111
o
k
107
255
106
j
!
33
8
41
)
Dekryptering av texten görs genom att man initierar slumptalsgeneratorn med samma
krypteringsnyckel och subtraherar slumptalen från teckenkoderna.
Skriv en metod som krypterar en text plainText utgående från krypteringsnyckeln
key. Den krypterade texten ska returneras som resultat. Metoden ska ha följande rubrik:
public static String encrypt(String plainText, long key);
Övning 9 – ArrayList
26
Övning 9 – ArrayList
Läs i läroboken: avsnitt 12.1-12.7
1.
Ett ArrayList-objekt cards är deklarerad och skapad enligt följande:
ArrayList<Card> cards = new ArrayList<Card>();
// Här läggs ett antal kort in i cards
Klassen Card har följande specifikation:
Card
/** Konstanter för färgern: */
static final int SPADES = ...;
static final int HEARTS = SPADES + 1;
static final int DIAMONDS = SPADES + 2;
static final int CLUBS = SPADES + 3;
/** Skapar ett spelkort med färgen suit (SPADES, HEARTS, DIAMONDS,
CLUBS) och valören rank (1-13). */
Card(int suit, int rank);
/** Tar reda på färgen. */
int getSuit();
/** Tar reda på valören. */
int getRank();
a) Skriv satser för att summera kortens valörer.
b) Skriv satser som tar reda på om spader dam finns bland korten. Resultatet av sökningen ska vara att en variabel pos innehåller spader dams position i vektorn (-1 om
spader dam ej finns i vektorn).
c) Ett nytt kort med valören r och färgen s ska sättas in i listan. Korten i listan ska
vara ordnade i stigande valör. Skriv satser som ser till att detta nya kort skapas och
hamnar på sin rätta plats i listan med hänsyn till dess valör.
2.
I följande klasser beskrivs en telefonkatalog i vilket man kan lagra namn och telefonnummer för ett antal personer:
Person
/** Skapar en person med namnet name och telefonnumret phoneNbr. */
Person(String name, String phoneNbr);
/** Tar reda på personens namn. */
String getName();
/** Tar reda på personens telefonnummer. */
String getPhoneNbr();
Övning 9 – ArrayList
PhoneDirectory
/** Skapar en tom telefonkatalog. */
PhoneDirectory();
27
/** Lägger in en person med namnet name och telefonnumret nbr (om
personen inte redan finns i katalogen). Om personen lagts in ska
true returneras, annars false. */
boolean insert(String name, String nbr);
/** Tar bort personen med namnet name ur registret. */
void delete(String name);
/** Tar reda på telefonnumret till personen med namnet name. Om
personen inte finns i katalogen returneras null. */
String findNbr(String name);
/** Returnerar en lista med namnen på de personer vars namn börjar
med strängen s. */
ArrayList<String> startsWith(String s);
/** Returnerar en sträng som representerar telefonkatalogen.
Strängen innehåller personens namn och telefonnummer med
radslutstecken mellan de olika personernas uppgifter.
Om telefonkatalogen är tom ska en tom sträng returneras. */
String toString();
Implementera klassen PhoneDirectory.
Ledning: Det finns fler metoder i klassen String än vad som behandlas i läroboken. (Se
dokumentationen av Javas standardklasser på nätet.) I den här uppgiften behöver du
använda metoden:
boolean startsWith(String prefix);
som returnerar true om strängen startar med tecknen i prefix.
Övning 10 – arv
28
Övning 10 – arv
Läs i läroboken: avsnitt 9.1-9.8
1.
Personer, studenter och lärare har ordnats i följande klasshierarki:
Person
String name
Person(String name)
Student
String program
Student(String name,
String program)
Teacher
String department
Teacher(String name,
String department)
a) Implementera klasserna Person och Student.
b) Följande tilldelningssatser är självklart korrekta:
Person p = new Person("Nils Nilsson");
Student s = new Student("Bo Ek", "D");
Teacher t = new Teacher("Eva Alm", "CS");
Åskådliggör i en bild hur objekten ser ut.
c) Satserna i föregående uppgift har utförts. Vilka av följande satser är korrekta, åtminstone så långt kompilatorn kan avgöra det?
p
s
s
s
=
=
=
=
s;
p;
t;
(Student) p;
d) Satsen s = (Student) p; kan ge ett fel under exekvering. När inträffar felet? Vad
kallas felet?
e) Alla klasser har en operation toString() som ska ge en ”läsbar representation”
av objektet (läroboken avsnitt 11.6). I klasserna Person och Student har metoden
implementerats enligt följande:
public class Person {
public String toString() {
return name;
}
}
public class Student extends Person {
public String toString() {
return super.toString() + ", " + program;
}
}
Vad betyder super.toString()? Vilken utskrift får man av nedanstående satser?
Person p = new Person("Nils Nilsson");
Student s = new Student("Bo Ek", "D");
System.out.println(p.toString());
System.out.println(s.toString());
p = s;
System.out.println(p.toString());
Övning 10 – arv
2.
29
En algoritm som man ibland har nytta av är ”partitionering”, vilket innebär att man delar
upp element i två grupper: de element som uppfyller ett villkor och de element som inte
uppfyller villkoret. I fortsättningen förutsätter vi att elementen är heltal som är lagrade
i vektorer och att partitioneringen innebär att elementen flyttas till början eller slutet av
vektorn. Exempel: partitionering av vektorn {1, 2, 3, 4, 5, 6} med villkoret ”jämnt tal” ska
medföra att vektorn blir {2, 4, 6, 1, 5, 3} (ordningen mellan de tre första talen är godtycklig,
liksom mellan de tre sista).
När man implementerar algoritmen i Java vill man kunna ”plugga in” olika sorters
partitioneringsvillkor i algoritmen. Ett sätt att göra det är att utnyttja följande klass:2
public abstract class Condition {
/** Returnerar true om x uppfyller villkoret, false annars */
public abstract boolean evaluate(int x);
}
För varje specifikt villkor skriver man sedan en subklass till Condition. Till exempel ser
en klass för villkoret ”jämnt tal” ut så här:
public class EvenCondition extends Condition {
public boolean evaluate(int x) {
return x % 2 == 0;
}
}
Nu kan man skicka med ett EvenCondition-objekt till algoritmen för att partitionera enligt
villkoret jämnt tal och objekt av andra subklasser för att partitionera enligt andra villkor.
Implementera partitioneringsalgoritmen i en metod med följande rubrik:
/** Flyttar om talen i vektorn v så att de tal som uppfyller villkoret
cond hamnar före de tal som inte uppfyller villkoret */
public static void partition(int[] v, Condition cond);
Observera att man bara behöver gå igenom vektorn en gång.
2
Detta kan lösas ännu elegantare med ett gränssnitt, ett interface. Mer om detta i fördjupningskursen.
Övning 11 – sortering, objekt
30
Övning 11 – sortering, objekt
Läs i läroboken: avsnitt 8.9, 3.7
Sortering
1.
(Detta är uppgift 8.7 i boken, omformulerad så att vektorn som ska sorteras är parameter.)
”Insättningssortering” är en bra sorteringsmetod om antalet tal som ska sorteras inte är
för stort. I metoden går man igenom talen i tur och ordning och sorterar in varje tal på
sin rätta plats bland de redan sorterade talen.
Exempel, där talföljden består av 7 tal:
13
4
7
5
27
12
2
När man har klarat av de tre första talen och ska sortera in det fjärde talet ser det ut så
här:
4
7
13
5
27
12
2
När nu 5-an ska sorteras in ska man dels finna talets rätta plats (till höger om 4-an), dels
flytta de tal som ligger till höger om denna plats (talet 7 och talet 13) ett steg åt höger. När
detta är klart kan man lägga in 5-an på den lediga platsen.
Implementera en metod som sorterar en vektor med insättningssortering. Vektorn och
antalet tal i vektorn som ska sorteras ska vara parametrar. Tänk på att metoden också
ska klara fallen när det tal som ska sorteras in är mindre än eller större än alla de redan
sorterade talen.
Objekt
2.
Betrakta följande klass:
public class A {
private int value;
public A(int value) {
this.value = value;
}
public void set(int newValue) {
value = newValue;
}
public void print() {
System.out.println("value = " + value);
}
}
Vilken utskrift fås när nedanstående main-metod exekveras? Rita aktiveringsposter, förklara resultatet.
public class Test {
public static void use(A pa) {
pa.print();
Övning 11 – sortering, objekt
pa.set(10);
pa.print();
}
public static void main(String[] args) {
A a = new A(5);
a.print();
use(a);
a.print();
}
}
3.
Vilken utskrift fås av satserna i uppgift 2 om metoden use har följande definition:
public void use(A pa) {
pa.print();
pa = new A(10);
pa.print();
}
31
33
2
Laborationer – anvisningar
1. Obligatorium. Laborationerna är obligatoriska och tillämpar den teori som behandlas under kursen, samt ger viktig träning på att skriva program. Observera att samtliga laborationer
och inlämningsuppgift måste vara godkända innan du får tentera!
2. Laborationstyper. Det finns två typer av laborationer: individuella och grupplabbar.
a) De individuella laborationerna ska lösas med självständigt enskilt arbete. Det är tillåtet att diskutera laborationerna och dess lösningar med kurskamraterna, men var och
en måste skriva sin egen lösning.
b) Grupplabbarna löses i samarbetsgrupper.
i. Diskutera i din samarbetsgrupp hur ni vill dela upp ansvaret och arbetet för olika
delar av koden. Det är lämpligt att varje klass har en huvudansvarig. Flera kan
hjälpas åt med samma klass, t.ex. genom att implementera olika metoder.
ii. När ni redovisar er lösning ska ni också kunna redogöra för hur ni delat upp
ansvar och arbete mellan er. Var och en redovisar sina delar.
iii. Grupplaborationer görs i huvudsak som hemuppgift. Salstiden används primärt
för redovisning.
3. Förberedelser. Till varje laboration finns förberedelser som du ska göra före laborationen.
Ta hjälp av en kamrat, en handledare, eller kursansvarig om det dyker upp några frågor
när du förbereder laborationen. Utnyttja resurstiderna! Innan varje laboration skall du ha:
a) studerat läroboken enligt läsanvisningarna,
b) läst igenom hela laborationen noggrant och gärna påbörjat uppgifterna,
c) löst förberedelseuppgifterna. I dessa uppgifter ska du i förekommande fall skriva
delar av de program som ingår i laborationen. Det krävs inte att allt du skrivit är helt
korrekt, men du måste ha gjort ett rimligt försök. Ta hjälp om du får problem med
uppgifterna, men låt inte någon annan lösa uppgiften åt dig.
Vi förutsätter att du har förberett dig enligt ovan innan du kommer till laborationen —
det är nödvändigt för att labhandledaren ska kunna hjälpa dig på bästa sätt. Om du har
förberett dig väl bör du hinna med alla uppgifterna under laborationen. Om du inte gör det
så får du göra de resterande uppgifterna på egen hand och redovisa dem vid påföljande
laborationstillfälle eller resurstillfälle (och förbereda dig mera till nästa laboration).
4. Sjukanmälan. Om du är sjuk vid något laborationstillfälle så måste du anmäla detta till
kursansvarig ([email protected]) före laborationen. Om du uteblir utan att ha anmält
sjukdom så får du inte göra uppgiften förrän kursen går nästa gång, dvs nästa år, och
då får du inte något slutbetyg i kursen i år. Om du varit sjuk bör du göra uppgiften på
egen hand och redovisa den vid ett senare tillfälle. Har du varit sjuk och behöver hjälp
för att lösa laborationen så gå till något av de resurstillfällen som finns tillgängliga för det
program du läser. Det kommer också att anordnas en ”uppsamlingslaboration” i slutet av
varje termin.
Laboration 1 – Quiz
34
Laboration 1 – Quiz
Mål: Under denna laboration lär du dig skriva och ändra i färdiga program som innehåller
variabler, tilldelning, alternativ och repetitioner. Du skriver programkoden i en valfri texteditor
och kompilerar i ett terminalfönster. Du ändrar i program som läser data från tangentbordet och
från filer.
Förberedelseuppgifter
• Läs i läroboken: avsnitt 1, 3.2 (samt 7.8 för mer om Scanner)
• Gör övning 1.
• Skapa en textfil med namnet quiz.txt som innehåller frågor på detta format:
4
Vilket är det vanligaste programspråket enligt Tiobe Index Juni 2015?
C
C++
Java
Scala
Python
3
Vem skapade den första kompilatorn?
Ada Lovelace
Linus Torvalds
Torgil Ekman
Grace Hopper
Adele Goldberg
4
Vem skrev det första datorprogrammet?
Ada Lovelace
Linus Torvalds
Grace Hopper
Barbara Liskov
Adele Goldberg
1
Vem uppfann Java?
Barbara Liskov
Martin Odersky
Adele Goldberg
Bjarne Stroustrup
James Gosling
5
Krav på textfilens dataformat:
– Första raden ska innehålla ett heltal som anger hur många frågor det finns.
– Därefter ska det finnas angivet antal frågor, var och en med detta innehåll i denna
ordning:
∗ En textrad med en fråga.
∗ Fem textrader med svarsalternativ.
∗ En rad med ett heltal som anger vilket alternativ som är korrekt.
Hitta gärna på andra frågor. Frågorna ovan finns här:
http://fileadmin.cs.lth.se/cs/Education/EDA016/exercises/quiz.txt
Laboration 1 – Quiz
35
Uppgifter
1.
Skriv nedanstående program i valfri texteditor och spara filen i lämplig katalog under
namnet GuessNumber.java. Försök medan du skriver in koden att lista ut vad som kommer att hända när programmet exekveras. Du behöver inte skriva in kommentarerna efter
// och du behöver inte förstå alla detaljer.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.Scanner;
import java.util.Random;
// Gör Scanner-klassen tillgänglig
// Gör Random-klassen tillgänglig
public class GuessNumber {
public static void main(String[] args){
Scanner scan = new Scanner(System.in); // Skapa ett Scanner-objekt
Random rnd = new Random();
// Skapa ett Random-objekt
int secret = rnd.nextInt(100) + 1;
// Dra slumptal 1 till 100
boolean found = false;
while (!found) {
System.out.print("Gissa ett tal mellan 1 och 100: ");
int guess = scan.nextInt();
// Läs heltal från tangentbordet
if (guess > secret) {
System.out.println("För stort :(");
} else if (guess < secret) {
System.out.println("För litet :(");
} else {
found = true;
}
}
System.out.println("Du gissade rätt :)");
}
}
2.
Kompilera ditt program i ett terminalfönster med kommandot javac GuessNumber.java
och kontrollera med ls att en .class-fil skapats. Om det blir kompileringsfel för att
du stavat fel eller glömt något tecken, rätta felet och kompilera om. När kompileringen
lyckats, kör ditt program med kommandot java GuessNumber och försök gissa talet med
så få gissningar som möjligt.
3.
Lägg till en variabel som håller reda på hur många gånger användaren gissar och skriv ut
totala antalet gissningar efter att användaren gissat rätt.
4.
Inför fel i programmet och studera kompilatorns felmeddelande. Vad blir felmeddelandet
(a) om du glömmer deklarera en variabel innan den används, (b) om du adderar 1.0
istället för 1 på rad 8. Tänk ut och prova några fler syntaxfel. Försök förklara begreppen
som kompilatorn använder i felutskrifterna.
5.
Inför ett logiskt fel i programmet genom att ändra ett av vilkoren i den nästlade if-satsen,
så att programmet kompilerar, men gör fel när det kör. Vad händer under exekveringen
av programmet?
6.
Vad händer om du tar bort ! i while-satsen och även ger variabeln found startvärdet true?
Byt namn på den booleska variabeln found till något som passar den nya logiken bättre
(till exempel notFound 3 ), och tilldela den false istället när användaren gissat rätt.
3
Namnet continue hade också passat bra, men det råkar vara ett reserverat ord i Java och får inte användas som
variabelnamn.
36
Laboration 1 – Quiz
7.
Skriv nedan program i valfri texteditor och spara filen i lämplig katalog under namnet
Quiz.java och försök medan du skriver in koden att lista ut vad som kommer att hända när programmet exekveras. Du behöver inte förstå alla detaljer – dessa kommer att
förklaras under kursens gång.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class Quiz {
public static void main(String[] args) throws FileNotFoundException {
Scanner fileScanner = new Scanner(new File("quiz.txt"));
Scanner inScanner = new Scanner(System.in);
int nbrOfQuestions = fileScanner.nextInt();
int nbrOfAlternatives = 5;
for (int i = 1; i <= nbrOfQuestions; i++) {
fileScanner.nextLine(); // hoppa över radbrytning
String question = fileScanner.nextLine();
System.out.println(question);
for (int j = 1; j <= nbrOfAlternatives; j++) {
String alternative = fileScanner.nextLine();
System.out.println(j + ": " + alternative);
}
System.out.print("Välj 1-" + nbrOfAlternatives + ": ");
int correct = fileScanner.nextInt();
int answer = inScanner.nextInt();
if (correct == answer) {
System.out.println("RÄTT!");
} else {
System.out.println("FEEEEEL!");
}
}
fileScanner.close();
}
}
8.
Kompilera programmet med kommandot javac Quiz.java. Kör det med kommandot
java Quiz. Se till att filen quiz.txt från labbförberedelserna finns i samma katalog som
programmet.
9.
Vad händer om du svarar med ett heltal större än 5?
10.
Vad händer om du svarar med bokstäver, t.ex. ETT?
11.
Vad händer om du svarar med ett decimaltal, t.ex. 1.0 eller 1,0?
12.
Inför en variabel som räknar antalet rätt svar och lägg in en utskrift på slutet som visar
totala antalet rätt svar.
Extrauppgifter
13.
Lägg till en utskrift av andelen rätt svar i procent så här långt, efter varje gång användaren
svarat.
14.
Byt ut == mot = i if-satsen och studera kompilatorns felmeddelande. Försök förklara varför
kompilatorn får problem med typerna.
Laboration 1 – Quiz
Checklista
Exempel på vad du ska kunna efter laborationen:
• Skriva och editera programkod i main i en texteditor.
• Kompilera och köra program i ett terminalfönster.
• Förstå skillnaden mellan kompileringsfel och exekveringsfel.
• Tolka några felmeddelanden från kompilatorn.
• Åtgärda enkla syntaktiska och logiska fel.
• Tolka och ändra i enkel kod som innehåller alternativ och repetitioner.
• Deklarera variabler och använda heltalsvariabler som räknare.
37
Laboration 2 – Eclipse
38
Laboration 2 – Eclipse
Mål: Du lär dig att editera, kompilera och exekvera enkla Java-program med hjälp av programutvecklingsverktyget Eclipse. Du ska också prova att använda debuggern i Eclipse .
Förberedelseuppgifter
• Läs igenom kapitel 1 i läroboken.
• Läs igenom anvisningarna om Eclipse (finns i kurskompendiet).
Uppgifter
1.
Logga in på datorn.
2.
Under laborationerna kommer du att använda en hel del färdigskrivna eller nästan färdigskrivna program. Nu ska du skapa ett Eclipse-arbetsområde (workspace) som innehåller
alla dessa filer.
1. Öppna ett terminalfönster.
2. Skriv följande kommandon:
wget http://cs.lth.se/eda016/ws
unzip ws
Det första kommandot laddar ner en komprimerad zip-fil med ditt workspace. Det
andra kommandot packar upp filens innehåll.
3. Nu har du fått en katalog ptd-workspace i din hemkatalog. Katalogen innehåller
underkatalogerna cs_pt, Lab1, Lab2, Lab3, . . . Kontrollera innehållet i ptd-workspace
med följande kommando:
ls ptd-workspace
I ptd-workspace finns också en katalog .metadata. När du senare startar Eclipse skapas i din hemkatalog en katalog .eclipse. Dessa kataloger syns inte när du gör ls,
eftersom deras namn inleds med punkt. Rör inte dessa kataloger — de används av
Eclipse för att spara viktig information, och om de inte finns eller har fel innehåll så
går det inte att starta Eclipse.
4. Den nedladdade filen behövs inte mera. Tag bort den med följande kommando:
rm ws
3.
Nu ska du starta Eclipse. Ge följande kommando:
eclipse &
Efter en stund får du en dialogruta där Eclipse frågar efter vilket arbetsområde som du
vill använda. Ändra förslaget workspace till ptd-workspace.
&-tecknet betyder att Eclipse ska startas ”i bakgrunden”, alltså som ett fristående program. Det medför att man inte låser terminalfönstret utan kan arbeta med andra saker i
det samtidigt som Eclipse kör. Alternativt kan du starta Eclipse genom att välja Eclipse
(den senaste versionen) från Gnome-menyn Applications > Programming.
I Eclipse-fönstret finns i projektvyn (”Package Explorer”, längst till vänster) projekten
cs_pt, Lab2, Lab3, etc. – det är de projektkataloger som finns i ptd-workspace. Filerna
med namn som börjar på lab innehåller färdigskrivna program som utnyttjas under laborationerna. cs_pt innehåller en biblioteksfil (.jar-fil) med klasser som utnyttjas. I samma
Laboration 2 – Eclipse
39
projekt finns källkoden (.java-filerna) till dessa klasser – de behövs inte för laborationerna,
men en del brukar vara intresserade av att titta på dem.
4.
Du ska nu ladda in filen Calculator.java i en editor så att du kan ändra den. Man måste
klicka en hel del för att öppna en fil:
a) Öppna projektet Lab2 genom att klicka på plustecknet (eller pilen) bredvid projektet.
b) Öppna katalogen src genom att klicka på plustecknet.
c) Öppna paketet (default package) genom att klicka på plustecknet.
d) Öppna filen Calculator.java genom att dubbelklicka på filnamnet. Filen öppnas i en
editorflik.
5.
Kör programmet: markera Calculator.java i projektvyn, högerklicka och välj Run As >
Java Application. I konsolfönstret skrivs texten Skriv två tal. Klicka i konsolfönstret,
skriv två tal och tryck på return. Observera: när man skriver reella tal ska man vid
inläsning använda decimalkomma. När man skriver reella tal i programkod använder
man decimalpunkt.
Man kan köra det senaste programmet en gång till genom att klicka på Run-ikonen i
verktygsraden.
6.
Ändra main-metoden i klassen Calculator så att fyra rader skrivs ut: talens summa,
skillnad, produkt och kvot. Exempel på utskrift när talen 24 och 10 har lästs in:
Summan av talen är 34.0
Skillnaden mellan talen är 14.0
Produkten av talen är 240.0
Kvoten mellan talen är 2.4
Du ska alltså efter utskriften av summan lägga in rader där talens skillnad, produkt och
kvot beräknas och skrivs ut. Subtraktion anger man med tecknet -, multiplikation med *,
division med /.
Under tiden du skriver så kommer du att märka att Eclipse hela tiden kontrollerar så
att allt du skrivit är korrekt. Fel markeras med kryss till vänster om raden. Om du håller
musmarkören över ett kryss så visas en förklaring av felet. Om man sparar en fil som
innehåller fel så visas felmeddelandena också i Problem-fliken längst ner.
7.
Spara filen. Filen kompileras automatiskt när den sparas.
När .java-filen kompileras skapas en ny fil med samma namn som klassen men med
tillägget .class. Denna fil innehåller programmet översatt till byte-kod och används när
programmet exekveras.
Man ser inte .class-filerna inuti Eclipse, men de lagras i arbetsområdet precis som
.java-filerna. .java-filerna finns i en katalog src under respektive projektkatalog, .classfilerna finns i en katalog bin.
8.
Kör programmet och kontrollera att utskriften är korrekt. Rätta programmet om den inte
är det.
9.
Du ska nu använda Eclipse debugger för att följa exekveringen av programmet. Normalt
använder man debuggern för att hitta fel i program, men här är avsikten bara att du ska
se hur programmet exekverar rad för rad och att du ska bekanta dig med debuggerkommandona.
Laboration 2 – Eclipse
40
• Sätt en brytpunkt på den första raden i main-metoden. (Läs avsnittet ”Hitta fel i
program” i Eclipse-handledningen om du inte vet hur man gör.) Kör programmet
under debuggern (Debug As > Java Application).
• Svara ja på frågan om du vill byta till Debug-perspektivet.
• Klicka på Step Over-ikonen några gånger. Notera att den rad i programmet som
ska exekveras markeras i editorfönstret och att variabler som deklareras dyker upp
i variabelvyn till höger. Variablernas aktuella värden visas i ett av eclipse-fönsterna,
se till att du ser detta och kan identifiera hur variablerna får sina värden under
programmets exekvering.
• Sätt en brytpunkt på raden där du skriver kvoten mellan talen.
• Klicka på Resume-ikonen för att köra programmet fram till brytpunkten.
• Klicka på Resume igen för att köra programmet till slut.
Byt tillbaka till Java-perspektivet genom att klicka på knappen Java längst till höger i
verktygsfältet.
10.
Följande program använder den färdiga klassen SimpleWindow:
import se.lth.cs.pt.window.SimpleWindow;
public class SimpleWindowExample {
public static void main(String[] args) {
SimpleWindow w = new SimpleWindow(500, 500, "Drawing Window");
w.moveTo(100, 100);
w.lineTo(150, 100);
}
}
Specifikationen av klassen SimpleWindow finns i appendix C i läroboken, eller online via
länk från kurshemsidan. Förklaring av de viktigaste delarna av programmet:
• På den första raden importeras den färdiga klassen SimpleWindow.
• Raderna som börjar med public och de två sista raderna med } är till för att uppfylla
Javas krav på hur ett program ska se ut.
• På nästa rad deklareras en referensvariabel med namnet w och typen SimpleWindow.
Därefter skapas ett SimpleWindow-objekt med storleken 500 × 500 pixlar och med
titeln ”Drawing Window”. Referensvariabeln w tilldelas det nya fönsterobjektet.
• Sedan flyttas fönstrets ”penna” till punkten 100, 100.
• Slutligen ritas en linje.
Programmet finns inte i arbetsområdet, utan du ska skriva det från början. Skapa en fil
SimpleWindowExample.java:
a) Markera projektet Lab2 i projektvyn,
b) Klicka på New Java Class-ikonen i verktygsraden.
c) Skriv namnet på klassen (SimpleWindowExample).
d) Klicka på Finish.
Dubbelklicka på SimpleWindowExample.java för att öppna filen i editorn (om detta inte redan gjorts automatiskt). Som du ser har Eclipse redan fyllt i klassnamnet och de
parenteser som alltid ska finnas. Komplettera klassen med main-metoden som visas ovan.
Spara filen, rätta eventuella fel och spara igen. Provkör programmet.
Laboration 2 – Eclipse
41
11.
Ändra programmet genom att välja bland metoderna i klassen SimpleWindow. Till exempel
kan du rita en kvadrat, ändra färg och linjebredd på pennan, rita fler linjer, skriva text,
etc.
12.
Öppna filen LineDrawing.java. I filen finns följande kod, komplettera programmet så att
det fungerar. Raderna med kommentarer ska ersättas med riktig programkod.
public class LineDrawing {
public static void main(String[] args) {
SimpleWindow w = new SimpleWindow(500, 500, "LineDrawing");
w.moveTo(0, 0);
while (true) {
// vänta tills användaren klickar på en musknapp
// rita en linje till den punkt där användaren klickade
}
}
}
Ledtråd: SimpleWindow innehåller också operationer för att ta hand om musklick. Dessa operationer har följande beskrivning:
/** Väntar tills användaren har klickat på en musknapp */
void waitForMouseClick();
/** Tar reda på x-koordinaten för musens position
vid senaste musklick */
int getMouseX();
/** Tar reda på y-koordinaten för musens position
vid senaste musklick */
int getMouseY();
Fundera ut vad som händer i programmet. while (true) betyder att repetitionen ska
fortsätta ”i oändlighet”. Man avbryter programmet genom att välja Quit i File-menyn i
SimpleWindow-fönstret.
Spara filen, rätta eventuella fel och spara då igen. Provkör programmet.
Checklista
Exempel på vad du ska kunna efter laborationen:
• Starta Eclipse, öppna önskat arbetsområde (workspace) och öppna önskat projekt.
• Skapa .java-filer och skriva in enkla Java-program. (Med enkla program menas här ett
program på några rader som till exempel Calculator.)
• Kunna använda operationen System.out.println och förstå vad som händer när den används.
• Förstå vad det innebär att läsa in tal från tangentbordet.
• Spara .java-filer (kompilering sker då automatiskt).
• Exekvera program.
• Använda debuggern:
– Sätta och ta bort brytpunkt.
Laboration 2 – Eclipse
42
– Starta debuggern.
– Köra fram till en brytpunkt med Resume.
– Exekvera stegvis med Step over (och senare i kursen med Step Into).
– Byta mellan debug-perspektivet och Java-perspektivet.
Laboration 3 – använda färdigskrivna klasser, kvadrat
43
Laboration 3 – använda färdigskrivna klasser,
kvadrat
Mål: Du ska lära dig att läsa specifikationer av klasser och att utnyttja färdigskrivna klasser för
att lösa enkla uppgifter. Du ska också lära dig lite mera om Eclipse.
Förberedelser
•
•
•
•
•
Studera avsnitt 2 och 3.1-3.2 i läroboken.
Läs avsnittet Bakgrund.
Lös uppgift 2.2 och 2.5 i läroboken.
Läs 7.3, 7.5 och 7.6 för att repetera loopar och if-satser.
Tänk igenom följande frågor och se till att du kan svara på dem:
1.
2.
3.
4.
5.
Vad är en main-metod? Hur ser en klass med en main-metoden ut i stora drag?
Vad är referensvariabler och vad har man dem till?
Hur skapar man objekt?
Hur utför man en operation på ett objekt?
Vad använder man parametrar till?
Bakgrund
En klass Square som beskriver kvadrater har nedanstående specifikation.
/** Skapar en kvadrat med övre, vänstra hörnet i x,y
och med sidlängden side. */
Square(int x, int y, int side);
/** Ritar kvadraten i fönstret w. */
void draw(SimpleWindow w);
/** Raderar bilden av kvadraten i fönstret w. */
void erase(SimpleWindow w);
/** Flyttar kvadraten avståndet dx i x-led, dy i y-led. */
void move(int dx, int dy);
/** Tar reda på x-koordinaten för kvadratens läge. */
int getX();
/** Tar reda på y-koordinaten för kvadratens läge. */
int getY();
/** Tar reda på kvadratens area. */
int getArea();
Användning av klassen Square
I nedanstående program skapas först ett ritfönster. Därefter skapas en kvadrat som placeras mitt
i fönstret och ritas upp.
import se.lth.cs.pt.window.SimpleWindow;
import se.lth.cs.pt.square.Square;
public class DrawSquare {
Laboration 3 – använda färdigskrivna klasser, kvadrat
44
public static void main(String[] args) {
SimpleWindow w = new SimpleWindow(600, 600, "DrawSquare");
Square sq = new Square(250, 250, 100);
sq.draw(w);
}
}
• På rad 1 och rad 2 importeras de klasser som behövs, i detta fall: klassen SimpleWindow och
klassen Square. De klasserna finns inte i Javas standardbibliotek utan har skrivits speciellt
för kurserna i programmeringsteknik. Klasserna finns i ”paket” vars namn börjar med
se.lth.cs; det betyder att de är utvecklade vid institutionen för datavetenskap vid LTH,
som använder domännamnet cs.lth.se.
• På rad 5 skapas ett SimpleWindow-objekt. Refererensvariabeln w refererar till detta objekt.
• Därefter skapas ett Square-objekt som referensvariabeln sq refererar till.
• Slutligen ritas kvadraten sq.
Notera parametrarna som man använder när man skapar objekten. I new-uttrycket som skapar
kvadratobjektet står det till exempel (250, 250, 100). Det betyder att kvadraten ska ha läget
250, 250 och sidlängden 100. Läget och sidlängden kan man ändra senare i programmet, med
sq.move(dx,dy) och sq.setSide(newSide).
Lägg också märke till att referensvariablerna w och sq har olika typer. En referensvariabels
typ avgör vilka slags objekt variabeln får referera till. w får referera till SimpleWindow-objekt och
sq får referera till Square-objekt.
Användning av klassen SimpleWindow
Bläddra fram källkoden till klassen Square. Du hittar klassen i Eclipse-projektet cs_pt under
katalogen src och i paketet se.lth.cs.pt.square. Titta på källkoden och försök förstå vad som
händer. Några av operationerna i SimpleWindow (operationerna för att flytta pennan och för att
rita linjer) utnyttjas i operationen draw i klassen Square.
SimpleWindow innehåller också operationer, som inte används i Square, t.ex. för att ta hand
om musklick. Dessa operationer har följande beskrivning:
/** Väntar tills användaren har klickat på en musknapp. */
void waitForMouseClick();
/** Tar reda på x-koordinaten för musens position vid senaste musklick. */
int getMouseX();
/** Tar reda på y-koordinaten för musens position vid senaste musklick. */
int getMouseY();
När exekveringen av ett program kommer fram till waitForMouseClick så ”stannar” programmet och fortsätter inte förrän man klickat med musen någonstans i programmets fönster. Ett
program där man skapar ett fönster och skriver ut koordinaterna för varje punkt som användaren klickar på har följande utseende:
import se.lth.cs.pt.window.SimpleWindow;
public class PrintClicks {
public static void main(String[] args) {
SimpleWindow w = new SimpleWindow(600, 600, "PrintClicks");
Laboration 3 – använda färdigskrivna klasser, kvadrat
45
while (true) {
w.waitForMouseClick();
w.moveTo(w.getMouseX(), w.getMouseY());
w.writeText("x = " + w.getMouseX() + ", " + "y = " + w.getMouseY());
}
}
}
while (true) betyder att repetitionen ska fortsätta ”i oändlighet”. Man avbryter programmet
genom att välja Quit i File-menyn i SimpleWindow-fönstret.
Också i nedanstående program ska användaren klicka på olika ställen i fönstret. Nu är det
inte koordinaterna för punkten som användaren klickar på som skrivs ut, utan i stället avståndet mellan punkten och den förra punkten som användaren klickade på. Vi sparar hela tiden
koordinaterna för den förra punkten (variablerna oldX och oldY).
Gå igenom ett exempel ”för hand” och övertyga dig om att du förstår hur programmet
fungerar.
import se.lth.cs.pt.window.SimpleWindow;
public class PrintClickDists {
public static void main(String[] args) {
SimpleWindow w = new SimpleWindow(600, 600, "PrintClickDists");
int oldX = 0; // x-koordinaten för "förra punkten"
int oldY = 0; // y-koordinaten
while (true) {
w.waitForMouseClick();
int x = w.getMouseX();
int y = w.getMouseY();
w.moveTo(x, y);
int xDist = x - oldX;
int yDist = y - oldY;
w.writeText("Avstånd: " + Math.sqrt(xDist * xDist + yDist * yDist));
oldX = x;
oldY = y;
}
}
}
Datorarbete
1.
Logga in på datorn, starta Eclipse, öppna projektet lab3. (Logga in, starta Eclipse och
öppna ett projekt ska du alltid göra, så det skriver vi inte ut i fortsättningen.)
2.
Öppna en webbläsare och gå till kursens hemsida, cs.lth.se/eda016 . Under Dokumentation finns en länk till dokumentationen av de färdigskrivna klasser som används under
laborationerna, alltså klasserna i paketet se.lth.cs.pt. Leta upp och titta igenom specifikationen av klassen Square. Det är samma specifikation som finns under Bakgrund, fast
i något annorlunda form.
3.
Klassen DrawSquare finns i filen DrawSquare.java. Öppna filen, kör programmet.
4.
Kopiera filen DrawSquare.java till en ny fil med namnet DrawThreeSquares.java. Enklast
är att göra så här:
1. Markera filen i projektvyn, högerklicka, välj Copy.
2. Högerklicka på (default package), välj Paste, skriv det nya namnet på filen.
Laboration 3 – använda färdigskrivna klasser, kvadrat
46
Notera att Eclipse ändrar klassnamnet i den nya filen till DrawThreeSquares.
Ändra sedan klassen så att kvadraten ritas tre gånger. Mellan uppritningarna ska
kvadraten flyttas. Du ska fortfarande bara skapa ett kvadratobjekt i programmet. Resultatet ska bli en figur med ungefär följande utseende:
Testa programmet, rätta eventuella fel.
5.
Någonstans i ditt program skapar du ett kvadratobjekt med en sats som har ungefär följande utseende: Square sq = new Square(300,300,200). Tag bort denna sats från programmet (eller kommentera bort den). Eclipse kommer att markera flera fel i programmet,
eftersom sq inte är deklarerad och du senare i programmet utnyttjar den variabeln. Läs
och tolka felmeddelandena.
Lägg sedan in satsen Square sq = null i början av programmet. Eclipse kommer inte
att hitta några fel, eftersom programmet nu följer de formella reglerna för Javaprogram.
Exekvera sedan programmet och se vad som inträffar. Studera felmeddelandet så att du
kan tolka det.
Meddelanden om exekveringsfel skrivs i konsolfönstret. Det skrivs också ut en ”stack
trace” för felet: var felet inträffade, vilken metod som anropade metoden där det blev
fel, vilken metod som anropade den metoden, osv. Om man klickar på ett radnummer
öppnas rätt fil i editorn med den raden markerad (under förutsättning att programtexten,
”källkoden”, för den filen är tillgänglig).
Lägg sedan tillbaka den ursprungliga satsen för att skapa kvadratobjektet. Ändra parametern w i det första anropet av draw till null. Kör programmet, studera det felmeddelande som du får.
När man får exekveringsfel kan det vara svårt att hitta orsaken till felet. Här är en
debugger till stor hjälp, i och med att man kan sätta brytpunkter och köra programmet
stegvis.
6.
Kör programmen PrintClicks och PrintClickDists.
7.
Skriv ett program där ett kvadratobjekt skapas och ritas upp i ett ritfönster. När användaren klickar med musen i fönstret ska bilden av kvadraten raderas, kvadraten flyttas
till markörens position och ritas upp på nytt. Titta gärna på den totala specifikationen
av SimpleWindow och Square som finns länkat från kurshemsidan. Vilka metoder som
verkar användbara för detta kan du hitta i dokumentationen?
Välj ett lämpligt namn på klassen. För att skapa en fil för klassen kan du antingen
skapa en tom klass (klicka på New Java Class-ikonen, skriv namnet på klassen, klicka på
Finish) eller kopiera någon av de filer som du redan har.
Testa programmet.
8.
Modifiera programmet i uppgift 7 så att varje flyttning går till så att kvadraten flyttas
stegvis till den nya positionen. Efter varje steg ska kvadraten ritas upp (utan att den
Laboration 3 – använda färdigskrivna klasser, kvadrat
47
gamla bilden raderas). Exempel på kvadratbilder som ritas upp när flyttningen görs i 10
steg:
klickat här
ursprunglig position
Kvadratens slutliga position behöver inte bli exakt den position som man klickat på. Om
man till exempel ska flytta kvadraten 94 pixlar i 10 steg är det acceptabelt att ta 10 steg
med längden 9.
Skriv in och testkör programmet.
9.
Observera att programmet från uppgift 8 ritar många bilder av samma kvadrat och att
alla bilderna kommer att synas i fönstret när programmet är slut. Om man raderar den
”gamla” bilden innan man ritar en ny bild så kommer man att få en ”rörlig”, ”animerad”,
bild. För att man ska se vad som händer måste man då göra en paus mellan uppritning
och radering — det gör man med SimpleWindow-metoden delay, som har en parameter
som anger hur många millisekunder man ska vänta. Exempel:
while (sq.getSide() > 0) {
sq.draw(w);
SimpleWindow.delay(10);
sq.erase(w);
sq.setSide(sq.getSide() - 10);
}
Kodsnutten ovan animerar en ny mindre kvadrat tills dess att kvadratens sida blivit
mindre än eller lika med noll.
Kopiera koden från uppgift 8 till en ny fil AnimatedSquare.java. Lägg in fördröjning
och radering så att kvadratbilden blir ”animerad”.
Anmärkning: i ett ”riktigt” program som visar rörliga bilder åstadkommer man inte animeringen på det här sättet. Denna lösning har bristen att man inte kan göra något annat
under tiden som animeringen pågår, till exempel kan programmet inte reagera på att man
klickar med musen.
10.
I denna uppgift ska du lära dig fler kommandon i Eclipse. Det finns ett otal kommandon,
mer eller mindre avancerade, och många av dem använder man sällan. Två kommandon
som man ofta använder:
• Source-menyn > Format. Korrigerar programlayouten i hela filen. Ser till exempel till
att varje sats skrivs på en rad, att det är blanka runt om operatorer, och så vidare.
Man bör formatera sina program regelbundet, så att de blir läsbara. Observera att
programmet ska vara korrekt formaterat när du visar det för labhandledaren för att
få det godkänt. Notera också kortkommandot som står intill menyalternativet. Prova
att använda kortkommandot också, och lär dig gärna det, då det är snabbare och mer
bekvämt än att klicka.
Laboration 3 – använda färdigskrivna klasser, kvadrat
48
• Refactor-menyn > Rename. Ändrar namn på den variabel eller metod som markerats
(lokalt om det är en lokal variabel, i hela klassen om det är ett attribut, även i andra
klasser om det är en publik metod).
11.
Kontrollera med hjälp av checklistan nedan att du behärskar de olika momenten i laborationen. Diskutera med övningsledaren om någonting är oklart.
Checklista
Exempel på vad du ska kunna efter laborationen:
• Tyda specifikationer av klasser.
• Skriva program som använder färdiga klasser:
– Deklarera referensvariabler och skapa objekt.
– Utföra operationer på objekt.
Laboration 4 – implementera klasser (Square), samt felsökning
49
Laboration 4 – implementera klasser (Square), samt
felsökning
Mål: Du ska träna på att implementera klasser och leta fel.
Förberedelseuppgifter
• Studera kap 3 i läroboken noggrant.
• Läs igenom texten i avsnittet Bakgrund.
Bakgrund
Klassen Point beskriver en punkt. Den har följande specifikation:
/** Skapar en punkt med koordinaterna x,y. */
Point(int x, int y);
/** Tar reda på x-koordinaten. */
int getX();
/** Tar reda på y-koordinaten. */
int getY();
/** Flyttar punkten avståndet dx i x-led, dy i y-led. */
void move(int dx, int dy);
/** Returnerar avståndet mellan denna punkt och punkten p. */
double distanceTo(Point p);
/** Returnerar en teckensträng som representerar punkten. Strängen
innehåller punktens koordinater. Ex: 150 200 */
String toString();
Specifikationen är till för den som ska använda klassen när den är färdig. Här ser man hur man
skapar Point-objekt och vilka metoder man kan anropa på ett sådant.
String är en typ (egentligen en klass) som används för att beskriva en teckensträng, dvs. en
följd av tecken. Se avsnitt 6.7 i läroboken.
Implementeringen av klassen Point ser ut så här:
public class Point {
private int x; // punktens x-koordinat
private int y; // punktens y-koordinat
/** Skapar en punkt med koordinaterna x,y. */
public Point(int x, int y) {
this.x = x;
this.y = y;
}
/** Tar reda på x-koordinaten. */
public int getX() {
return x;
}
/** Tar reda på y-koordinaten. */
public int getY() {
return y;
Laboration 4 – implementera klasser (Square), samt felsökning
50
}
/** Flyttar
public void
x = x +
y = y +
}
punkten avståndet dx i x-led, dy i y-led. */
move(int dx, int dy) {
dx;
dy;
/** Returnerar avståndet mellan denna punkt och punkten p. */
public double distanceTo(Point p) {
return Math.hypot(x - p.x, y - p.y);
}
/** Returnerar en teckensträng som representerar punkten. Strängen
innehåller punktens koordinater. Ex: 150 200 */
public String toString() {
return x + " " + y;
}
}
Observera skillnaden mellan specifikationen (beskrivningen) av klassen och implementeringen
(programkoden).
Implementeringen av klassen Point ska finnas i en egen fil med namnet Point.java. Den
innehåller den fullständiga programkoden för klassen. Här deklareras attribut och här finns de
satser som utförs när en metod anropas.
Studera klassen ordentligt och försäkra dig att du förstår alla detaljer. Tänk igenom följande
frågor och se till att du kan svara på dem:
1.
2.
3.
4.
5.
Vilka attribut finns i klassen Point?
Vad händer inuti konstruktorn?
Vad menas med public och private?
Varför står det void framför vissa metodnamn?
Vad innebär return?
Inuti metoden distanceTo måste man komma åt den andra punktens x- och y-koordinat för
att kunna beräkna avståndet till den. Trots att attributen är privata kan man skriva p.x och p.y
för att nå dessa koordinater. Det beror på att ett attribut som är deklarerat private är privat
för klassen och inte bara för ett speciellt objekt. Men man kan även nå den andra punktens
koordinater på ett annat sätt. Hur?
Felsökning
Fel i datorprogram kan yttra sig på två sätt: som kompileringsfel eller som exekveringsfel. Att
finna och åtgärda sådana fel är en viktig del av programmeringsarbetet.
Kompileringsfel
Vid kompileringsfel markerar Eclipse den eller de felaktiga raderna med kryss till vänster i
editorfönstret. Om man håller musmarkören över ett sådant kryss så visas en förklaring av felet.
Man kan också se en lista med samtliga fel, genom att i menyn välja Window → Show View
→ Problems: då listas felen längst ner i Eclipse-fönstret, där konsollutskrifter annars visas.
Kompileringsfel innebär att programmet inte kan kompileras och därför inte heller exekveras. Kompileringsfelen måste alltså åtgärdas.
Förutom kompileringsfel kan kompilatorn också visa varningar: dessa betyder att programmet visserligen kan kompileras, men ändå innehåller något som verkar konstigt. Exempelvis
Laboration 4 – implementera klasser (Square), samt felsökning
51
varnar kompilatorn om man deklarerar en variabel utan att använda den. Det brukar löna sig
att uppmärksamma varningar – de tyder ofta på att man gjort misstag.
Exekveringsfel
Exekveringsfel är fel som uppstår vid exekvering, det vill säga när man kör programmet. Om
programmet under körning bryter mot Javas regler – exempelvis dividerar ett heltal med noll –
så avbryts programmet och ett felmeddelande skrivs ut. Då skrivs också en anropskedja ut, där
man kan se dels i vilken metod felet uppstod, dels varifrån den metoden i sin tur anropades. I
anropskedjan visas radnummer, och när man klickar på ett sådant radnummer öppnas rätt fil i
Eclipse (under förutsättning att programtexten, källkoden, för filen är tillgänglig).
Till exekveringsfelen räknas även logiska fel. Ett program kan ge felaktigt resultat även om
det följer Javas regler. Om man som programmerare exempelvis skrivit + istället för -, eller
använder fel villkor i en if-sats, kommer programmet ju inte att fungera som förväntat.
Felsökning
Som framgått kan kompilatorn inte avgöra om programmet gör det man själv vill, och även om
man tänkt igenom sitt program visar sig vissa fel först då programmet körs. För att felsöka i
programmet behöver man alltså provköra det.
Felsökning är ett detektivarbete. Genom att pröva olika kombinationer av indata och studera
programmets resultat kan man förstå mer om hur felet yttrar sig. Utifrån denna förståelse får
man idéer om var i programmet felet kan finnas. När man sedan vill undersöka en viss del
av programmet är debuggern till stor hjälp – särskilt möjligheten att sätta brytpunkter, studera
variablernas värden och köra programmet stegvis.
I denna uppgift ska du få bekanta dig med sådant felsökningsarbete.
Datorarbete
Denna laboration har två delar: (1) Du ska utgå från en färdig klass och ändra implementationen, samt (2) du ska träna på felsökning. Filen Point.java används i del 1, medan filerna
BuggySquareDrawer.java och BuggySquare.java används i del 2.
Del 1: Ändra implementation av klassen Square
1.
Först ska vi ordna så att du får en egen Square.java, som du kan ändra i.
• Kopiera filen Square.java från projektet cs_pt till detta projekt (Lab4). Du finner filen
i projektet cs_pt under src/se.lth.cs.pt.square.
• Kopiera även filen DrawThreeSquares.java från Lab3 till Lab4. Filen innehåller en
main-metod som använder klassen Square.
• Din ”nya”, kopierade, DrawThreeSquares.java ska nu använda den version av Square.java som du just kopierat till ditt projekt (Lab4). Detta gör du genom att i filen
DrawThreeSquares.java ta bort raden som importerar klassen Square.
Nu kommer Java söka efter klassen Square i samma katalog som du lagt DrawThreeSquares.java, och eftersom Square nu finns där behöver den alltså inte importeras.
• Kontrollera att inga fel återstår i ditt projekt innan du går vidare till nästa steg.
2.
Ett objekts tillstånd kan representeras på olika sätt och ändras utan att det påverkar specifikationen eller de andra klasser i programmet som utnyttjar klassen. Inuti klassen Square
finns det två heltalsattribut x och y som håller reda på kvadratens position. Man skulle
lika gärna kunna använda en punkt för detta. Byt ut attributen x och y mot ett attribut av
Laboration 4 – implementera klasser (Square), samt felsökning
52
typen Point. Gör de ändringar som krävs i resten av klassen. Kör programmet DrawThreeSquares för att kontrollera att klassen fortfarande fungerar. OBS: Koden i DrawThreeSquares ska inte behöva ändras för att få programmet i att fungera. Klassen Square ska
alltså bara ändras internt, anropen till den ska inte förändras alls.
3.
Lägg till följande metoder i klassen Square:
/** Returnerar true om denna kvadrat innesluter punkten (x,y). */
boolean contains(int x, int y);
/** Returnerar true om denna kvadrat innesluter punkten p. */
boolean contains(Point p);
/** Returnerar true om denna kvadrat innesluter alla punkter i
kvadraten sq. */
boolean contains(Square sq);
Ledning: Börja med den första contains-metoden, därefter den andra. För den tredje
metoden kan det underlätta att rita ett par exempel, och utifrån dessa formulera en lösning
på problemet. Sedan uttrycker du din lösning i Java i contains(Square sq).
4.
Skriv en klass med en main-metod där du skapar och ritar några Square-objekt och på
något sätt använder metoden contains(Square sq) för att testa om metoden är korrekt
eller ej. Tänk på att dina tester ska vara tillräckligt omfattande för att övertyga din labbhandledare om att metoden fungerar.
Kör programmet och kontrollera att allt fungerar som det ska. Använd gärna debuggern för att hitta eventuella fel.
Del 2: Felsökning
5.
I filen BuggySquareDrawer.java finns ett litet program som är tänkt att rita upp kvadrater
i ett SimpleWindow. Programmet använder BuggySquare.java. Användaren får mata in
värden för kvadratens sida och koordinater. När kvadraten ritats i fönstret får användaren
möjlighet att mata in uppgifter om en ny kvadrat. Programmet ska dessutom kontrollera
om den begärda kvadraten får plats inuti fönstret: om kvadraten inte får plats ska den
inte ritas upp.
I main-metoden i BuggySquareDrawer.java har en rad kommenterats bort. Ta bort
kommentartecknen så att main-metoden ser ut så här:
public static void main(String[] args) {
runApplication();
}
Din uppgift är nu att hitta samtliga fel i BuggySquareDrawer.java och BuggySquare.java och ändra så att allt fungerar som förväntat.
Tips: Först måste du åtgärda ett kompileringsfel för att alls kunna köra programmet.
Därefter måste du köra och testa programmet noggrant för att hitta alla fel. Det är svårt
att ange det exakta antalet fel eftersom det beror på hur man räknar, men totalt krävs
ändringar på fyra olika rader i programmet och ett tillägg, i form av en rad som saknas
helt, för att få det att fungera.
Laboration 4 – implementera klasser (Square), samt felsökning
53
En del av utmaningen är att testa tillräckligt mycket för att inse vad som är fel. Detta
är ofta svårare än att hitta det egentliga felet. Du kan inleda din undersökning med att
exempelvis testa fallet sida = 10, x = 100, y = 50.
Redovisning
För att bli godkänd på uppgiften ska du kunna visa upp ett program som fungerar enligt ovan.
Därtill ska du vara beredd att berätta om ditt felsökningsarbete. Fyll därför i information om
varje fel i tabellen nedan (kategorisera dem utifrån beskrivningen av olika slags fel ovan).
Skriv ner dina resultat (exempelvis i den följande tabellen) så att du minns dem till redovisningen.
Fel (vilken klass, vilken rad?)
Beskriv: Vad var problemet?
Typ av fel
Hur upptäcktes felet?
Kontrollera med hjälp av checklistan nedan att du behärskar de olika momenten i laborationen.
Diskutera med övningsledaren om någonting är oklart.
Checklista
Exempel på vad du ska kunna efter laborationen:
• Förstå skillnaden mellan specifikation och implementation.
• Implementera en klass:
Laboration 4 – implementera klasser (Square), samt felsökning
54
– attribut
– konstruktor
– void-metoder och funktioner
• Tyda felutskrifter vid kompileringsfel och rätta felen.
• Tyda programmets resultat vid exekveringsfel och rätta felen.
• Förstå hur logiska uttryck kan användas för att jämföra två objekt.
• Kunna anropa metoder från andra metoder, och kunna använda metoder som returnerar
boolska resultat (typen boolean).
Laboration 5 – implementera klasser, gissa tal
55
Laboration 5 – implementera klasser, gissa tal
Mål: Du ska fortsätta på att träna att implementera klasser. Du ska också få mer träning i att
använda if-, for- och while-satser.
Förberedelseuppgifter
• Repetera kap 2 och 3 i läroboken.
• Läs kapitel 4 i läroboken.
• Läs igenom texten i avsnittet Bakgrund.
Bakgrund
Under den här laborationen ska du skriva ett program för spelet ”Gissa talet”. Spelet går till så
att datorn ”tänker” på ett tal inom ett visst intervall. Användaren gissar på ett tal och får reda
på om det är för litet, för stort eller korrekt. Spelet fortsätter tills användaren gissat rätt tal.
Klassen NumberToGuess beskriver objekt som kan välja ett slumptal i ett visst intervall och
hålla reda på detta.
/** Skapar ett objekt med ett slumpmässigt valt heltal i
intervallet [min, max]. */
NumberToGuess(int min, int max);
/** Tar reda på minsta möjliga värde talet kan ha. */
int getMin();
/** Tar reda på största möjliga värde talet kan ha. */
int getMax();
/** Tar reda på om talet är lika med guess. */
boolean isEqual(int guess);
/** Tar reda på om talet är större än guess. */
boolean isBiggerThan(int guess);
Förutom denna klass ska du skriva en klass med en main-metod som utför själva spelet.
Datorarbete
1.
Öppna projektet Lab5. Där finns en fil NumberToGuess.java med ett ”skelett” (en klass
utan attribut och med tomma metoder) till klassen NumberToGuess.
Implementera klassen NumberToGuess: Tänk ut vilka attribut som behövs och skriv in
dem i klassen. Skriv gärna en kommentar till varje attribut som förklarar vad attributet
betyder. Skriv också konstruktorn och se till att alla attribut får rätt startvärden. Implementera de övriga operationerna.
Ett av attributen ska tilldelas ett slumpmässigt valt värde. Användning av slumptal
beskrivs i läroboken, avsnitt 6.10. För tillfället behöver du bara veta att man måste skapa
ett Random-objekt och att man får ett nytt slumpmässigt heltal i intervallet [0, n) med
funktionen nextInt(n). Skrivsättet [0, n) betyder att 0 ingår i intervallet, n ingår inte i
intervallet. rand.nextInt(100) ger alltså ett slumpmässigt heltal mellan 0 och 99.
2.
I projektet Lab5 finns det ett program TestNumberToGuess som är tänkt att användas
för att testa att metoderna i klassen NumberToGuess ger rätt resultat. Studera gärna pro-
Laboration 5 – implementera klasser, gissa tal
56
grammet om du vill. Kör programmet. Utskrifterna från programmet ger besked om allt
fungerar som det ska eller ej. Rätta eventuella fel och provkör igen.
3.
Skriv programmet som utför en spelomgång där användaren får gissa på ett tal upprepade gånger tills rätt tal gissats. Programmet ska börja med att fråga användaren vilket
min- och max-värde som ska gälla, varpå dessa läses in med Scannern. Därefter ska användarens gissningar läsas in. Efter varje gissning ska användaren få besked om ifall det
gissade talet var för litet, för stort eller korrekt. Sist i programmet ska antal gissningar
som krävdes skrivas ut. Testa programmet.
4.
(Frivillig uppgift) Det finns olika mer eller mindre smarta sätt att gissa talet. I den här uppgiften ska du skriva ett program som ska ta reda på hur många gissningar som krävs om
man använder en optimal strategi. I programmet ska du använda klassen OptimalGuesser
(se nedan). Genom att anropa metoden nbrGuesses ett stort antal gånger och ta reda på
det maximala antal gissningar som krävdes kan man uppskatta antal gissningar som krävs
för ett visst intervall.
/** Beskriver en spelare som gissar tal på ett optimalt sätt. */
OptimalGuesser();
/** Gissar tal på ett optimalt sätt tills rätt tal gissats.
nbr är det objekt som håller reda på det tal som ska gissas.
Returnerar antal gissningar som krävdes. */
int nbrGuesses(NumberToGuess nbr);
Implementera klassen OptimalGuesser. Börja med att fundera ut vilken strategi man ska
använda för att behöva så få gissningar som möjligt. Diskutera med din labhandledare
ifall du är osäker på vilken strategi du ska använda inuti metoden nbrGuesses.
5.
(Frivillig uppgift.) Skriv ett program som anropar metoden nbrGuesses ett stort antal
gånger och tar reda på största antalet gissningar som krävdes.
Ledning: För att beräkna maximala antalet gissningar behöver du en lokal variabel
som håller reda på hittills största värde. Kontrollera efter varje anrop av nbrGuesses om
denna variabel behöver uppdateras.
Att beräkna maximum (eller minimum) är ett vanligt problem (behandlas i läroboken,
avsnitt 7.13). Här är en generell algoritmen för att beräkna maximum:
max = "litet värde";
för alla värden
value = "nästa värde";
if (value > max) {
max = value;
}
Checklista
I den här laborationen har du övat på att
• skriva ett program med flera egna klasser,
• generera slumptal inom ett givet intervall, och
• använda if- och while-villkor för att styra exekveringen av program.
Laboration 6 – implementera och ärva klasser, Turtle och ColorTurtle
57
Laboration 6 – implementera och ärva klasser,
Turtle och ColorTurtle
Mål: Du ska fortsätta att träna på att implementera och använda klasser. Du ska också få mer
träning i att använda Eclipse debugger. Du ska även skapa en klass som ärver en annan klass.
Förberedelseuppgifter
• Repetera kap 3 i läroboken.
• Läs kapitel 9.1 – 9.4.
• Tänk igenom följande frågor och se till att du kan svara på dem:
1. Vad är en specifikation respektive en implementering av en klass?
2. Vad är ett attribut? Var deklareras attributen? När reserveras det plats för dem i
datorns minne och hur länge finns de kvar?
3. När exekveras satserna i konstruktorn? Vad brukar utföras i konstruktorn?
4. Vilka likheter/skillnader finns det mellan attribut, lokala variabler och formella parametrar.
5. Vad menas med public och private?
Bakgrund
I grafiksystemet Turtle graphics kan man rita linjer. Linjerna ritas av en (tänkt) sköldpadda som
går omkring i ett ritfönster. Sköldpaddan har en penna som antingen kan vara lyft (då ritas
ingen linje då sköldpaddan går) eller sänkt (då ritas en linje). Sköldpaddan kan bara gå rakt
framåt, i den riktning som huvudet pekar. När sköldpaddan står stilla kan den vrida sig så att
dess huvud pekar åt olika håll.
En klass Turtle som beskriver en sköldpadda av detta slag har följande specifikation:
/** Skapar en sköldpadda som ritar i ritfönstret w. Från början
befinner sig sköldpaddan i punkten x,y med pennan lyft och
huvudet pekande rakt uppåt i fönstret (i negativ y-riktning). */
Turtle(SimpleWindow w, int x, int y);
/** Sänker pennan. */
void penDown();
/** Lyfter pennan. */
void penUp();
/** Går rakt framåt n pixlar i den riktning som huvudet pekar. */
void forward(int n);
/** Vrider beta grader åt vänster runt pennan. */
void left(int beta);
/** Går till punkten newX,newY utan att rita. Pennans läge (sänkt
eller lyft) och huvudets riktning påverkas inte. */
void jumpTo(int newX, int newY);
/** Återställer huvudriktningen till den ursprungliga. */
void turnNorth();
/** Tar reda på x-koordinaten för sköldpaddans aktuella position. */
int getX();
58
Laboration 6 – implementera och ärva klasser, Turtle och ColorTurtle
/** Tar reda på y-koordinaten för sköldpaddans aktuella position. */
int getY();
/** Tar reda på sköldpaddans riktning, i grader från positiv x-led. */
int getDirection();
Observera att vi här bestämmer vilket fönster som sköldpaddan ska rita i när vi skapar ett
sköldpaddsobjekt. Det kan väl sägas motsvara verkligheten: en sköldpadda ”befinner” sig ju
alltid någonstans.
I följande program ritar en sköldpadda en kvadrat med sidorna parallella med axlarna:
import se.lth.cs.pt.window.SimpleWindow;
public class TurtleDrawSquare {
public static void main(String[] args) {
SimpleWindow w = new SimpleWindow(600, 600, "TurtleDrawSquare");
Turtle t = new Turtle(w, 300, 300);
t.penDown();
for (int i = 0; i < 4; i++) {
t.forward(100);
t.left(90);
}
}
}
I nedanstående variant av programmet har vi gjort två ändringar: 1) längden på sköldpaddans
steg väljs slumpmässigt mellan 0 och 99 pixlar, 2) efter varje steg görs en paus på 100 millisekunder. Den första ändringen medför att figuren som ritas inte blir en kvadrat, den andra ändringen
medför att man ser hur varje linje ritas.
import se.lth.cs.pt.window.SimpleWindow;
import java.util.Random;
public class TurtleDrawRandomFigure {
public static void main(String[] args) {
Random rand = new Random();
SimpleWindow w = new SimpleWindow(600, 600, "TurtleDrawRandomFigure");
Turtle t = new Turtle(w, 300, 300);
turtle.penDown();
for (int i = 0; i < 4; i++) {
t.forward(rand.nextInt(100));
SimpleWindow.delay(100);
t.left(90);
}
}
}
Användning av slumptal beskrivs i läroboken, avsnitt 6.10. För tillfället behöver du bara veta
att man måste skapa ett Random-objekt och att man får ett nytt slumpmässigt heltal i intervallet
[0, n) med funktionen nextInt(n). Skrivsättet [0, n) betyder att 0 ingår i intervallet, n ingår inte
i intervallet. rand.nextInt(100) ger alltså ett slumpmässigt heltal mellan 0 och 99.
När klassen Turtle ska implementeras måste man bestämma vilka attribut som klassen ska
ha. En sköldpadda måste hålla reda på:
• fönstret som den ska rita i: ett attribut SimpleWindow w,
• var i fönstret den befinner sig: en x-koordinat och en y-koordinat. För att minska inverkan
av avrundningsfel i beräkningarna ska x och y vara av typ double,
Laboration 6 – implementera och ärva klasser, Turtle och ColorTurtle
59
• i vilken riktning huvudet pekar. Riktningen kommer alltid att ändras i hela grader. Man
kan själv välja om attributet som anger riktningen ska vara i grader eller i radianer,
• om pennan är lyft eller sänkt. Ett sådant attribut bör ha typen boolean. booleanvariabler
kan bara anta två värden: true eller false. Man testar om en booleanvariabel isPenDown
har värdet true med if (isPenDown) . . . .
När sköldpaddan ska gå rakt fram i den aktuella riktningen ska en linje ritas om pennan är sänkt.
Den aktuella positionen ska också uppdateras. Antag att sköldpaddan i ett visst ögonblick är
vriden vinkeln α i förhållande till positiv x-led. När operationen forward(n) utförs ska pennan
flyttas från punkten ( x, y) till en ny position, som vi kallar ( x1, y1):
x
(x1, y1)
n
α
(x, y)
y
Av figuren framgår att ( x1, y1) ska beräknas enligt:
x1
= x + n cos α
y1
= y − n sin α
I Java utnyttjas standardfunktionerna Math.cos(alpha) och Math.sin(alpha) för att beräkna
cosinus och sinus. Vinkeln alpha ska ges i radianer.
x och y är av typ double. När koordinaterna utnyttjas som parametrar till SimpleWindowmetoderna moveTo och lineTo och när de ska returneras som funktionsresultat i getX och getY
måste de avrundas till heltalsvärden. Det kan till exempel se ut så här:
w.moveTo((int) Math.round(x), (int) Math.round(y));
ColorTurtle
I en av uppgifterna i laborationen ska du använda en klass ColorTurtle som beskriver en
sköldpadda som ritar med en färgad penna. För övrigt fungerar klassen precis likadant som en
”vanlig” Turtle. Man skapar objekt av klassen enligt följande exempel:
ColorTurtle t1 = new ColorTurtle(w, 100, 100, java.awt.Color.RED);
ColorTurtle t2 = new ColorTurtle(w, 100, 100, java.awt.Color.BLUE);
Det är inte särskilt svårt att skriva klassen: den enda skillnaden mot Turtle är att klassen behöver ett attribut där färgen sparas och att man i metoden forward sätter linjefärgen till denna färg med SimpleWindow-operationen setLineColor. Man kan kopiera klassen Turtle till
ColorTurtle och införa dessa tillägg. Detta är dock inte alls bra — om man senare kommer
på att man måste ändra något i Turtle måste man komma ihåg att också ändra samma sak i
ColorTurtle.
Betydligt bättre är att använda arv: man låter ColorTurtle ”ärva” alla egenskaper från
Turtle och skriver bara de tillägg som behövs. Man kallar ColorTurtle för en ”subklass” till
”superklassen” Turtle.
Här visas ett exempel på hur man kan implementera ColorTurtle:
Laboration 6 – implementera och ärva klasser, Turtle och ColorTurtle
60
import se.lth.cs.window.SimpleWindow;
import java.awt.Color;
public class ColorTurtle extends Turtle {
private Color color;
public ColorTurtle(SimpleWindow w, int x, int y, Color color) {
super(w, x, y);
this.color = color;
}
public void forward(int n) {
Color savedColor = w.getLineColor();
w.setLineColor(color);
super.forward(n);
w.setLineColor(savedColor);
}
}
Kommentarer:
• extends, ”utökar”, anger att klassen ColorTurtle ärver alla egenskaper från klassen Turtle.
• Alla objektets attribut måste få startvärden. De attribut som finns i ”Turtle-delen” av
objektet initierar man genom att anropa Turtle-konstruktorn med super(w, x, y).
• I forward sparar man den aktuella linjefärgen, ändrar till sköldpaddans egen färg, anropar
forward i superklassen Turtle och sätter tillbaka linjefärgen.
• För att det ska fungera måste man göra en ändring i Turtle: skyddet på SimpleWindowattributet w ändras från private till protected. Det betyder att subklasserna får använda
attributet.
Datorarbete
1.
I filen Turtle.java i projektet Lab6 finns ett ”skelett” (en klass utan attribut och med tomma
metoder) till klassen Turtle.
Implementera klassen Turtle: Skriv in attributen i klassen. Skriv gärna en kommentar
till varje attribut som förklarar vad attributet betyder. Skriv också konstruktorn och se till
att alla attribut får rätt startvärden. Implementera de övriga operationerna.
2.
Klasserna TurtleDrawSquare och TurtleDrawRandomFigure finns i filerna TurtleDrawSquare.java och TurtleDrawRandomFigure.java. Kör programmen och kontrollera att din Turtleimplementation är korrekt.
Använd gärna debuggern för att hitta eventuella svårfunna fel. Här kan det vara bra
att utnyttja kommandot Step Into. Det fungerar som Step Over med skillnaden att man
följer exekveringen in i metoder som anropas.
3.
Du ska nu träna mer på att använda debuggern i Eclipse. I fortsättningen förutsätter vi
att du utnyttjar debuggern för att hitta fel i dina program.
Välj ett av programmen från uppgift 2. Sätt en brytpunkt på en rad där en metod i
Turtle anropas, t. ex. t.penDown() eller något liknande. Kör programmet i debuggern.
Använd Step Into och följ hur man från main-metoden ”gör utflykter” in i metoderna i
klassen Turtle.
Lägg märke till vilka storheter (variabler, attribut, parametrar) som är tillgängliga när
man ”är i” main-metoden resp. inuti någon metod i klassen Turtle. Tips! Om du klickar
på trekanten framför this i variabelvyn visas Turtle-objektets attribut.
Laboration 6 – implementera och ärva klasser, Turtle och ColorTurtle
4.
61
I projektet finns också programmet TurtleTest, som testar din Turtle-klass i olika avseenden. Ett antal misstag kan upptäckas på detta sätt. Resultatet ska se ut så här:
Kör programmet TurtleTest. Om de ritade figurerna är felaktiga, eller något av testerna
besvaras med ”NEJ”, gå tillbaka till din Turtle-klass och åtgärda felet.
5.
Skriv ett program där en sköldpadda tar 1000 steg i ett fönster. Sköldpaddan ska börja
sin vandring mitt i fönstret. I varje steg ska steglängden väljas slumpmässigt i intervallet
[1, 10]. Efter varje steg ska sköldpaddan vridas ett slumpmässigt antal grader i intervallet
[−180, 180].
6.
Skriv ett program där två sköldpaddor vandrar över ritfönstret. Steglängden och vridningsvinkeln ska väljas slumpmässigt i samma intervall som i föregående uppgift. Vandringen ska avslutas när avståndet mellan de båda sköldpaddorna är mindre än 50 pixlar.
Sköldpaddorna ska turas om att ta steg på följande sätt:
while ("avståndet mellan sköldpaddorna" >= 50) {
"låt den ena sköldpaddan ta ett slumpmässigt steg och göra
en slumpmässig vridning"
"låt den andra sköldpaddan ta ett slumpmässigt steg och göra
en slumpmässig vridning"
SimpleWindow.delay(10);
}
Låt den ena sköldpaddan börja sin vandring i punkten (250,250) och den andra i (350,350).
7.
Betrakta klassen Turtle. Ge exempel på ett attribut, en formell parameter och en lokal
variabel. Ange också var i klassen/programmet respektive storhet är tillgänglig och får
användas.
Exempel på
Attribut
Namn
Får användas var
Formell parameter
Lokal variabel
8.
Implementera klassen ColorTurtle genom att ärva Turtle, så att basklassen utökas med
ett attribut color. Ändra sedan slumpvandringsprogrammet från uppgift 6 så att en röd
och en blå sköldpadda vandrar i fönstret.
Laboration 6 – implementera och ärva klasser, Turtle och ColorTurtle
62
Checklista
I den här laborationen har du övat på
• skillnaden mellan attribut, parametrar och lokala variabler,
• att använda konstruktorer för att sätta attributens startvärden,
• att använda klassen Math för beräkningar, och
• att skapa och använda en egen klass.
• att skapa och använda en klass som ärver en annan klass och utökar den med extra
attribut och metoder.
Laboration 7 – Maze
63
Laboration 7 – Maze
Mål: Du ska lösa ett problem där huvuduppgiften är att konstruera en algoritm för att hitta
vägen genom en labyrint. Det ska du göra genom att skriva program som använder din Turtleklass från Lab6.
Förberedelseuppgifter
1. Repetera kap 3 i läroboken.
2. Läs bakgrundsavsnittet nedan och studera dokumentationen för Maze på kurshemsidan
http://cs.lth.se/eda016/doc
Bakgrund
En labyrint består av ett rum med en ingång och en utgång. Alla väggar är parallella med xeller y-axeln. Ingången finns alltid i den vägg som är längst ner i figuren. Exempel:
Ett sätt att hitta från ingången till utgången är att gå genom labyrinten och hela tiden hålla
den vänstra handen i väggen. När man vandrar genom labyrinten är fyra riktningar möjliga. En
riktning definieras som vinkeln mellan x-axeln och vandringsriktningen och mäts i grader:
90
180
0
270
Labyrinten beskrivs av en färdigskriven klass Maze. Labyrintens utseende läses in från en fil när
labyrintobjektet skapas. Klassen har följande specifikation:
/** Skapar en labyrint med nummer nbr. */
Maze(int nbr);
/** Ritar labyrinten i fönstret w. */
void draw(SimpleWindow w);
/** Tar reda på x-koordinaten för ingången. */
int getXEntry();
/** Tar reda på y-koordinaten för ingången. */
int getYEntry();
Laboration 7 – Maze
64
/** Undersöker om punkten x,y är vid utgången. */
boolean atExit(int x, int y);
/** Undersöker om man, när man befinner sig i punkten x,y och är på väg i
riktningen direction, har en vägg direkt till vänster om sig. */
boolean wallAtLeft(int direction, int x, int y);
/** Som wallAtLeft, men undersöker om man har en vägg direkt framför sig. */
boolean wallInFront(int direction, int x, int y);
/** Väntar ms millisekunder. */
static void delay(int ms);
I operationerna wallAtLeft och wallInFront betraktas alla riktningar R ± n · 360◦ , n = 1, 2, . . .
som ekvivalenta med R.
Väggarna har bredden 6 pixlar. De ritas dock med bredden 4 pixlar för att det ska bli litet
luft mellan sköldpaddan och väggen. Exempel där man vandrar runt ett hörn:
9
8
7
6
5
4
3
2
1
I punkterna 1–4 är riktningen 90. wallAtLeft ger i dessa punkter värdet true, wallInFront
värdet false. I punkt 5 ger wallAtLeft värdet false, varför man svänger åt vänster (riktningen
90 ändras till 180). Man fortsätter sedan genom punkterna 6–9. I dessa punkter ger wallAtLeft
värdet true, wallInFront värdet false.
I uppgiften ska du skriva en klass MazeWalker som låter en sköldpadda vandra i en labyrint.
Låt klassen ha följande uppbyggnad:
public class MazeWalker {
private Turtle turtle;
public MazeWalker(Turtle turtle) {
// fyll i kod
}
/** Låter sköldpaddan vandra genom labyrinten maze, från
ingången till utgången. */
public void walk(Maze maze) {
// fyll i kod
}
}
Uppgifter
1.
Till denna uppgift finns inte något färdigt Eclipseprojekt. Du måste alltså skapa ett sådant:
1. Klicka på New-ikonen i verktygsraden. Välj Java Project.
2. Skriv namnet på projektet (t.ex. MazeTurtle).
3. Klicka på Finish.
Laboration 7 – Maze
2.
65
Programmet som du skriver kommer att använda klasserna SimpleWindow och Maze.
Dessa klasser finns i biblioteksfilen cs_pt.jar, som finns i projektet cs_pt. Programmet
kommer också att utnyttja klassen Turtle från laboration 6.
Du måste därför göra cs_pt.jar och projektet Lab6 tillgängliga för ditt nya projekt.
1. Högerklicka på projektet MazeTurtle, välj Build Path → Configure Build Path.
2. Klicka på fliken Libraries.
3. Klicka på Add JARS. . . , öppna projektet cs_pt, markera filen cs_pt.jar och klicka OK.
4. Klicka nu på fliken Projects (istället för Libraries).
5. Klicka på Add. . . , markera Lab6 och klicka OK.
6. Klicka på OK för att lämna dialogen.
3.
Skapa klassen MazeWalker. I metoden walk ska sköldpaddan börja sin vandring i punkten
med koordinaterna getXEntry(), getYEntry() och gå uppåt i labyrinten. Alla steg ska ha
längden 1. Lägg in en fördröjning efter varje steg så att man ser hur sköldpaddan vandrar.
4.
Skriv ett huvudprogram som kontrollerar att sköldpaddan kan vandra i labyrinterna.
Numret på labyrinten ska läsas in från tangentbordet.
Testa programmet. Det finns fem olika labyrinter, numrerade 1–5. Givetvis ska alla
testas och fungera.
Extrauppgifter
5.
Utforska källkoden för Maze som finns i projektet cs_pt. Titta också på de textfiler som
lagrar utseendet för respektive labyrint. Kan du med hjälp av källkoden förstå hur dessa
ska tolkas?
6.
Använd ColorTurtle och rita sköldpaddan väg med valfri färg.
7.
Summera antalet steg sköldpaddan tar och antalet riktningsändringar som sköldpaddan
gör och skriv ut dessa summor när sköldpaddan kommit i mål.
Laboration 8 – vektorer, simulering av patiens
66
Laboration 8 – vektorer, simulering av patiens
Mål: Du ska skriva ett enkelt simuleringsprogram och lära dig att använda vektorer.
Förberedelseuppgifter
• Studera avsnitt 8.1–8.3 i läroboken.
• Läs avsnittet Bakgrund.
• Betrakta följande satser:
Card[] theCards = new Card[52];
theCards[0] = new Card(Card.SPADES, 12);
theCards[1] = new Card(Card.HEARTS, 13);
// 1
// 2
Se till att du kan förklara vad som händer i steg 1 respektive 2 ovan. Rita en figur (på
papper) som visar vilka variabler och objekt som finns när man exekverat satserna till och
med punkt 1 resp. 2 ovan.
Bakgrund
Patiensen 1–2–3 läggs på följande sätt: Man tar en kortlek, blandar den och lägger sedan ut
korten ett efter ett. Samtidigt som man lägger korten räknar man 1–2–3–1–2–. . . , det vill säga
när man lägger det första kortet säger man 1, när man lägger det andra kortet säger man 2, osv.
Patiensen går ut om man lyckas lägga ut alla kort i leken utan att någon gång få upp ett ess när
man säger 1, någon 2-a när man säger 2 eller någon 3-a när man säger 3.
Man kan med hjälp av sannolikhetslära bestämma exakt hur stor sannolikheten är att patiensen ska gå ut.4 Men det är betydligt enklare att uppskatta sannolikheten genom att lägga
patiensen många gånger och räkna antalet gånger som den går ut.
Allra snabbast går det om datorn lägger patiensen. Då har man användning av följande
klasser, som beskriver kort och kortlekar:
/** konstanter för färgerna */
static final int SPADES = ...;
static final int HEARTS = SPADES + 1;
static final int DIAMONDS = SPADES + 2;
static final int CLUBS = SPADES + 3;
/** Skapar ett spelkort med färgen suit (SPADES, HEARTS, DIAMONDS, CLUBS)
och valören rank (1-13). */
Card(int suit, int rank);
/** Tar reda på färgen. */
int getSuit();
/** Tar reda på valören. */
int getRank();
/** Returnerar en läsbar representation av kortet, till exempel
"spader ess". */
String toString();
4
Se ”Fråga Lund om matematik”, www.maths.lth.se/query/answers, sök efter ”patiens” på sidan.
Laboration 8 – vektorer, simulering av patiens
/** Skapar en kortlek. */
CardDeck();
67
/** Återställer kortleken till fullt antal och blandar den. */
void shuffle();
/** Undersöker om det finns fler kort i kortleken. */
boolean moreCards();
/** Drar det översta kortet i leken. */
Card getCard();
I följande program skriver man ut färg och valör för alla kort som man drar ur en blandad
kortlek:
public class CardExample {
public static void main(String[] args) {
CardDeck deck = new CardDeck();
deck.shuffle();
while (deck.moreCards()) {
Card c = deck.getCard();
System.out.println(c);
}
}
}
Med hjälp av System.out.println(c) skriver man ut ett kort på skärmen. Inuti println anropas metoden toString och den sträng som metoden returnerar kommer att skrivas ut. Det är
alltså i Card-klassens toString-metod man bestämmer hur kortet ska presenteras på skärmen.
Datorarbete
1.
(valfritt) Om du vill kan du få lite mer hjälp av Eclipse när du programmerar. Ta fram
fönstret med Eclipse-inställningarna:
Window → Preferences (Studentdatorerna, Windows, Linux)
Eclipse → Inställningar... (Mac)
Välj Java → Editor → Content Assist → Advanced. Klicka i Java proposals och Template proposals. Klicka Apply, därefter OK. Hädanefter kommer Eclipse att ge förslag beroende på
sammanhanget. Följande bild visar hur det kan se ut när programmeraren skrivit ”deck”,
tryckt punkt, och nu bläddrar bland förslagen:
68
Laboration 8 – vektorer, simulering av patiens
2.
Öppna projektet Lab8. I projektet finns bland annat den färdiga klassen Card. Öppna filen
Card.java och se hur klassen är implementerad.
3.
Ett program med programraderna från den tredje förberedelseuppgiften finns i filen ArrayExample.java. Sätt en brytpunkt i början av programmet och provkör programmet i
debuggern. Framför theCards i variabelvyn visas ett plustecken eller en triangel – klicka
där så ser du innehållet i vektorn.
4.
Implementera klassen CardDeck.
I klassen ska man hålla reda på de 52 korten i en kortleken. Det gör man enklast
genom att lagra korten i en vektor:
import java.util.Random;
public class CardDeck {
private Card[] cards;
private int current; // index för "nästa" kort
private static Random rand = new Random();
public CardDeck() {
cards = new Card[52]; // skapa vektorn
... // skapa korten, lägg in dem i vektorn
current = 0;
}
...
}
Att skapa korten i ordning är inte så enkelt – man känner ju inte värdet på konstanterna som anger färgerna. Men man ser att konstanterna ligger i ordning, så man kan skriva
satser som den följande:
for (int suit = Card.SPADES; suit <= Card.CLUBS; suit++) ...
När man ska blanda kortleken bör man använda följande algoritm:
för i = 51, 50, ..., 1 {
nbr = slumptal i intervallet [0,i+1)
byt plats på korten med index i och index nbr
}
5.
Ett testprogram (i filen TestCardDeck.java) för CardDeck finns i projektet. Testprogrammet
skapar en kortlek och skriver ut alla korten. Därefter blandas kortleken och alla kort skrivs
ut på nytt.
Kör programmet och kontrollera att det som skrivs ut ser ut att stämma.
6.
Skriv ett huvudprogram som utnyttjar klasserna Card och CardDeck för att lägga patiensen ett stort antal gånger och därefter skriva ut sannolikheten för att patiensen ska gå
ut.
Kör programmet. Den korrekta sannolikheten är ungefär 0.008165.
Checklista
I den här laborationen har du övat på att
Laboration 8 – vektorer, simulering av patiens
• använda vektorer för att lagra och hantera objekt,
• använda konstanter för att representera egenskaper, och
• använda attribut för att lagra information om objektets tillstånd.
69
Grupplaboration 9 – TurtleRace, ArrayList och ärvning
70
Grupplaboration 9 – TurtleRace, ArrayList och
ärvning
Mål: Du tränar på att använda listor för att hålla reda på objekt och lär dig mer om arv.
Förberedelseuppgifter
•
•
•
•
Studera kapitel 12 i läroboken (framför allt 12.1–12.6) noga!
Läs även 9.1–9.8 i läroboken.
Läs igenom texten i avsnittet Bakgrund.
Läs avsnitt 3.10, om static, i läroboken. Titta i klassen RaceWindow och fundera över de
statiska attribut och metoder som finns där. Fundera på varför det är rimligt att dessa är
statiska. Om något är oklart, fråga din handledare vid laborationens början.
• Diskutera i din samarbetsgrupp hur ni vill dela upp ansvaret och arbetet för olika delar
av koden. Det är lämpligt att varje klass har en huvudansvarig. Flera kan hjälpas åt med
samma klass, t.ex. genom att implementera olika metoder.
• När ni redovisar er lösning ska ni också kunna redogöra för hur ni delat upp ansvar och
arbete mellan er. Var och en redovisar sina delar.
• Grupplaborationer görs i huvudsak som hemuppgift. Salstiden används primärt för redovisning.
Bakgrund
I denna laboration utvecklas ett program där olika typer av sköldpaddor tävlar mot varandra i
en kapplöpning.
Del A – RaceTurtle
Vi använder klassen SimpleWindow på ett annorlunda sätt än tidigare: vi har här specialiserat den
genom subklassen RaceWindow. Ett RaceWindow fungerar som ett vanligt SimpleWindow (eftersom
det ärver från SimpleWindow) men med tillägget att en kapplöpningsbana ritas upp då fönstret
skapas.
Uppgiften går ut på att skriva ett program som simulerar en (slumpmässig) kapplöpning
mellan sköldpaddor (Turtle-objekt). En sköldpadda tar sig fram genom simulerade tärningskast (dvs. slumpmässiga steg framåt mellan 1 och 6), med ett ”kast” i varje runda. Samtliga
sköldpaddor ska lagras i en ArrayList.
En klass RaceTurtle ska representera en sköldpadda som kan simulera en kapplöpning.
RaceTurtle ska ärva från klassen Turtle och därmed kan en RaceTurtle göra allt som en vanlig
Turtle kan. Därtill ska en RaceTurtle innehålla en slumptalsgenerator och ett startnummer.
RaceTurtle ska ha en metod raceStep() och en metod toString(). Metoden raceStep() ska
beskriva hur sköldpaddan tar ett löpsteg.
RaceTurtle ska alltså ärva från Turtle och ha följande specifikation:
/**
* Skapar en sköldpadda som ska springa i fönstret w och som har start* nummer nbr. Sköldpaddan startar med pennan nere och nosen vänd åt höger.
*/
RaceTurtle(RaceWindow w, int nbr);
/**
* Låter sköldpaddan gå framåt ett steg. Stegets längd ges av ett
* slumptal (heltal) mellan 1 och 6.
Grupplaboration 9 – TurtleRace, ArrayList och ärvning
71
*/
void raceStep();
/**
* Returnerar en läsbar representation av denna RaceTurtle,
* på formen "Nummer x" där x är sköldpaddans startnummer.
*/
String toString();
Del B – Subklasser till RaceTurtle
Uppgiften innefattar också att skapa ett antal klasser som beskriver olika slags tävlande sköldpaddor, samt att modifiera huvudprogrammet för att genomföra en sådan kapplöpning. Klassen
RaceTurtle från Del A ska representera det som samliga tävlingssköldpaddor har gemensamt.
Nu ska emellertid metoden raceStep överskuggas (override) av subklassernas mer specifika definition av metoden. Vi behöver flera subklasser, en för varje slags sköldpadda. Bilden nedan
visar arvshierarkin.
Programmet ska innehålla lika många tävlande sköldpaddor (subklasser till RaceTurtle)
som ni är personer i samarbetsgruppen. Nedan finns exempel på olika slags sköldpaddor:
• MoleTurtle, som beskriver en ”mullvadssköldpadda”, d.v.s. en tävlande sköldpadda som
då och då går under jorden (genom att lyfta pennan slumpmässigt).
• AbsentMindedTurtle, som är en tankspridd sköldpadda. Graden av tankspriddhet anges i
procent när sköldpaddan skapas, och sannolikheten att en tankspridd sköldpadda skall gå
framåt bestäms av tankspriddhetsgraden. Exempel: en tankspriddhetsgrad på 34 procent
ska medföra att sköldpaddan i 34 procent av fallen glömmer att ta sitt steg.
• DizzyTurtle, som beskriver en yr tävlingssköldpadda, som vinglar när den skall ta sig
framåt. När dessa sköldpaddor skapas ska graden av yrsel (från 1 till 5) anges, och deras
förmåga att hålla kursen skall bestämmas av yrselgraden.Det behövs inte någon avancerad simulering av yrseln, men en sköldpadda med högre yrselgrad bör vingla mer än en
mindre yr sköldpadda.
Turtle
@
@
RaceTurtle
@
@
AbsentMindedTurtle
DizzyTurtle
MoleTurtle
AnotherTurtle
Grupplaboration 9 – TurtleRace, ArrayList och ärvning
72
Datorarbete
Del A
1.
Inkludera din Turtle från laboration 6 genom att:
1. Högerklicka på projektet Lab9, välj Build Path → Configure Build Path.
2. Klicka på fliken Projects.
3. Klicka på Add. . . , markera Lab6 och klicka OK.
4. Klicka på OK för att lämna dialogen.
2.
Implementera klassen RaceTurtle enligt beskrivning och specifikation ovan. När konstruktorn implementeras för RaceTurtle måste dess position beräknas. Det finns två
hjälpmetoder i RaceWindow, som beroende på startnummer returnerar en lämplig x- respektive y-position. På så sätt får alla sköldpaddor en egen bana.
3.
I klassen RaceTurtleTest finns en main-metod som skapar en sköldpadda och låter denna
genomföra ett eget lopp utan motståndare. Klassen kan användas som ett första test av
din RaceTurtle. Avkommentera och studera koden – det är meningen att du ska förstå
vad den gör. Kör sedan programmet för att se om sköldpaddan går i mål som förväntat.
4.
Gör en ny klass TurtleRace för att genomföra ett lopp enligt följande (det är lämpligt att
inspireras av testprogrammet):
• Skapa åtta sköldpaddor och lagra dessa i en ArrayList. Låt dem ha sin kapplöpning
i ett och samma RaceWindow.
• Efter loppet ska första, andra och tredje plats skrivas ut (enligt exemplet nedan). Det
är ett krav att utskriften använder sig av toString-metoden.
På plats 1: Nummer 5
På plats 2: Nummer 6
På plats 3: Nummer 1
• Tips: När en sköldpadda går i mål kan man ta ut den ur den vanliga listan, och sätta
in den i en separat lista. När den vanliga listan är tom har alla sköldpaddorna gått i
mål, och då görs utskriften ovan.
• Observera att for-each-loopen inte tillåter att vi ändrar en listas innehåll i loopen.
• Små orättvisor i resultatberäkningen är acceptabla. Kanske upptäcker lösningen gynnar sköldpaddor med lägre startnummer över de med högre startnummer. Förklara
för din handledare vari orättvisan består, och diskutera gärna hur man kan åtgärda
den.
• Det är därmed inte heller nödvändigt att hantera delade placeringar.
5.
Provkör ditt sköldpaddslopp. Använd en fördröjning, t. ex. RaceWindow.delay(10), så
man hinner se hur loppet fortskrider. Kontrollera att rätt placeringar skrivs ut.
Del B
6.
Implementera tävlingssköldpaddorna som beskrivs i Bakgrund Del B. Skapa en ny klass
för varje typ av sköldpadda och låt varje klass ärva från RaceTurtle. Varje subklass sådan
ska ha sin egen definition av metoderna raceStep() och toString() – dessa ska alltså
implementeras på nytt.
Grupplaboration 9 – TurtleRace, ArrayList och ärvning
73
Observera att raceStep() och toString() inte är abstrakta i RaceTurtle. De har alltså redan en implementation. Det gör att man måste komma ihåg att överskugga dessa i
varje subklass, men de går samtidigt att utnyttja för att slippa duplicera kod. Exempelvis kan kan man från subklassernas raceStep-metoder anropa super.raceStep() för att
genomföra själva steget, förutom den kod som är specifik för varje subklass (lyfta/sänka
pennan, jämföra tankspriddhet, välja riktning).
7.
Skapa en ny klass MultiTurtleRace som ärver klassen TurtleRace, men genomför ett
lopp enligt följande:
• Skapa åtta sköldpaddor av slumpmässig typ.
• Notera att då alla ärver från en gemensam klass behöver listan inte ändras på något
vis. Se till att du förstår varför en lista av RaceTurtle även kan innehålla objekt av
klasserna MoleTurtle, AbsentMindedTurtle och DizzyTurtle. Fråga gärna handledaren.
• Då AbsentMindedTurtle ska skapas ska tankspriddhetsgraden slumpas fram mellan
0 och 100.
• Då DizzyTurtle ska skapas ska yrselgraden slumpas fram mellan 1 och 5.
• Innan loppet börjar ska hela startuppställningen skrivas ut enligt exemplet nedan,
ett krav är att detta sker genom respektive sköldpaddas toString-metod.
Nummer
Nummer
Nummer
Nummer
Nummer
Nummer
Nummer
Nummer
1
2
3
4
5
6
7
8
-
MoleTurtle
AbsentMindedTurtle (45% Frånvarande)
AbsentMindedTurtle (43% Frånvarande)
DizzyTurtle (Yrsel: 3)
MoleTurtle
MoleTurtle
AbsentMindedTurtle (71% Frånvarande)
DizzyTurtle (Yrsel: 5)
• Efter loppet ska första, andra och tredje plats skrivas ut (enligt exemplet nedan).
På plats 1: Nummer 5 - MoleTurtle
På plats 2: Nummer 6 - MoleTurtle
På plats 3: Nummer 4 - DizzyTurtle (Yrsel: 3)
• Det kan hända att du behöver ändra i klassen TurtleRace, så att det lättare ska gå
att återanvända dess kod i subklassen MultiTurtleRace.
• Ändra nu så att varje sköldpaddstyp ritas med en egen färg genom att använda ColorTurtle på lämpligt sätt. Fundera först på olika möjliga lösningar och tänk igenom
var ändringar behöver göras. Rita ett klassdiagram som visar ärvningsstrukturen.
Checklista
I den här laborationen har du övat på att
• använda listor för att lagra objekt,
• ärva egenskaper från en superklass,
• implementera och använda toString-metoden, och
• använda statiska hjälpmetoder.
Laboration 10 – matris, spelet life
74
Laboration 10 – matris, spelet life
Mål: Du ska skriva program som använder matriser. Du ska också få mer träning på att använda
färdiga klasser och att implementera egna klasser.
Förberedelseuppgifter
• Studera avsnitt 8.4 i läroboken.
Bakgrund
”Game of Life”, konstruerat av matematikern John Conway, är ett simuleringsspel som uppvisar
likheter med de förändringar som äger rum i samhällen med levande individer.
Som spelplan används ett rutnät. En ruta kan antingen vara tom eller innehålla en individ.
Från en given spelplan, en generation, ska samhället ändra sin struktur enligt vissa regler för
födelse och död.
Nedanstående regler gäller när en ny generation bildas. Alla förändringar vid en generationsväxling sker samtidigt över hela rutnätet.
1. Fortlevnad. En individ lever vidare till nästa generation om den har 2 eller 3 grannar.
2. Dödsfall. En individ med 4 eller fler grannar dör av trängsel. En individ med 0 eller 1
granne dör av isolering.
3. Födelse. I en tom ruta gränsande till exakt 3 individer föds en individ.
Varje ruta i rutnätet har 8 grannrutor. Även de diagonalt intilliggande rutorna betraktas alltså
som grannrutor.
Exempel på fyra på varandra följande generationer i Life-spelet (individerna markeras med
fyllda rutor):
Generation 1
Generation 2
Generation 3
Generation 4
Du ska skriva ett program för Life-spelet. Användargränssnittet, det fönster där spelplanen
ritas upp, beskrivs av en färdigskriven klass med namnet LifeView. Användaren kan när som
helst ändra spelplanens utseende genom att klicka på en ruta i spelplanen. Om man klickar
på en tom ruta skapas en individ i rutan, om man klickar på en fylld ruta töms rutan. Detta
utnyttjas normalt för att skapa generation 1.
Förutom spelplanen ritas en Next-knapp, som användaren klickar på när nästa generation
ska skapas och ritas upp, samt en Quit-knapp som användaren klickar på när spelet ska avslutas.
Följande klasser ska finnas i lösningen:
LifeBoard, som beskriver spelplanen och generationsräknaren. Innehåller operationer för att
ta reda på och ändra spelplanens utseende, t ex vilka rutor som ska vara tomma och vilka som
ska innehålla individer.
LifeView, som beskriver hur spelplanen presenteras på skärmen. Innehåller operationer för
att rita upp spelplanen och för att vänta på att användaren klickar i rutorna eller på Next- eller
Quit-knappen. Klassen är färdigskriven (specifikationen finns nedan).
Laboration 10 – matris, spelet life
75
Life, som beskriver Life-spelet. Innehåller operationer för att skapa en ny generation enligt
reglerna och för att skapa en individ i en ruta eller tömma en ruta.
LifeController, en klass med en main-metod, som genomför Life-spelet från generation 1
till ”Quit”.
Specifikation för LifeBoard och LifeView:
/** Skapar en spelplan med rows rader och cols kolonner. Spelplanen är från
början tom, dvs. alla rutorna är tomma och generationsnumret är 1. */
LifeBoard(int rows, int cols);
/** Undersöker om det finns en individ i rutan med index row,col. Om
index row,col hamnar utanför spelplanen returneras false. */
boolean get(int row, int col);
/** Lagrar värdet val i rutan med index row, col. */
void put(int row, int col, boolean val);
/** Tar reda på antalet rader. */
int getRows();
/** Tar reda på antalet kolonner. */
int getCols();
/** Tar reda på aktuellt generationsnummer. */
int getGeneration();
/** Ökar generationsnumret med ett. */
void increaseGeneration();
/** Skapar vy till spelplanen board. */
LifeView(LifeBoard board);
/** Ritar upp de fixa delarna av spelplanen (rutnätet,
generationsräknaren och knapparna. */
void drawBoard();
/** Ritar om de delar av fönstret som ändrats sedan föregående
uppritning. */
void update();
/** Väntar tills användaren klickar med musen. Ger:
1: klick i ruta på spelplanen. Index för rutan kan hämtas med
getRow och getCol.
2: klick i Next-rutan.
3: klick i Quit-rutan. */
int getCommand();
/** Tar reda på radnummer för den klickade rutan efter kommando nr 1. */
int getRow();
/** Tar reda på kolonnummer för den klickade rutan efter kommando nr 1. */
int getCol();
Rutorna i rutnätet numreras 0..rows–1, 0..cols–1. I klassen LifeBoard finns det dock en tänkt
”ram” kring rutnätet. I ramrutorna finns inga individer. Genom att ramen finns behöver du inte
specialbehandla rutorna i kanten av rutnätet. Du kan alltså göra get(row,col) med row/col =
–1 eller rows/cols. I de fallen ger metoden get alltid resultatet false.
Laboration 10 – matris, spelet life
76
Klassen Life har följande specifikation:
/** Skapar ett Life-spel med spelplanen board. */
Life(LifeBoard board);
/** Skapar en ny generation. */
void newGeneration();
/** Ändrar innehållet i rutan med index row, col från individ till tom
eller tvärtom. */
void flip(int row, int col);
Datorarbete
Det är lämpligt att lösa ett stort problem stegvis. Först skriver man lite kod, sedan testar man
att det fungerar innan man går vidare. Därför är laborationsuppgiften uppdelad i mindre deluppgifter.
1.
Öppna projektet Lab10. I filen LifeBoard.java finns ett skelett (en klass utan attribut och
med tomma metoder) till klassen LifeBoard.
Implementera klassen LifeBoard.
2.
Ett testprogram (TestLifeBoard.java) för klassen LifeBoard finns i projektet. Testprogrammet testar inte klassen fullständigt, men skapar ett LifeBoard-objekt, låter ett LifeViewobjekt rita upp brädet samt anropar de olika metoderna i LifeBoard.
Kör programmet och kontrollera att det fungerar som det ska. Avsluta exekveringen
genom att klicka i Quit-rutan på spelplanen.
3.
Det är dags att börja arbeta med den riktiga main-metoden. Inspiration kan hämtas från
testprogrammet (TestLifeBoard.java). Skapa en fil med namnet LifeController.java som
ska innehålla main-metoden. Du ska till att börja med se till att Quit-knappen fungerar
(Vänta med rutnätet och Next-knappen).
Ledning: Med metoden getCommand i LifeView kan man vänta tills användaren klickar
på en ruta eller en knapp. Då returneras ett tal med vars hjälp man kan avgöra vad
användaren gjorde. Låt alltså användaren klicka i fönstret tills användaren klickar i Quitrutan. Avsluta då programmet med hjälp av System.exit(0).
Kör programmet och kontrollera att du kan avsluta programmet genom att trycka på
Quit-knappen.
4.
Skapa en fil Life.java som ska innehålla klassen Life. Implementera konstruktorn och
metoden flip i klassen Life. Implementera metoden newGeneration, men bara delvis –
lägg in ökning av generationsnumret.
Spara klassen Life och rätta eventuella kompileringsfel.
5.
Fortsätt med klassen med main-metoden. Implementera satser i main-metoden så att rätt
saker utförs när man klickar med musen; om man klickar i en ruta ska en individ skapas
eller tas bort (med metoden flip). Om man klickar på Next-knappen ska generationsnumret ändras. Om man trycker på Quit-knappen ska programmet avslutas.
Kör programmet och kontrollera att det fungerar som det ska.
Laboration 10 – matris, spelet life
6.
77
Nu återstår att implementera färdigt metoden newGeneration i klassen Life. Lägg gärna
till en intern (privat) metod som beräknar antal grannar till en ruta. Metoden newGeneration
blir kortare och mer lättläst då.
Tänk på att i metoden newGeneration ska alla förändringar vid en generationsväxling
utföras samtidigt över hela spelplanen. Enklast åstadkommer du detta genom att deklarera en hjälpmatris där du lagrar den nya spelplanen. I slutet av metoden kopierar du
hjälpmatrisen till den riktiga spelplanen. Alternativt kan du använda ett hjälpbräde (ett
annat LifeBoard-objekt) där du lagrar den nya spelplanen. Även i detta fall måste du
avsluta med att kopiera hjälpbrädet till den riktiga spelplanen.
Testa programmet på åtminstone följande fyra exempel:
Exempel 1 (två konfigurationer erhålls omväxlande)
Exempel 2 (i generation 7 är alla döda)
Exempel 3 (antalet individer växer, i generation 15 erhålls en stabil konfiguration). Observera att spelplanen behöver vara större än vad som visas här – minst 13x13 rutor.
Prova också följande. Varför kallar Life-entusiasterna denna figur för ”glider”?
Mer att läsa om ”Conway’s Game of Life” och fler exempel att prova finns på nätet.
Använd t. ex. sökorden "Conway game of life".
Laboration 10 – matris, spelet life
78
Checklista
I den här laborationen har du övat på att
• använda matriser,
• spara och ändra tillståndet hos en spelplan,
• använda logiska värden och skriva logiska uttryck, och
• strukturerat dela upp funktionalitet mellan klasser.
Grupplaboration 11 – Matriser, bilbehandling ImageFilter
79
Grupplaboration 11 – Matriser, bilbehandling
ImageFilter
Mål: Du lär dig att använda matriser och att implementera mera komplicerade operationer. Du
får också en introduktion till bildbehandling.
Förberedelseuppgifter
• Läs avsnitt 8.6 (matriser), 8.10 (registrering), 9.3 och 9.6 (arv) i läroboken.
• Diskutera i din samarbetsgrupp hur ni vill dela upp ansvaret och arbetet för olika delar
av koden. Det är lämpligt att varje klass har en huvudansvarig. Flera kan hjälpas åt med
samma klass, t.ex. genom att implementera olika metoder.
• När ni redovisar er lösning ska ni också kunna redogöra för hur ni delat upp ansvar och
arbete mellan er. Var och en redovisar sina delar.
• Grupplaborationer görs i huvudsak som hemuppgift. Salstiden används primärt för redovisning.
Bakgrund
En digital bild består av ett rutnät (en matris) av pixlar. Varje pixel har en färg, och om man har
många pixlar flyter de samman för ögat så att de tillsammans skapar en bild.
Färger kan anges enligt olika system, men i datorer är det vanligt att man använder RGBsystemet. I RGB-systemet sätts en färg samman av tre grundfärger: röd, grön och blå. Mättnaden
av varje grundfärg anges av ett heltal som vi i fortsättningen förutsätter ligger i intervallet
[0, 255]. 0 anger ”ingen färg” och 255 anger ”maximal färg”. Man kan därmed representera
256 × 256 × 256 = 16 777 216 olika färgnyanser. Man kan också representera gråskalor; det
gör man med färger som har samma värde på alla tre grundfärgerna: (0, 0, 0) är helt svart,
(255, 255, 255) är helt vitt.
I Java representeras färger av klassen java.awt.Color. Den klassen beskrivs i appendix C i
läroboken och utförligare i Javadokumentationen på nätet.
När man har en digital bild i datorn kan man förändra och förbättra bilden på olika sätt. Det
finns många program för detta ändamål, till exempel Photoshop (kommersiellt) och GIMP (gratis, GNU-projekt). Du ska i denna uppgift skriva ett program som kan göra en del enkla transformationer (kallas i fortsättningen ”filtreringar”) av bilder, till exempel förbättra kontrasten i
en bild och markera konturer i en bild. Användargränssnittet till programmet är färdigskrivet;
din uppgift är att skriva metoder som utför filtreringarna.
Figur 1 visar hur användargränssnittet till programmet ser ut. Man väljer filtreringsmetod
i menyn längst till vänster och utför filtreringen genom att klicka på Apply-knappen. En del
av filtreringsmetoderna behöver en parameter; värdet på denna skriver man i textrutan. I fönstret visas originalbilden till vänster och den filtrerade bilden till höger (här har Sobelfiltrering
använts, som markerar konturer i bilden).
Användargränssnittet beskrivs av klassen ImageGUI. main-metoden finns i klassen ImageProcessor. Den klassen har följande utseende:
import se.lth.cs.ptdc.images.ImageFilter;
import se.lth.cs.ptdc.images.ImageGUI;
public class ImageProcessor {
public static void main(String[] args) {
ImageFilter[] filters = new ImageFilter[1];
filters[0] = new IdentityFilter("Identity Filter");
new ImageGUI(filters);
Grupplaboration 11 – Matriser, bilbehandling ImageFilter
80
Figur 1: Användargränssnittet i bildbehandlingsprogrammet.
}
}
De objekt som man lägger in i vektorn filters tas omhand av användargränssnittet. Du kommer senare att skapa flera filterobjekt av olika slag och skicka till ImageGUI. Namnen på filtren
(strängparametern till konstruktorn) visas i menyn där man väljer filtreringsmetod. När man
trycker på Apply-knappen letar ImageGUI upp filtreringsobjektet med rätt namn och anropar en
metod med namnet apply i detta objekt. Om man trycker på Replace-knappen ersätts originalbilden av den filtrerade bilden (det använder man om man vill filtrera en bild med flera olika
filter efter varandra).
Det finns en färdigskriven filterklass, IdentityFilter. Filtreringsmetoden i den klassen gör
ingenting annat än att skapa en identisk kopia av ursprungsbilden. Klassen finns med bara för
att visa hur en filterklass är uppbyggd:
import java.awt.Color;
import se.lth.cs.ptdc.images.ImageFilter;
/** IdentityFilter beskriver en identitetstransformation */
public class IdentityFilter extends ImageFilter {
/** Skapar ett filterobjekt med namnet name */
public IdentityFilter(String name) {
super(name);
}
/** Filtrerar bilden i matrisen inPixels och returnerar resultatet
i en ny matris. Utnyttjar eventuellt värdet av paramValue */
public Color[][] apply(Color[][] inPixels, double paramValue) {
int height = inPixels.length;
int width = inPixels[0].length;
Color[][] outPixels = new Color[height][width];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
Color pixel = inPixels[i][j];
outPixels[i][j] = new Color(pixel.getRed(),
pixel.getGreen(),
pixel.getBlue());
}
}
return outPixels;
}
Grupplaboration 11 – Matriser, bilbehandling ImageFilter
81
}
Metoden apply har två parametrar: inPixels som är originalbilden (matrisen med Color-objekt)
och paramValue som är det värde som man skrivit i Parameter-rutan. (Om man inte har skrivit något i Parameter-rutan, eller skrivit något som inte kan tolkas som ett talvärde, så har
paramValue värdet 0.) paramValue-värdet används inte i klassen IdentityFilter.
I metoden skapas en matris outPixels med samma storlek som inPixels. Därefter skapas
ett nytt Color-objekt i varje matriselement. Eftersom det är en identitetstransformation så är
varje pixel i outPixels lika med motsvarande pixel i inPixels (de har samma RGB-värden). Så
kommer det naturligtvis inte att vara i de filtreringsmetoder som du ska skriva: där kommer
RGB-värdena i outPixels att skilja sig från motsvarande värden i inPixels. Till slut returneras
den nya matrisen. ImageGUI omvandlar sedan matrisen till en bild och visar den i användargränssnittet.
I superklassen ImageFilter beskrivs det som är gemensamt för alla filter: att de har ett
namn och en metod apply som behandlar en bild. Dessutom finns två hjälpmetoder i klassen.
Att metoden apply är ”abstrakt” innebär att den måste implementeras i alla subklasser. Klassen
har följande utseende:
import java.awt.Color;
/** ImageFilter är superklass till alla filterklasser */
public abstract class ImageFilter {
private String name;
/** Skapar ett filterobjekt med namnet name */
protected ImageFilter(String name) {
this.name = name;
}
/** Tar reda på filtrets namn */
public String getName() {
return name;
}
/** Filtrerar bilden i matrisen inPixels och returnerar resultatet
i en ny matris. Utnyttjar eventuellt värdet av paramValue.
Abstrakt metod, implementeras i subklasserna */
public abstract Color[][] apply(Color[][] inPixels, double paramValue);
/** Beräknar intensiteten hos alla pixlarna i pixels, returnerar
resultatet i en ny matris */
protected short[][] computeIntensity(Color[][] pixels) {
// ... färdigskriven metod, se uppgift 5 nedan
}
/** Faltar punkten p[i][j] med faltningskärnan kernel. Summan
av elementen i kernel är weight */
protected short convolve(short[][] p, int i, int j,
short[][] kernel, int weight) {
// ... färdigskriven metod, se uppgift 8, laboration ??
}
}
Grupplaboration 11 – Matriser, bilbehandling ImageFilter
82
Uppgifter
Nedan beskrivs ett antal bildfilterklasser med olika svårighetsgrad. Om ni är n personer i samarbetsgruppen, ska ni implementera minst n + 1 bildfilterklasser. Om ni inte vill implementera
nedanstående filter kan ni välja andra som ni tycker är intressanta och lagom utmanande; prata
i så fall med en handledare om svårighetsgraden. Som frivillig extrauppgift får ni gärna implementera ännu fler filter. Ni kan t.ex. hämta inspiration här:
http://www.jhlabs.com/ip/filters/
1.
På kurshemsidan finns en länk till API-specifikationerna för alla standardklasserna i Java.
Leta upp specifikationen av klassen Color i paketet java.awt och titta igenom den.
2.
I projektet teamlab-imagefilters finns filerna ImageProcessor.java och IdentityFilter.java.
Det finns också en katalog images som innehåller ett antal JPEG-bilder som du kan testa
programmet med (du kan också använda egna JPEG-bilder om du vill). Testkör programmet. I File-menyn finns kommandon för att öppna bildfiler och för att spara filtrerade
bilder på fil. Använd nya filnamn om du sparar filtrerade bilder.
3.
Blåfilter. Skriv en klass BlueFilter som skapar en ny bild där varje pixel bara innehåller den blå komponenten. Kopiera klassen IdentityFilter och gör de ändringar som
behövs. Ändra också huvudprogrammet så att det nya filtret kommer med i menyn.
4.
Invertering av bild. Skriv en klass InvertFilter som inverterar en bild dvs skapar en
”negativ” kopia av bilden.
Fundera över vad som kan menas med en inverterad eller negativ kopia: de nya RGBvärdena är inte ett dividerat med de gamla värdena (då skulle de nya värdena bli flyttal)
och inte de gamla värdena med ombytt tecken (då skulle de nya värdena bli negativa).
5.
Konvertering av färgbild till gråskalebild. Skriv en klass GrayScaleFilter som konverterar en färgbild till en gråskalebild. (Om man utgår från en gråskalebild ska resultatet bli
identiskt med ursprungsbilden.)
Här gäller det alltså att omvandla varje punkt i RGB-rymden (som har storleken 256 ×
256 × 256) till en punkt i gråskalerymden (som har storleken 256). Man behöver ett mått på
hur ”ljus” eller ”intensiv” varje färg är. Om vi kallar RGB-komponenterna r, g respektive
b får vi ett mått på intensiteten genom att räkna ut avståndet från punkten (r, g, b) till
origo.
Man kan använda den vanliga avståndsformeln:
intensity = Math.sqrt((r * r + g * g + b * b) / 3)
Divisionen med 3 kommer sig av att man vill att resultatet ska hamna i intervallet [0, 255].
Det blir dock tidsödande att göra tre multiplikationer och en kvadratrotsberäkning för
varje pixel, så man kan i stället använda följande mått:
intensity = (r + g + b) / 3
Intensiteter måste beräknas i flera av uppgifterna och man bör beskriva hur man gör
det bara en gång. Den färdigskrivna metoden short[][] computeIntensity(Color[][])
som utför beräkningarna har därför placerats i klassen ImageFilter. Den blir därigenom
tillgänglig i alla subklasser. Skyddsspecifikationen protected betyder att metoden är tillgänglig i subklasserna men inte utanför klassen.
Grupplaboration 11 – Matriser, bilbehandling ImageFilter
83
Att göra en gråskalefiltrering tar inte irriterande lång tid, men vi ska ändå titta på en
uppenbar optimering som sparar både tid och minnesutrymme. Gör denna optimering
om du vill och undersök om filtreringen blir märkbart snabbare.
En gråskalebild innehåller högst 256 olika färger: (0, 0, 0), (1, 1, 1), . . . , (255, 255, 255).
Om vi antar att en viss bild har 300 × 500 pixlar och att man skapar ett nytt Color-objekt
för varje pixel så blir det totalt 150 000 objekt, varav många är identiska. Man kan klara
sig med 256 olika objekt om man börjar med att skapa en vektor med de möjliga färgerna:
Color[] grayLevels = new Color[256];
// skapa färgen (0,0,0) och lägg in den i grayLevels[0],
// (1,1,1) i grayLevels[1], ..., (255,255,255) i grayLevels[255]
När man sedan har bestämt sig för att en pixel ska ha en viss intensitet 0–255 behöver
man inte skapa ett nytt objekt med new Color(intensity, intensity, intensity) —
ett sådant objekt finns ju färdigt i vektorelementet grayLevels[intensity].
6.
Kryptering av bild med XOR. Skriv en klass XORCryptFilter som krypterar bilden med
hjälp av Javas xor-operator ˆ. Denna operator gör binär xor mellan bitarna i ett heltal.
Exempelvis ger 8 ˆ 127 värdet 119. Om man gör xor igen med 127, alltså 119 ˆ 127, får
man tillbaka värdet 8. Detta ska vi utnyttja för kryptering genom att göra xor på varje
RGB-värde med en sekvens av pseudoslumptal. Gör krypteringen så här:
1. Skapa ett Random-objekt, t.ex. kallad rand, med det parametervärde som användaren
väljer som slumptalsfrö.
2. För varje pixel: Låt nya RGB-värden bli xor mellan gamla värdet och nästa pseudoslumptal rand.nextInt(256).
Detta gör att bilden ser ut som brus (krypterad). Men om man applicerar filtret igen med
samma parametervärde får man tillbaka bilden intakt (dekrypterad).
7.
Förbättring av kontrasten i bild. Vi inskränker oss här till att förbättra kontrasten i
gråskalebilder. Om man applicerar kontrastfiltrering på en färgbild så kommer bilden att
konverteras till en gråskalebild. (Man kan naturligtvis förbättra kontrasten i en färgbild
och få en färgbild som resultat. Då behandlar man de tre färgkanalerna var för sig.)
Många bilder lider av alltför låg kontrast. Det beror på att bilden inte utnyttjar hela
det tillgängliga området 0–255 för intensiteten. I diagrammet i figur 2 visas antalet pixlar
med olika intensiteter för en bild. Ett sådant diagram kallas ett intensitetshistogram.
Här ser vi att det inte finns några pixlar med intensitet mindre än 45 och bara få pixlar
med intensitet större än 225 (ungefär). Man får en bild med bättre kontrast om man ”töjer
ut” intervallet enligt följande formel (lineär interpolation):
newIntensity = 255 * (intensity - 45) / (225 - 45)
Som synes kommer en punkt med intensiteten 45 att få den nya intensiteten 0 och en
punkt med intensiteten 225 att få den nya intensiteten 255. Mellanliggande punkter sprids
ut jämnt över intervallet [0, 255]. För punkter med en intensitet mindre än 45 sätter man
den nya intensiteten till 0, för punkter med en intensitet större än 225 sätter man den nya
intensiteten till 255.
Vi kallar intervallet där de flesta pixlarna finns för [lowCut, highCut]. De punkter
som har intensitet mindre än lowCut sätter man till 0, de som har intensitet större än
highCut sätter man till 255. För de övriga punkterna interpolerar man med formeln ovan
(45 ersätts med lowCut, 225 med highCut).
Grupplaboration 11 – Matriser, bilbehandling ImageFilter
84
Det återstår nu att hitta lämpliga värden på lowCut och highCut. Detta är inte något
som kan göras helt automatiskt, eftersom värdena beror på intensitetsfördelningen hos
bildpunkterna. Man börjar med att beräkna bildens intensitetshistogram, dvs hur många
punkter i bilden som har intensiteten 0, hur många som har intensiteten 1, . . . , till och
med 255. Detta är ett typiskt registreringsproblem som ska lösas enligt metoden i avsnitt
8.10 i läroboken.
I de flesta bildbehandlingsprogram kan man sedan titta på histogrammet och interaktivt bestämma värdena på lowCut och highCut. Så ska vi dock inte göra här. I stället
bestämmer vi oss för ett procenttal cutOff (som vi matar in i Parameter-rutan i användargränssnittet) och beräknar lowCut så att cutOff procent av punkterna i bilden har en
intensitet som är mindre än lowCut och highCut så att cutOff procent av punkterna har
en intensitet som är större än highCut.
Exempel: antag att en bild innehåller 100 000 pixlar och att cutOff är 1.5. Beräkna bildens intensitetshistogram i en vektor int[] histogram = new int[256]. Beräkna lowCut
så att histogram[0] + histogram[1] + ... + histogram[lowCut] = 0.015 * 100000
(så nära det går att komma, det blir troligen inte exakt likhet). Beräkna highCut på liknande sätt.
Sammanfattning av algoritmen:
1. Beräkna intensiteterna hos alla punkterna i bilden, lagra dem i en short-matris. Använd den färdigskrivna metoden computeIntensity.
2. Beräkna bildens intensitetshistogram.
3. Parametervärdet paramValue är det värde som ska användas som cutOff.
4. Beräkna lowCut och highCut enligt ovan.
5. Beräkna nya intensiteter enligt interpolationsformeln och lagra de nya pixlarna i
outPixels.
Skriv en klass ContrastFilter som implementerar algoritmen. I katalogen images kan
bilden moon.jpg vara lämpliga att testa, eftersom den har låg kontrast.
Anmärkning: om cutOff sätts = 0 så får man samma resultat av denna filtrering som
man får av GrayScaleFilter. Detta kan man se genom att studera interpolationsformeln.
Utjämning av konturer, Gaussfiltrering. Gaussfiltrering är ett exempel på så kallad faltningsfiltrering. (Du kommer att läsa mycket om faltning i kommande kurser i matematik
1200
1000
800
Antal pixlar
8.
600
400
200
0
1
16
31
46
61
76
91 106 121 136 151 166 181 196 211 226 241 256
Intensitet
Figur 2: Intensitetshistogram.
Grupplaboration 11 – Matriser, bilbehandling ImageFilter
85
och signalbehandling.) Filtreringen bygger på att man modifierar varje bildpunkt genom
att titta på punkten och omgivande punkter.
För detta utnyttjar man en så kallad faltningskärna K som är en liten kvadratisk heltalsmatris. Man placerar K över varje element i intensitetsmatrisen och multiplicerar varje
element i K med motsvarande element i intensitetsmatrisen. Man summerar produkterna
och dividerar summan med summan av elementen i K för att få det nya värdet på intensiteten i punkten. Divisionen med summan gör man för att de nya intensiteterna ska
hamna i rätt intervall.
Exempel:




intensity = 



5
4
9
8
..
.
4
3
8
6
..
.
2
4
7
6
..
.
8
9
7
5
..
.
...
...
...
...
..
.









0

K= 1
0
1
4
1

0

1 
0
Här är summan av elementen i K 1 + 1 + 4 + 1 + 1 = 8. För att räkna ut det nya värdet på
intensiteten i punkten med index [1][1] (det nuvarande värdet är 3) beräknar man:
newintensity =
0·5+1·4+0·2+1·4+4·3+1·4+0·9+1·8+0·7
32
=
=4
8
8
Man fortsätter med att flytta K ett steg åt höger och beräknar på motsvarande sätt ett nytt
värde för elementet med index [1][2] (där det nuvarande värdet är 4 och det nya värdet
blir 5). Därefter gör man på samma sätt för alla element utom för ”ramen” dvs elementen
i matrisens ytterkanter.
Skriv en klass GaussFilter som implementerar denna algoritm. Varje färg ska behandlas separat. Gör på följande sätt:
1. Bilda tre short-matriser och lagra pixlarnas red-, green- och blue-komponenter i
matriserna.
2. Utför faltningen av de tre komponenterna för varje element och lagra ett nytt Colorobjekt i outPixels för varje punkt.
3. Elementen i ramen behandlas ju inte, men i outPixels måste också dessa element
få värden. Enklast är att flytta över dessa element oförändrade från inPixels till
outPixels. Man kan också sätta dem till Color.WHITE, men då kommer den filtrerade bilden att se något mindre ut.
I ImageFilter finns en färdigskriven metod med följande rubrik:
protected short convolve(short[][] p, int i, int j,
short[][] kernel, int weight);
Metoden faltar punkten p[i][j] med faltningskärnan kernel och ska anropas med red-,
green- och blue-matrisen. weight är summan av elementen i kernel. Faltningskärnan kan
vara ett attribut i klassen och skapas enklast på följande sätt:
private static short[][] GAUSS_KERNEL =
{ {0, 1, 0},
{1, 4, 1},
{0, 1, 0} };
Grupplaboration 11 – Matriser, bilbehandling ImageFilter
86
svart
tröskelvärde
vitt
Figur 3: En funktion (heldragen linje) och dess derivata (streckad linje).
Det kan vara intressant att prova med andra värden än 4 i mitten av faltningsmatrisen.
Med värdet 0 får man en större utjämning eftersom man då inte alls tar hänsyn till den
aktuella pixelns värde. Mata in detta värde i Parameter-rutan.
Anmärkning: det kan ibland vara svårt att se någon skillnad mellan den filtrerade
bilden och originalbilden. Om man vill ha en riktigt suddig bild så måste man använda
en större matris som faltningskärna.
9.
Markering av konturer, Sobelfiltrering. Med Gaussfiltrering beräknar man medelvärden
och jämnar därför ut en bild. Med Sobelfiltrering, som också är ett exempel på faltningsfiltrering, får man motsatt effekt dvs man förstärker konturer i en bild. I princip deriverar
man bilden i x- och y-led och sammanstället resultatet.
Först en förklaring om varför derivering förstärker konturer. I figur 3 visas en funktion
f (heldragen linje) och funktionens derivata f 0 (streckad linje). Vi ser att där funktionen
gör ett ”hopp” så får derivatan ett stort värde. Om funktionen representerar intensiteten
hos pixlarna längs en linje i x-led eller y-led så motsvarar ”hoppen” en kontur i bilden.
Om man sedan bestämmer sig för att pixlar där derivatans värde överstiger ett visst tröskelvärde ska vara svarta och andra pixlar vita så får man en bild med bara konturer.
Nu är ju intensiteten hos pixlarna inte en kontinuerlig funktion som man kan derivera
enligt vanliga matematiska regler. Men man kan approximera derivatan, till exempel med
följande formel:
f 0 (x) ≈
f ( x + h) − f ( x − h)
2h
(Om man här låter h gå mot noll så får man definitionen av derivatan.) Uttryckt i Java och
matrisen intensity så får man:
derivative = (intensity[i][j+1] - intensity[i][j-1]) / 2
Allt detta kan man uttrycka med hjälp av faltning.
1. Beräkna intensitetsmatrisen med metoden computeIntensity.
2. Falta varje punkt i intensitetsmatrisen med två kärnor:
X_SOBEL = { {-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1} };
Y_SOBEL = { {-1, -2, -1},
{ 0, 0, 0},
{ 1, 2, 1} };
Grupplaboration 11 – Matriser, bilbehandling ImageFilter
87
Använd metoden convolve med vikten 1 (här behöver man inte normera resultatet).
Koefficienterna i matrisen X_SOBEL uttrycker derivering i x-led (ger vertikala konturer), i Y_SOBEL faltning i y-led (ger horisontella konturer). För att förklara varför
koefficienterna ibland är 1 och ibland 2 måste man studera den bakomliggande teorin
noggrant, men det gör vi inte här.
3. Om resultaten av faltningen i en punkt betecknas med sx och sy så får man en indikator på närvaron av en kontur med Math.abs(sx) + Math.abs(sy). Absolutbelopp
behöver man eftersom man har negativa koefficienter i faltningsmatriserna.
4. Sätt pixeln till svart om indikatorn är större än tröskelvärdet, till vit annars. Mata in
tröskelvärdet i Parameter-rutan.
Skriv en klass SobelFilter som implementerar denna algoritm.
89
3
Inlämningsuppgift – anvisningar
Du ska välja en inlämningsuppgift som du ska lösa och redovisa helt själv. Målet med inlämningsuppgiften är att du visa att du kan ska skapa ett större program. Observera att samtliga
laborationer och inlämningsuppgift måste vara godkända innan du får tentera! Följande regler gäller:
1. Inlämningsuppgiften ska lösas individuellt och den ska redovisas på därför avsedd laborationstid.
2. Välj en av de föreslagna inlämningsuppgifterna eller hitta på en egen. Din inlämningsuppgift ska demonstrera att du kan följande:
a) Skriva ett större program själv från grunden; minst ca 500 rader, minst 5 klasser, gärna
mer.
b) Skapa egna klasser som samverkar effektivt för att lösa ett icke-trivialt problem.
c) Använda färdiga klasser tillsammans med dina egna klasser för att lösa ett icketrivialt problem.
d) Skapa, läsa, uppdatera och visa innehållet en datastruktur, till exempel ArrayList.
e) Avlusa och förbättra ditt program stegvis.
3. Senast två veckor innan redovisningen ska du komma överens med en handledare om vad
din inlämningsuppgift ska innehålla.
4. Var förberedd inför presentationen. Du ska vara beredd att svara på frågor kring ditt
program och hur du har tänkt. Du ska även beskriva framväxten av ditt program och hur
du stegvis avlusat och förbättrat implementationen.
5. Det är tillåtet att diskutera uppgifterna och tolkningen av dessa med utomstående på ett
allmänt plan men inte att få hjälp med de konkreta lösningarna.
6. Det är inte tillåtet att kopiera annans lösningar helt eller delvis. Det är inte heller tillåtet
att kopiera från litteratur eller internet.
7. Väsentlig hjälp, av annat än lärare på kursen, för att genomföra en uppgift skall redovisas
tydligt. Detsamma gäller om man använt någon annan form av hjälpmedel som läraren
inte kan förutsättas känna till.
8. Kontakta ansvarig lärare om du är osäker på någon av punkterna ovan.
Inlämningsuppgift alternativ 1 – bankapplikation
90
Inlämningsuppgift alternativ 1 – bankapplikation
Uppgiften är att skriva ett program som håller reda på bankkonton och kunder i en bank.
Programmet är helt textbaserat: en meny skrivs ut till konsollen och menyvalen görs via tangentbordet. Du ska skriva programmet helt själv, och det ska följa de objektorienterade principer
du lärt dig i kursen.
Krav
Kraven för programmet beskrivs nedan. Sist i häftet återfinns också ett antal exempelkörningar,
som illustrerar hur ditt program ska fungera.
För att ditt program ska bli godkänt ska det uppfylla följande krav:
• Programmet ska ha följande funktioner:
1. Hitta konton för en viss kontoinnehavare
2. Sök kontoinnehavare på (del av) namn
3. Sätta in pengar
4. Ta ut pengar
5. Överföring mellan konton
6. Skapa nytt konto
7. Ta bort konto
8. Skriv ut bankens alla konton
9. Avsluta
• Programdesignen ska följa de specifikationer som finns under avsnittet Design nedan.
• Du ska använda en lista (t.ex. ArrayList) för att hålla reda på alla bankkonton.
• Inga utskrifter eller inläsningar (System.out, Scanner och liknande) får lov att finnas i
klasserna Customer, BankAccount eller Bank. Allt som gäller användargränssnitt skall skötas från klassen BankApplication. I BankApplication får du lov att använda valfritt antal
hjälpmetoder eller andra hjälpklasser för att sköta användargränssnittet. (Det är vanligt
att man begränsar användargränssnittet till vissa klasser på detta sätt. På så sätt kan klasserna Customer, BankAccount och Bank återanvändas i ett annat program med ett annat
användargränssnitt.)
• Hela projektet får bara innehålla ett Scanner-objekt för att läsa från tangentbordet (d.v.s
man får bara göra new Scanner(System.in) en gång).
• Klasserna Customer, BankAccount och Bank ska exakt följa specifikationerna som anges
under avsnittet Design nedan. Det är tillåtet att lägga till privata hjälpmetoder. (Det är inte
ovanligt att en klass, för att fungera ihop med andra klasser, måste se ut på ett visst sätt –
följa en viss specifikation – trots att man implementerar funktionaliteten själv.)
• Alla metoder och attribut ska ha lämpliga åtkomsträttigheter (private/public).
• De indata som ges i exemplet nedan ska ge väsentligen samma utskrift som exemplen i
bilagan. Du får gärna byta ordval, men ditt program ska i allt väsentligt fungera likadant.
• Listor ska skrivas ut genom att iterera över listans objekt och anropa respektive toString
metod. Det är inte ok att använda listans toString-metod direkt då ni då inte kan få önskvärt format på utskriften.
Inlämningsuppgift alternativ 1 – bankapplikation
91
• Du behöver inte hantera Exceptions men övrig rimlig felhantering är önskvärd. Exempelvis: Programmet bör inte krascha om användaren matar in oväntade värden, exempelvis
kontonummer eller menyval som inte finns, men det är inte nödvändigt att hantera indata
av fel typ (exempelvis att användaren matar in bokstäver då programmet förväntar sig ett
heltal).
Tips och övriga anvisningar
• I vissa lösningar kan Scannern tyckas bete sig konstigt vid inläsning av omväxlande tal
och strängar. Detta kan exempelvis visa sig i att inlästa strängar blir tomma.
Det finns olika sätt att lösa detta. Konkreta tips utdelas på en föreläsning i samband med
inlämningsuppgiften. Du kan naturligtvis också fråga någon av handledarna.
• Tänk på val av attribut i klasserna. En variabel som bara används i en metod behöver inte
vara åtkomligt i hela objektet och ska således ej vara attribut. Fråga en handledare om du
är osäker.
• Programmet ska testas noggrant. Tänk på att den som rättar kan mycket väl testa på helt
andra fall än de som förekommer i exemplen.
Frivillig utökning av uppgiften
Gör först klart inlämningsuppgiftens obligatoriska delar. Därefter kan du, om du vill, utöka ditt
program enligt följande.
• Då man söker kunder utifrån en del av deras namn (menyval 2) kan samma kund förekomma flera gånger i resultatet. (Att det är samma kund ser man på att inte bara namn
och personnummer är detsamma, utan även kundnumret.) Detta är naturligtvis inte helt
önskvärt. Utöka gärna ditt program så att varje kund bara förekommer högst en gång i
utskriften från menyval 2.
• En annan utökning, som även kan underlätta testningen av programmet, är att implementera läsning från och skrivning till fil och på så vis ge möjlighet att spara kund- och
konto-informationen mellan körningarna.
Design
Följande klass ska användas för att lagra grundläggande information om en kontoinnehavare:
Customer
/**
* Skapar en kund (kontoinnehavare) med namnet ’name’ och id-nummer ’idNr’.
* Kunden tilldelas också ett unikt kundnummer.
*/
Customer(String name, long idNr);
/** Tar reda på kundens namn. */
String getName();
/** Tar reda på kundens personnummer. */
long getIdNr();
/** Tar reda på kundens kundnummer. */
int getCustomerNr();
/** Returnerar en strängbeskrivning av kunden. */
Inlämningsuppgift alternativ 1 – bankapplikation
92
String toString();
Följande klass ska användas för att lagra information om ett bankkonto:
BankAccount
/**
* Skapar ett nytt bankkonto åt en innehavare med namn ’holderName’ och
* id ’holderId’. Kontot tilldelas ett unikt kontonummer och innehåller
* inledningsvis 0 kr.
*/
BankAccount(String holderName, long holderId);
/**
* Skapar ett nytt bankkonto med innehavare ’holder’. Kontot tilldelas
* ett unikt kontonummer och innehåller inledningsvis 0 kr.
*/
BankAccount(Customer holder);
/** Tar reda på kontots innehavare. */
Customer getHolder();
/** Tar reda på det kontonummer som identifierar detta konto. */
int getAccountNumber();
/** Tar reda på hur mycket pengar som finns på kontot. */
double getAmount();
/** Sätter in beloppet ’amount’ på kontot. */
void deposit(double amount);
/**
* Tar ut beloppet ’amount’ från kontot. Om kontot saknar täckning
* blir saldot negativt.
*/
void withdraw(double amount);
/** Returnerar en strängrepresentation av bankkontot. */
String toString();
Följande klass ska användas som register för att administrera bankens samtliga konton:
Bank
/** Skapar en ny bank utan konton. */
Bank();
/**
* Öppna ett nytt konto i banken. Om det redan finns en kontoinnehavare
* med de givna uppgifterna ska inte en ny Customer skapas, utan istället
* den befintliga användas. Det nya kontonumret returneras.
*/
int addAccount(String holderName, long idNr);
/**
* Returnerar den kontoinnehavaren som har det givna id-numret,
* eller null om ingen sådan finns.
*/
Customer findHolder(long idNr);
/**
Inlämningsuppgift alternativ 1 – bankapplikation
93
* Tar bort konto med nummer ’number’ från banken. Returnerar true om
* kontot fanns (och kunde tas bort), annars false.
*/
boolean removeAccount(int number);
/**
* Returnerar en lista innehållande samtliga bankkonton i banken.
* Listan är sorterad på kontoinnehavarnas namn.
*/
ArrayList<BankAccount> getAllAccounts();
/**
* Söker upp och returnerar bankkontot med kontonummer ’accountNumber’.
* Returnerar null om inget sådant konto finns.
*/
BankAccount findByNumber(int accountNumber);
/**
* Söker upp alla bankkonton som innehas av kunden med id-nummer ’idNr’.
* Kontona returneras i en lista. Kunderna antas ha unika id-nummer.
*/
ArrayList<BankAccount> findAccountsForHolder(long idNr);
/**
* Söker upp kunder utifrån en sökning på namn eller del av namn. Alla
* personer vars namn innehåller strängen ’namePart’ inkluderas i
* resultatet, som returneras som en lista. Samma person kan förekomma
* flera gånger i resultatet. Sökningen är "case insensitive", det vill
* säga gör ingen skillnad på stora och små bokstäver.
*/
ArrayList<Customer> findByPartofName(String namePart);
Implementation
Implementationen görs lämpligen stegvis, genom att du implementerar några metoder i taget
och därefter provkör resultatet. Därefter implementerar och provkör du fler metoder. Förslagsvis
följer du dessa steg i ditt arbete:
1. Skapa projektet i Eclipse.
2. Implementera klassen Customer.
3. Implementera klassen BankAccount.
4. Skapa klassen BankApplication, som ska innehålla programmets main-metod.
Det är lämpligt att införa ytterligare metoder i BankApplication: exempelvis en metod för
att driva hela programmet (t. ex. runApplication) som i sin tur anropar andra metoder
beroende på vad som ska göras. Ha gärna en separat metod för att skriva ut menyn.
Implementera tillräckligt för att kunna köra igång programmet, skriva ut menyn och låta
användaren välja i den.
5. Implementera menyval 6 och 8, som motsvarar metoderna addAccount och getAllAccounts
i klassen Bank. Återigen kan du införa metoder i BankApplication, exempelvis för att läsa
in data och anropa Bank med dessa.
Observera din sortering i getAllAccounts inte ska använda någon av Javas standardklasser
för sortering, som t. ex. Collections.sort eller Arrays.sort.
Inlämningsuppgift alternativ 1 – bankapplikation
94
Observera också återigen att det inte är tillåtet att använda Scanner och System.out i
klassen Bank.
6. Testa menyval 6 och 8 noga.
Tips: under testningen kan det bli enformigt att mata in uppgifter om konton om och om
igen. Du kan lägga in satser i ditt program som anropar addAccount ett antal gånger, så
att ditt program startar med ett antal färdiga testkonton. Låt dessa satser skapa kontona
i icke-alfabetisk ordning (med avseende på kontoinnehavarnas namn), så att du ser att
sorteringen i getAllAccounts fungerar.
7. Implementera menyvalen 1 och 2. De motsvaras av metoderna findAccountsForHolder
och findByPartOfName i klassen Bank.
8. Lägg till några konton (om du inte införde testkonton enligt punkt 6 ovan) och testa menyvalen 1 och 2.
9. Implementera resten av menyvalen en åt gången. Testa efter varje.
10. Testa ditt program som helhet. Om du lade in satser för att automatiskt skapa testkonton
(under punkt 6 ovan, ta bort dem.
Kör först igång programmet utan att skapa några konton, och kontrollera att menyns
alternativ fungerar även utan konton. Skapa därefter ett antal konton och kontrollera att
programmets delar fungerar som förväntat.
Glöm inte testa med orimliga indata och kontrollera att programmet hanterar dessa på ett
rimligt sätt. Gå tillbaka och åtgärda de fel du upptäcker.
Redovisning
När du gått igenom alla moment ovan, och testat noga, är det dags att redovisa. Förbered dig
inför redovisningen. Om du hunnit glömma vad du gjort så repetera innan redovisningen – du
förväntas kunna förklara ditt program!
Exempel på körning av programmet
Här visas en möjlig exempelkörning av programmet. Data som matas in av användaren visas
i fetstil. Ditt program behöver inte efterlikna dessa utskrifter i exakta detaljer, men den övergripande strukturen ska vara densamma. De data som matas in av användaren har markerats i
fetstil.
Ett antal konton skapas, och pengar sätts in på ett par av dem. Ett stort belopp överförs
mellan två konton. Slutligen tas de flesta kontona bort, och endast ett finns kvar då programmet
avslutas.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 6
namn: Charles Ingvar Jönsson
id: 3705209999
konto skapat: 1001
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
Inlämningsuppgift alternativ 1 – bankapplikation
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 6
namn: Ragnar Vanheden
id: 4111195555
konto skapat: 1002
- - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 6
namn: Jakob Morgan Rockefeller Wall-Enberg Jr
id: 2306207777
konto skapat: 1003
- - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 6
namn: Jakob Morgan Rockefeller Wall-Enberg Jr
id: 2306207777
konto skapat: 1004
- - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 2
namn: ROCK
Jakob Morgan Rockefeller Wall-Enberg Jr, id 2306207777,
Jakob Morgan Rockefeller Wall-Enberg Jr, id 2306207777,
- - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 2
namn: ar
Charles Ingvar Jönsson, id 3705209999, kundnr 101
Ragnar Vanheden, id 4111195555, kundnr 102
- - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
95
- - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
kundnr 103
kundnr 103
- - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
96
Inlämningsuppgift alternativ 1 – bankapplikation
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 3
konto: 1003
belopp: 1000000.0
konto 1003 (Jakob Morgan Rockefeller Wall-Enberg Jr, id 2306207777, kundnr 103): 1000000.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 3
konto: 1001
belopp: 50.0
konto 1001 (Charles Ingvar Jönsson, id 3705209999, kundnr 101): 50.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 4
från konto: 1001
belopp: 900.0
uttaget misslyckades, endast 50.0 på kontot!
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 5
från konto: 1003
till konto: 1001
belopp: 500000.0
konto 1003 (Jakob Morgan Rockefeller Wall-Enberg Jr, id 2306207777, kundnr 103): 500000.0
konto 1001 (Charles Ingvar Jönsson, id 3705209999, kundnr 101): 500050.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 4
från konto: 1001
belopp: 900.0
konto 1001 (Charles Ingvar Jönsson, id 3705209999, kundnr 101): 499150.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
Inlämningsuppgift alternativ 1 – bankapplikation
97
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 1
id: 2306207777
konto 1003 (Jakob Morgan Rockefeller Wall-Enberg Jr, id 2306207777, kundnr 103): 500000.0
konto 1004 (Jakob Morgan Rockefeller Wall-Enberg Jr, id 2306207777, kundnr 103): 0.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 8
konto 1001 (Charles Ingvar Jönsson, id 3705209999, kundnr 101): 499150.0
konto 1003 (Jakob Morgan Rockefeller Wall-Enberg Jr, id 2306207777, kundnr 103): 500000.0
konto 1004 (Jakob Morgan Rockefeller Wall-Enberg Jr, id 2306207777, kundnr 103): 0.0
konto 1002 (Ragnar Vanheden, id 4111195555, kundnr 102): 0.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 7
konto: 1001
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 7
konto: 1003
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 7
konto: 1004
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 7
98
Inlämningsuppgift alternativ 1 – bankapplikation
konto: 87654
felaktigt kontonummer!
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 8
konto 1002 (Ragnar Vanheden, id 4111195555, kundnr 102): 0.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1. Hitta konto utifrån innehavare
2. Sök kontoinnehavare utifrån (del av) namn
3. Sätt in
4. Ta ut
5. Överföring
6. Skapa konto
7. Ta bort konto
8. Skriv ut konton
9. Avsluta
val: 9
Inlämningsuppgift alternativ 2 – Mandelbrot
99
Inlämningsuppgift alternativ 2 – Mandelbrot
I denna uppgift lära du dig att implementera algoritmer med numeriska beräkningar.
Bakgrund
Den fransk-amerikanske matematikern Benoît Mandelbrot (född i Polen) införde år 1975 ett
nytt matematiskt begrepp, fraktal. Fraktaler har anknytning till den relativt unga matematiska
vetenskapen kaosteori. En fraktal är en geometrisk figur med ovanliga egenskaper, till exempel
är den sådan att mönster i figuren upprepas i det oändliga när man förstorar olika delar av den.
Ett välkänt exempel på en fraktal är Mandelbrotmängden M, som är en delmängd av de
komplexa talen C. Den innehåller ett oändligt antal punkter men har en begränsad utsträckning
i det komplexa talplanet. Man kan undersöka om ett komplext tal c tillhör M på följande sätt:
• Definiera en talföljd (Mandelbrotföljden):
(
zk =
0,
z2k−1 + c,
k=0
k = 1, 2, . . .
• z0 är 0, så z1 blir c. Nästa z-värde blir z2 = c2 + c, nästa igen blir z3 = c4 + 2c3 + c2 + c, och
så vidare.
• Följden z0 , z1 , z2 , . . . kan bete sig på tre sätt:
1. Den kan konvergera mot en punkt i det komplexa talplanet. Till exempel ger c = i/2
en följd som konvergerar mot −0.136009 + 0.393075 i.
2. Den kan ”konvergera” mot två eller flera punkter som upprepas periodiskt. Till exempel ger c = i en följd som omväxlande antar värdena −1 + i och −i.
3. Den kan divergera mot oändligheten. Detta gäller till exempel för c = 2.
Definition: Ett komplext tal c tillhör Mandelbrotmängden M om följden av komplexa tal inte
divergerar, det vill säga om något av fallen 1 eller 2 ovan gäller.
Om man i ett datorprogram ska undersöka om en punkt tillhör Mandelbrotmängden så kan
man inte använda definitionen direkt — för att kunna avgöra om en följd konvergerar eller
divergerar skulle ju man behöva iterera oändligt länge. I ett program nöjer man sig därför med
att göra ett begränsat antal iterationer och därefter använda följande sats:
Om det för något k gäller att |zk | > 2 så divergerar Mandelbrotföljden.
Uppgift
Du ska skriva ett program som beräknar Mandelbrotmängden och åskådliggör punkterna i
mängden i ett komplext talplan. Programmets användargränssnitt är färdigskrivet. Det är en
god idé att följa den arbetsordning som beskrivs nedan.
Börja med att skapa ett projekt inl2 för filerna i uppgiften.
1 Komplexa tal
I uppgiften behövs en klass som beskriver komplexa variabler. Du behöver bara implementera
de operationer på komplexa tal som kommer att behövas i detta program. Skriv alltså en klass
Complex med följande specifikation:
Inlämningsuppgift alternativ 2 – Mandelbrot
100
/** Skapar en komplex variabel med realdelen re och imaginärdelen im */
Complex(double re, double im);
/** Tar reda på realdelen */
double getRe();
/** Tar reda på imaginärdelen */
double getIm();
/** Tar reda på talets absolutbelopp i kvadrat */
double getAbs2();
/** Adderar det komplexa talet c till detta tal */
void add(Complex c);
/** Multiplicerar detta tal med det komplexa talet c */
void mul(Complex c);
Skriv ett testprogram som kontrollerar att din Complex-klass fungerar. Metoderna getRe och
getIm kommer du bara att använda i testprogrammet och när du ritar en cirkel (moment 3).
Anledningen till att klassen bara har en operation för att beräkna absolutbeloppet i kvadrat, inte
för absolutbeloppet, är att man vill slippa en tidsödande kvadratrotsberäkning.
Eftersom programmet vid varje exekvering kommer att göra flera miljoner beräkningar med
Complex-objekt är det viktigt att räkneoperationerna implementeras effektivt. Detta innebär att
operationerna ska implementeras i rektangulära koordinater, som inte kräver några tidsödande
beräkningar av trigonometriska funktioner.
Var försiktig när du implementerar operationen mul så att du under beräkningarna inte råkar
förstöra ett värde som du senare behöver. Tänk särskilt på fallet z.mul(z).
2 Orientering om användargränssnittet
När du senare har genererat Mandelbrotmängden ska du visa en bild av mängden i ett fönster.
Användargränssnittet är färdigskrivet (klassen se.lth.cs.ptdc.fractal.MandelbrotGUI). En
länk till specifikationen av klassen finns på kursens hemsida. Studera specifikationen noggrant
medan du läser vidare. Börja med att skriva en klass Mandelbrot med en main-metod som skapar
ett objekt av MandelbrotGUI (använd konstruktorn utan parametrar). Kompilera och testa!
I fönstret visas ett komplext talplan och fyra rutor som visar intervallen på planets koordinataxlar. Man kan förstora (”zooma in”) en del av planet genom att med musen dra ut en
rektangel. Testa att detta fungerar, dvs att min- och maxvärdena på koordinataxlarna ändras när
du zoomar.
Överst i fönstret finns några knappar och menyer. Som du märker fungerar de ännu inte
(avsikten är förstås att du ska implementera deras funktion). Exempelvis går det inte att avsluta
programmet med Quit-knappen.
Det finns också en textruta märkt Extra där du kan mata in vilka data du vill och hämta dem
till programmet. Se avsnitt 7.
Din main-metod ska kommunicera med användargränssnittet genom att fråga efter kommandon som användaren ger genom att trycka på knapparna. I MandelbrotGUI finns en metod
getCommand som returnerar ett heltal som är numret på ett kommando. Vidare finns ett antal
publika konstanter som är numren på kommandona (RENDER, RESET, osv).
Lägg i main-metoden in en repetitionssats där du frågar efter ett kommando och därefter gör
olika saker beroende vad som returneras. Använd en switch-sats — förbered med case-grenar
för varje kommando men gör tills vidare bara en utskrift som identifierar kommandot. Testa! Ta
bort utskrifterna efterhand som du implementerar de olika kommandona.
Inlämningsuppgift alternativ 2 – Mandelbrot
0
101
maxIm
width – 1
0
minRe
maxRe
height – 1
minIm
Figur 4: Ritytans koordinatsystem och det komplexa koordinatsystemet.
Implementera slutligen kommandona RESET och QUIT. RESET ska medföra att axlarnas minoch maxvärden återställs till de ursprungliga och att ritytan töms. QUIT ska medföra att programmet avslutas med System.exit(0).
Kontrollera att du nu kan avsluta programmet genom att trycka på QUIT-knappen. Testa
också RESET-funktionen.
3 Rita en cirkel (det svåraste problemet)
I denna deluppgift ska du börja skriva en klass Generator som ska beräkna punkterna i Mandelbrotmängden och visa dem i fönstret. Det gäller att för varje punkt c i det komplexa talplanet
(den del som syns i fönstret) avgöra om punkten tillhör Mandelbrotmängden eller inte. Om c
tillhör mängden färgas motsvarande pixel svart (java.awt.Color.BLACK), annars färgas pixeln
vit (java.awt.Color.WHITE).
Till att börja med så nöjer vi oss dock med att rita enklare bilder än fraktaler, för att vi ska få
ordning på de olika koordinatsystem som är inblandade i problemet. Man ska också kunna rita
bilder med olika upplösning; det väntar vi också med.
I uppgiften har vi att göra med två koordinatsystem. Det första systemet är det komplexa
talplanet. I ritfönstret visas en del av detta: från början visas intervallet [−2.4, 0.8] av den reella axeln och intervallet [−1.4, 1.4] av den imaginära axeln. Användaren kan ändra intervallen
genom att zooma med musen. Det finns operationer i MandelbrotGUI för att ta reda på intervallgränserna: getMinimumReal, getMaximumReal, getMinimumImag och getMaximumImag.
Det andra koordinatsystemet är ritytans system (detta kallas i fortsättningen för ritsystemet).
Detta system har som vanligt en bredd och en höjd räknat i pixlar. Det finns operationer för att
ta reda på dessa värden (getWidth och getHeight). Ritsystemet har origo i övre vänstra hörnet
av ritytan och y-axeln är riktad nedåt.
Varje pixel i ritsystemet motsvaras alltså av ett komplext tal. Till exempel motsvaras punkten
med index 0, 0 i ritsystemet av ett komplext tal med realdelen getMinimumReal() och imaginärdelen getMaximumImag().
I figur 4 visas ritytan. Den har bredden width pixlar och höjden height pixlar. minRe och
maxRe är den reella axelns gränser, minIm och maxIm är den imaginära axelns gränser. Lägg
märke till att y-axeln i de båda koordinatsystemen pekar i olika riktningar.
Inlämningsuppgift alternativ 2 – Mandelbrot
102
Klassen Generator ska ha följande metoder:
/** Ritar en bild i fönstret i användargränssnittet gui */
public void render(MandelbrotGUI gui);
/** Skapar en matris där varje element är ett komplext tal som
har rätt koordinater (se beskrivning nedan) */
private Complex[][] mesh(double minRe, double maxRe,
double minIm, double maxIm,
int width, int height);
Anvisningar för mesh. Matrisen som returneras ska ha storleken height rader och width kolonner och ska användas för att till varje bildelement associera rätt komplext tal. Tänk dig alltså att
matrisen läggs ovanpå ritytan.
Fyll i rätt komplext tal i varje element i matrisen. Elementet med index [0][0] ska ha realdelen minRe och imaginärdelen maxIm, elementet med index [height - 1][width - 1] ska ha
realdelen maxRe och imaginärdelen minIm. Kontrollera dina formler så att de stämmer.
Anvisningar för render. I denna metod ska du göra följande:
1. Anropa disableInput i MandelbrotGUI. Anropet medför att knapparna i användargränssnittet inte reagerar på tryck och att beräkningarna inte störs av att användaren gör operationer i fönstret.
2. Anropa mesh för att skapa matrisen med komplexa tal. Kalla matrisen complex.5
3. Det behövs ytterligare en matris som ska innehålla bildpunkterna som ritas. Deklarera
matrisen med Color[][] picture. Matrisen ska vara lika stor som ritytan (detta kommer
att ändras senare, när du ska rita bilder med olika upplösning).
4. Gå igenom matrisen picture rad för rad och fyll i en färg på varje plats. Färgen väljs
enligt följande: om complex[i][k] har absolutbeloppet > 1 så sätts picture[i][k] till
Color.WHITE, annars väljs en unik färg för varje kvadrant av planet. Använd till exempel
Color.RED till den första kvadranten, Color.BLUE till den andra, osv. Resultatet ska bli att
en cirkel med fyra färger ritas.
5. När varje element har fått en färg ska bilden ritas med putData i MandelbrotGUI.
6. Anropa till sist enableInput för att återställa funktionen hos användargränssnittets knappar.
Komplettera sedan din main-metod genom att implementera kommandot RENDER i switch-satsen
med ett anrop till render-metoden. Testa!
Som du ser är det är en hel del beräkningar att reda ut innan man kan skriva in och testa
någon kod. Du sparar många felsökningstimmar vid datorn om du planerar noggrant med
papper och penna.
Om bilden inte ritas upp rätt så beror det oftast på att man räknat fel i mesh. Skriv då om
och testa igen — det är ingen idé att gå vidare förrän detta fungerar.
4 Zoom-funktionen
Du ska nu återgå till att skriva kod i main-metoden i Mandelbrot. Implementera ZOOM så att
zoomning medför att bilden genereras om i det nya området. Detta ska dock bara göras om det
5
Man kan klara sig utan matrisen complex om man beräknar realdel och imaginärdel för rätt komplext tal när man
behöver talet, dvs när man ska generera en bildpunkt. Beskrivningen i fortsättningen blir dock betydligt enklare om
man använder sig av matrisen.
Inlämningsuppgift alternativ 2 – Mandelbrot
103
Bildmatrisen picture
Komplexmatrisen complex
Välj det mittersta komplexa
talet för varje bildelement
Figur 5: Medium upplösning — varje bildelement innehåller 5 × 5 pixlar.
redan finns en bild uppritad i fönstret. Om ritytan är tom, som den är när programmet nyss har
startats eller efter en RESET-operation, ska inte render anropas. Testa!
5 Rita med olika upplösning
Användaren kan i en meny välja upplösning på den resulterande bilden i fem nivåer, från
mycket låg upplösning till mycket hög. Vid mycket hög upplösning ska varje bildelement ha
bredden 1 och höjden 1, vilket ger att matriserna complex och picture har samma storlek (det
är denna upplösning vi använt i föregående avsnitt). Vid lägre upplösning ska bildelementen
vara större, enligt följande tabell:
Resolution
Pixel width
Pixel height
Very high
High
Medium
Low
Very low
1
3
5
7
9
1
3
5
7
9
Bildmatrisen ska nu vara mindre än komplexmatrisen, på så sätt att man delar antalet rader och
kolonner med de värden som finns i tabellen för en viss upplösning. Detta ger en mindre bildmatris och snabbare beräkningar. För varje bildelement ska man nu välja det mittersta komplexa
talet — se figur 5, där upplösningen är Medium.
Ändra renderingsmetoden så att den tar hänsyn till den upplösning som användaren har
valt. Provkör programmet och kontrollera att det fungerar — cirkelns periferi ska bli taggig i
varierande grad beroende på upplösningen.
Du måste noggrant testa så att programmet fungerar med alla upplösningar. Felaktiga formler leder här ofta till att man råkar indexera utanför den ena eller den andra matrisens gränser,
vilket naturligtvis inte är acceptabelt.
6 Rita Mandelbrotmängden
Nu ska du (äntligen) rita en bild av Mandelbrotmängden i stället för en cirkel. För vart och ett
av c-värdena i bildelementens mittpunkter ska du beräkna en följd av komplexa tal med Mandelbrotformeln och med ledning av den sats som presenterades bestämma om punkten tillhör
Inlämningsuppgift alternativ 2 – Mandelbrot
104
Mandelbrotmängden M eller inte. Använd förslagsvis högst 200 iterationer. Om punkten tillhör
M sätter du bildmatrisens motsvarande element till Color.BLACK, annars till Color.WHITE. Gör
inte fler iterationer än nödvändigt!
7 Avslutande förbättringar
Du ska förbättra din Generator-klass så att den uppfyller följande krav:
• Rita med färg om användaren har begärt det. I stället för att använda vit färg på alla
divergerande punkter, låt dem få en annan färg som svarar mot ”hur snabbt” de divergerar.
Av effektivitetsskäl bör man inte skapa nya färgobjekt varje gång man ska sätta en färg på
en punkt. Det kan ju inte bli fråga om att använda fler än högst så många färger som man
har iterationer, så dessa färger bör man skapa en gång och lägga i en vektor (lämpligen i
konstruktorn i Generator). Jämför med vektorn grayLevels i deluppgift 5 i lab 11.
• Valfritt: utnyttja Extra-rutan i användargränssnittet för att mata in data till programmet,
till exempel antalet iterationer som ska göras i Mandelbrotföljden (när man har zoomat
in på fina detaljer i bilden är det bra att använda många iterationer), eller för att tala om
vilken färgskala som ska användas, . . .
Inlämningsuppgift alternativ 3 – Draw
105
Inlämningsuppgift alternativ 3 – Draw
I denna uppgift skapar du en enkel ritapplikation.
Bakgrund
I ritprogram, till exempel xfig, kan man rita figurer som man sedan kan behandla på olika sätt.
Man kan till exempel ändra storlek på figurerna, flytta dem och ta bort dem.
Här beskrivs ett extremt enkelt ritprogram. Det finns bara tre slags figurer: kvadrater, liksidiga trianglar och cirklar. Det enda användaren kan göra är att flytta omkring figurerna i ritfönstret
genom att först klicka på en figur och sedan klicka på den nya positionen.
Ritfönstret har följande utseende:
I ritprogrammet beskrivs de gemensamma egenskaperna hos figurerna i en klass Shape. Denna
klass används som superklass på klasserna som beskriver specifika figurer (kvadrat, triangel och
cirkel).
public abstract class Shape {
protected int x;
protected int y;
/** Skapar en figur med läget x,y */
protected Shape(int x, int y) {
this.x = x;
this.y = y;
}
/** Ritar upp figuren i fönstret w */
public abstract void draw(SimpleWindow w);
/** Raderar bilden av figuren, flyttar figuren till newX,newY
och ritar upp den på sin nya plats i fönstret w */
public void moveToAndDraw(SimpleWindow w, int newX, int newY) {
java.awt.Color savedColor = w.getLineColor();
w.setLineColor(java.awt.Color.WHITE);
draw(w);
x = newX;
y = newY;
w.setLineColor(savedColor);
draw(w);
}
Inlämningsuppgift alternativ 3 – Draw
106
/** Undersöker om punkten xc,yc ligger "nära" figuren */
public boolean near(int xc, int yc) {
return Math.abs(x - xc) < 10 && Math.abs(y - yc) < 10;
}
}
I programmet håller man reda på figurerna som man skapar genom att man lägger in dem i
en lista (ett objekt av klassen ShapeList). I följande program skapar man fem figurer, lägger in
dem i listan och ritar upp dem:
import se.lth.cs.window.SimpleWindow;
import se.lth.cs.ptdc.shapes.ShapeList;
public class ShapeTest {
public static void main(String[] args) {
SimpleWindow w = new SimpleWindow(600, 600, "ShapeTest");
ShapeList shapes = new ShapeList();
shapes.insert(new Square(100, 300, 100));
shapes.insert(new Triangle(400, 200, 100));
shapes.insert(new Circle(400, 400, 50));
shapes.insert(new Square(450, 450, 50));
shapes.insert(new Square(200, 200, 35));
shapes.draw(w);
}
}
Parametrarna till konstruktorerna är figurens läge (x och y) och storlek (sidlängd eller radie).
Klassen ShapeList har följande specifikation:
/** Skapar en tom lista */
ShapeList();
/** Lägger in figuren s i listan */
void insert(Shape s);
/** Ritar upp figurerna i listan i fönstret w */
void draw(SimpleWindow w);
/** Tar reda på en figur som ligger nära punkten xc,yc; ger null om
ingen sådan figur finns i listan */
Shape findHit(int xc, int yc):
Operationen findHit används för att ta reda på vilken figur som användaren pekat och klickat
på. Detta hanteras av klassen CommandDispatcher, som ansvarar för programmets kommunikation med användaren. Klassen har följande uppbyggnad:
import se.lth.cs.window.SimpleWindow;
import se.lth.cs.ptdc.shapes.Shape;
import se.lth.cs.ptdc.shapes.ShapeList;
class CommandDispatcher {
private SimpleWindow w;
private ShapeList shapes;
public CommandDispatcher(SimpleWindow w, ShapeList shapes) {
this.w = w;
this.shapes = shapes;
Inlämningsuppgift alternativ 3 – Draw
107
}
public void mainLoop() {
while (true) {
// användaren klickar på en figur
// användaren klickar på en ny position
// figuren flyttas till den nya positionen
}
}
}
Uppgifter
Råd: i nedanstående uppgifter är det lämpligt att börja med uppgift 1 till 3 men bara hantera kvadrater, så att man ser att allt fungerar. Komplettera sedan programmet med cirklar och
trianglar.
1.
Klassen Shape finns färdigskriven (se.lth.cs.ptdc.shapes.Shape). Skriv tre subklasser
till Shape: Square, Triangle och Circle. Lägg klasserna i separata filer: (Square.java,
Triangle.java, Circle.java). I subklasserna måste du definiera vad som avses med ”läget”
hos en figur. Koordinaterna x och y kan för en kvadrat ange övre vänstra hörnet, för
en triangel nedre vänstra hörnet, för en cirkel medelpunkten. Man ska kunna bestämma
figurens storlek med en parameter till konstruktorn.
När man ska rita en cirkel är det enklast att tänka sig cirkeln som en regelbunden
månghörning med många hörn. I lösningen till en av övningsuppgifterna i kapitel 9 i
läroboken finns en metod som ritar en cirkel.
2.
Klassen ShapeTest finns i filen ShapeTest.java. I klassen utnyttjas en färdigskriven version av klassen ShapeList. Tag bort kommentartecknen på insert-satserna och testkör
programmet.
3.
Skriv in klassen CommandDispatcher. Modifiera också main-metoden i ShapeTest så att
ett CommandDispatcher-objekt skapas och operationen mainLoop utförs. Testkör det nya
programmet.
4.
Implementera klassen ShapeList med hjälp av en ArrayList<Shape> (ett programskelett
finns i ShapeList.java). Testa programmet med din version av ShapeList. För att göra detta
måste du ta bort import-satsen där ShapeList importeras från filerna ShapeTest.java och
CommandDispatcher.java. Javasystemet kommer då att hitta den ShapeList-klass som
finns i den aktuella katalogen, det vill säga din egen klass.
5.
Ändra programmet så att man inte behöver använda insert-satser i main-metoden för att
lägga in figurer i listan. I stället ska uppgifter om figurerna som ska skapas läsas från en
fil. I projektkatalogen finns en fil shapedata.txt med följande innehåll (filen specificerar
samma figurer som i det tidigare exemplet):
S
T
C
S
S
100
400
400
450
200
300
200
400
450
200
100
100
50
50
35
Inlämningsuppgift alternativ 3 – Draw
108
6.
När man ska välja ut en figur att flytta känns det inte naturligt att man måste klicka på
ett bestämt hörn (kvadrat och triangel) eller medelpunkten (cirkel). Det hade varit bättre
om man kunde klicka på (eller nära) någon av figurens linjer. Detta kan man åstadkomma
genom att skriva olika near-funktioner för de olika figurtyperna.
Ändra ditt program så att fungerar på det beskrivna sättet. Det är lättast att skriva
near-metoden i klassen Circle, så det är lämpligt att börja med den.
Tips för kvadrater och trianglar: skriv en metod som undersöker om en punkt ligger
nära en linje och anropa den med kvadratens fyra linjer och triangelns tre linjer. När man
ska undersöka som en punkt ligger nära en sned linje är det enklast att räkna ut punktens
avstånd till linjens båda ändpunkter och jämföra summan av dessa avstånd med linjens
längd:
xc,yc
Utvidgningar
Du ska nu förbättra ditt program med valfria utvidgningar. Diskutera i förväg med en
handledare på vilket sätt du vill gå vidare. Välj att utvidga programmet med funktioner som du tycker är intressanta att implementera och som samtidigt gör att du tränar
på sådant du behöver extra träning på. Nedanstående uppgifter är förslag på lämpliga
utvidgningar:
7.
Lägg till fler geometriska former i ditt program, till exempel rektangel och polygon.
8.
Gör så att det går att byta konturfärg på vald figur genom att trycka på tangenterna R för
rött, G för grönt, B för blått, och S för svart.
9.
Gör så att figurerna flyttas med en animering: istället för att direkt rita ut figuren på sin
nya plats, rita ut den på flera platser på vägen med en kort fördröjnig emellan så det ser
ut som om den glider mjukt till sin nya plats.
10.
Gör så att det går att flytta vald figur med piltangenterna.
11.
Gör så att programmet sparar figurerna på filen, så att figurerna hade sina nya lägen nästa
gång man körde programmet.
Eclipse – en handledning
4
109
Eclipse – en handledning
Inledning
Här beskrivs Eclipse, den programutvecklingsmiljö som utnyttjas i programmeringskurserna.
Mera information finns på:
http://www.eclipse.org
http://help.eclipse.org
http://www.eclipse.org/downloads/
Eclipse hemsida. Enorma mängder information.
Hjälpsidor för Eclipse, senaste versionen. Dessa
hjälpsidor finns också lokalt i Eclipse-systemet,
under Help Contents i Help-menyn.
Nedladdning av Eclipse för olika datorer. Välj
”Eclipse IDE for Java Developers”.
Det finns också många handledningar som är betydligt utförligare än denna, till exempel på
www.vogella.com/articles/Eclipse/article.html.
Att utveckla Javaprogram
Ett Javaprogram består av en eller flera klasser som lagras i filer med tillägget .java. Dessa filer
innehåller ”vanlig text” (bokstäver, siffror och andra tecken). Innan man kan exekvera (köra)
programmet måste filerna kompileras (översättas) till ett annat format (bytekod) som datorn
”förstår”. Bytekoden lagras i filer med tillägget .class. Översättningen görs av en kompilator
som heter javac, och programmet körs av en Javatolk som heter java.
Allt detta kan man hantera manuellt. Då editerar man .java-filerna med Emacs eller en annan
texteditor, kompilerar dem med javac och kör programmet med java. Det kan se ut så här:
% emacs HelloWorld.java &
% javac HelloWorld.java
% ls
HelloWorld.class HelloWorld.java
% java HelloWorld
Hello, world!
%
Starta en editor, skriv följande programtext:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
Kompilera programmet
Lista innehållet i katalogen
Två filer: kompilerad kod och källkod
Kör programmet
Det är inget fel med att utveckla program på detta sätt, men det har en del nackdelar: stora
program består av många klasser och det kan vara svårt att hålla reda på dem, man måste
komma ihåg att kompilera varje fil som man ändrar, när kompilatorn hittar fel i ett program
måste man i editorn hitta motsvarande rad i filen för att kunna rätta till felet, om man vill följa
exekveringen av ett program måste man ha en separat ”debugger” (felsökningsprogram).
Ett alternativt sätt att arbeta är att använda en integrerad utvecklingsmiljö (Integrated Development Environment, IDE). Det är ett program med ett grafiskt användargränssnitt där editor,
Eclipse – en handledning
110
kompilator och debugger är integrerade. Man startar IDE-programmet och hanterar sedan alla
moment i programutvecklingen inifrån denna miljö.
Programutvecklingen består fortfarande av editering, kompilering och programkörning, men
det blir enklare att utföra dessa moment — de utförs av IDE-programmet genom att man klickar
på knappar eller väljer ur menyer.
Det finns många olika IDE-program, mer eller mindre avancerade (Eclipse, IDEA, JBuilder,
Netbeans, JCreator, . . . ). I programmeringskurserna utnyttjas Eclipse.
Eclipse
Översikt
Eclipse är ett avancerat IDE-program som erbjuder all funktionalitet som behövs för utveckling
av Javaprogram. Det finns också ett stort antal ”plugins” som utökar Eclipse med nya möjligheter
(utveckling av webbprogram, databasprogram, diagramritning, andra programspråk än Java,
osv). Programmet är gratis.
Eftersom Eclipse har så många möjligheter är det inte helt enkelt att lära sig att använda
fullt ut. Man måste sätta sig in i en hel del terminologi och lära sig hitta bland alla menyer och
kommandon. Men grunderna är inte alltför komplicerade, och de beskrivs här.
Några termer måste man känna till:
Projekt
Arbetsområde
Perspektiv
(project) Ett antal .java- och .class-filer som hör ihop, till exempel alla
filer som behövs för en laboration eller en inlämningsupgift. Motsvaras av en katalog i filsystemet.
(workspace) Det område där man sparar de projekt som man skapar.
Motsvaras av en katalog i filsystemet (projekten är underkataloger till
denna katalog).
(perspective) Utseendet hos Eclipsefönstret. Ett perspektiv har ett antal fönster och knappar för kommandon.
Det finns massor av möjligheter att ställa in Eclipse så att det fungerar på olika sätt (Preferences
i Window-menyn). Varje inställningssida har en knapp Restore Defaults som ställer tillbaka alla
inställningar till de ursprungliga.
Användning av Eclipse
Vi förutsätter i detta avsnitt att Eclipse har installerats (hur man gör skiljer sig något mellan olika
datorer; se separata anvisningar). Vi förutsätter också att vi har skapat ett arbetsområde med ett
projekt test0 som innehåller en fil HelloWorld.java och en kompilerad fil HelloWorld.class. Hur
man skapar projekt och filer visar vi i nästa avsnitt. När man startar Eclipse får man ett fönster
som visas i figur 6 (under Linux, det kan se något annorlunda ut på andra datorer):
Om det inte står Java i fönstrets titelrad så är fel perspektiv aktiverat. Byt då perspektiv med
Open Perspective i Window-menyn. I projektvyn (längst till vänster under Package Explorer)
ser man de projekt man har skapat. När man klickar på pilen vid ett projekt så öppnas projektet
så att man ser innehållet. .java-filerna finns i katalogen src under (default package), .class-filerna
finns i en katalog bin som inte visas i projektvyn.
Området i mitten är en texteditor. När man dubbelklickar på en fil i ett projekt så laddas filen
in i editorn. Man kan editera flera filer samtidigt i olika flikar.
I editorn arbetar man som i alla editorer: man skriver och tar bort text som vanligt, markerar
text genom att dra med musen, klipper ut och klistrar in med control-x och control-v, och
så vidare. Eclipse-editorn känner till hur Javaprogram ska formateras, så den gör automatiskt
indragningar där de ska vara, stoppar in en högerparentes av rätt slag när man skriver en
Eclipse – en handledning
111
Figur 6: Eclipse-fönster, Java
vänsterparentes, och så vidare. Man kan till och med formatera om en hel fil så att programmet
följer kodkonventionerna; det gör man med kommandot Format i Source-menyn.
Eclipse-editorn kontrollerar också programmet och kompilerar det under tiden man skriver.
Om man skriver något som bryter mot Javas regler så markeras rader som innehåller fel med
kryss till vänster om raden. Man får en förklaring av felet genom att hålla musmarkören över
krysset.
Området under texteditorn har flera flikar. I Console visas utskrifter från program som körs,
och där skriver man också indata till programmet. I Problems visas felmeddelanden som finns i
filer som man sparat.
Man kör ett program genom att högerklicka på filen som innehåller main-metoden i projektvyn och välja Run As > Java Application. (Eller genom att välja Run As > Java Application i
Run-menyn eller genom att klicka på Run-ikonen
i verktygsraden.)
Om man råkat stänga till exempel editorfönstret utan att vilja det eller ställt till något annat med Javaperspektivet, så kan man återställa perspektivet till utgångsläget med kommandot
Reset Perspective i Window-menyn.
Skapa projekt och klass
Nu visar vi steg för steg hur man skapar ett nytt projekt med en klass och hur man kör programmet. Projektet ska heta test1 och innehålla en klass Calculator med följande utseende:
/** Calculator beräknar summan av två tal */
public class Calculator {
public static void main(String[] args) {
double nbr1 = 5.5;
double nbr2 = 11.3;
double sum = nbr1 + nbr2;
System.out.println("Summan av talen är " + sum);
Eclipse – en handledning
112
}
}
Detta är inte ett vettigt program, eftersom det bara kan beräkna summan av talen 5.5 och 11.3.
Om man vill beräkna summan av två andra tal måste man ändra i programmet och kompilera
om det. Något mera användbart skulle programmet bli om det läste in talen från tangentbordet,
men vi gör det så enkelt som möjligt nu.
Gör följande:
1. Skapa projektet test1. Välj File > New > Java Project, skriv namnet på projektet, klicka på
Finish.
2. Skapa en fil Calculator.java. Markera projektet test1 i projektvyn, klicka på New Java Classikonen
i verktygsraden, skriv namnet på klassen (Calculator), klicka på Finish.
3. Öppna projektet, src-katalogen och (default package) genom att klicka på pilarna. Dubbelklicka på Calculator.java för att ladda in filen i editorn. Som du ser så har Eclipse redan
fyllt i klassnamnet och de parenteser som alltid ska finnas. Komplettera klassen med mainmetoden som visas ovan.
4. Spara filen.
5. Kör programmet genom att högerklicka på filen och välja Run As > Java Application.
Utskriften som görs med System.out.println hamnar i konsolfönstret (om utskriften inte
blir Summan av talen är 16.8 så har du skrivit fel i programmet).
En del program behöver utnyttja klasser som inte finns i det aktuella projektet. (Egentligen gör
alla program det, de använder Javas standardklasser, men dessa är alltid tillgängliga och man
behöver inte göra något för att programmen ska komma åt dem.) Man måste för projektet ange
var dessa klasser finns. Det gör man genom att högerklicka på projektet och välja Build Path >
Configure Build Path.
Sedan finns det två fall:
• Klasserna som ska utnyttjas finns i ett annat projekt i samma arbetsområde. Klicka på
Projects > Add. . . och markera projektet som innehåller klasserna.
• Klasserna som ska utnyttjas finns i en biblioteksfil (.jar-fil, Java Archive) i ett annat projekt.
Klicka på Libraries > Add JARs. . . och välj rätt .jar-fil. Om .jar-filen finns utanför arbetsområdet klickar man i stället på Add External JARs. . . och letar upp filen.
Hitta fel i program
Fel i program är av två slag: dels ”språkfel” som kompilatorn hittar (man glömmer ett semikolon, glömmer att deklarera en variabel, stavar fel till ett ord och liknande fel), dels ”logiska
fel” som medför att ett program ger felaktigt resultat när det exekveras.
Som exempel tar vi programmet som beräknar summan av två tal från föregående avsnitt.
Satsen som beräknar summan och sparar den i variabeln sum ser ut så här:
double sum = nbr1 + nbr2;
Om vi i stället hade skrivit dubbel sum = nbr1 + nummer2; hade vi gjort två språkfel: typen
för reella tal heter double, inte dubbel, och variabeln där värdet av det andra talet finns heter
nbr2, inte nummer2. Kompilatorn markerar alla fel av denna typ, och de är för det mesta enkla att
korrigera (det kan vara svårt att tolka felmeddelandena i början, men det lär man sig efterhand).
Logiska fel är svårare att hitta. Antag att vi hade skrivit satsen på följande sätt:
Eclipse – en handledning
113
double sum = nbr1 - nbr2;
Detta är en helt korrekt sats och programmet kan kompileras och köras utan problem, men
resultatet blir inte korrekt (man får utskriften Summan av talen är -5.8).
Det här programmet är så litet och felet är så uppenbart att man kan hitta felet genom att
bara titta på programmet. Men i allmänhet är det inte så enkelt: ”riktiga” program är mycket
större och felet kan vara mycket mera komplicerat.
För att hitta logiska fel behöver man skaffa sig information om vad som händer under exekveringen av programmet. Ett sätt att göra det är att lägga in utskrifter av viktiga variabelvärden på
lämpliga ställen i programmet (det är ju egentligen det vi gjort i ovanstående program, där vi
omedelbart efter summeringen skriver ut summavärdet). Detta sätt är dock svårhanterligt och
tidsödande.
Bättre är att utnyttja en debugger (”avlusare”). Då får man möjlighet att själv kontrollera
exekveringen av programmet: man kan köra programmet rad för rad och man kan under tiden
titta på hur variabelvärdena ändras. Man kan också sätta brytpunkter i programmet. När man
sedan startar programmet så stoppas exekveringen när den når en brytpunkt.
Man sätter en brytpunkt på en rad genom att dubbelklicka till vänster om raden, och man
tar bort en brytpunkt på samma sätt. När man har satt en brytpunkt kan man starta debuggern.
Det gör man genom att högerklicka på filen och välja Debug As > Java Application. Eclipse byter
till debug-perspektivet. Fönstrets nya utseende visas i figur 7.
I mitten finns editorfönstret. Den rad som står i tur att exekveras är markerad med en pil.
Ovanför editorfönstret finns en vy med information om det körande programmet. I verktygsraden finns ikoner för kommandon. De viktigaste är:
Figur 7: Eclipse-fönster, debug
Eclipse – en handledning
114
Resume — fortsätt exekveringen till nästa brytpunkt eller till programmets slut.
Terminate — avsluta exekveringen av programmet.
Step over — exekvera en rad i programmet.
Step into — exekvera till nästa rad, gå in i metoder som anropas.
Importera och exportera filer
När man arbetar med Eclipse har man normalt alla sina filer i projekt i arbetsområdet. Om man
har filer någon annanstans kan man importera dem till ett existerande projekt. Det gör man
genom att högerklicka på projektet, välja Import. . . > General > File System och leta upp filerna.
Genom att i stället välja Export kan man exportera filer.
Man kan också importera och exportera hela projekt. Det gör man genom att högerklicka i
projektvyn och välja Import > General > Archive File respektive genom att högerklicka på ett
projekt och välja Export > General > Archive File. Ett arkiverat projekt sparas som en komprimerad .zip-fil.
Att skapa nya projekt (för egna övningar)
Du kan skapa egna projekt i Eclipse, där du kan öva på att skriva egna program. Om du lägger
dina program i egna projekt hamnar de i en egen katalog i Eclipse, precis som laborationerna.
Hur du skapar ett nytt projekt ser du i avsnittet ”Skapa projekt och klass” (som börjar på
s. 111). Här beskrivs hur du gör kursens klasser (SimpleWindow, Square, m. fl.) tillgängliga för
ditt nya projekt. Om du inte gör detta så kommer Eclipse att ge felmeddelanden då du försöker
använda dessa (t. ex. i import-satser).
• Högerklicka på ditt nya projekt i vänsterspalten i Eclipse.
• En lång meny dyker upp, där du väljer Build Path > Configure Build Path... .
• I rutan som dyker upp väljer du fliken Libraries.
• Klicka på Add JARs...
• I listan över projekt öppnar du cs_pt och väljer filen cs_pt.jar. Klicka OK för att lämna
projektlistan, därefter OK för att lämna Build Path-inställningarna.
Nu ska ditt nya projekt kunna utnyttja kursens klasser, precis som laborationerna.