Die Türme von Hanoi, eine kleine Einführung in die

Transcription

Die Türme von Hanoi, eine kleine Einführung in die
Die Türme von Hanoi,
eine kleine Einführung in die Objektorientierung in Java
Matthias Krause (Hochschule für Telekommunikation Leipzig)
19. September 2008
Software und Literatur zu Java
• Die Quelltexte und das Skript zu diesem Vortrag liegen unter
http://www.hft-leipzig.de/~krause/docs/Hanoi.zip
• Programmiersprache Java (http://java.sun.com/)
download Java SE (Java Standard Edition)
Übersetzen mit: javac Hanoi.java
Abarbeiten mit: java Hanoi
• Programmierumgebung eclipse (http://www.eclipse.org/downloads/)
• Guido Krüger: Handbuch der Javaprogrammierung (http://www.javabuch.de)
• ...
Das Problem lautet der Legende nach folgendermaßen: Die Mönche eines Klosters sollen einen Stapel von
64 Goldscheiben unter Benutzung eines Hilfsstapels auf einen Zielstapel bewegen. Dabei darf immer nur
eine Scheibe bewegt werden und niemals darf eine größere auf einer kleineren Scheibe liegen. Der Legende
zufolge beauftragte der oberste Mönch den unter ihm, 63 Scheiben auf den Hilfsstapel zu bewegen, danach
wolle er die 64. Scheibe auf den Zielstapel legen, am Ende kann der Untergebene die 63 Scheiben auf
den Zielstapel legen. Der dem obersten Mönch untergebene verfolgt bei seinen 63 Scheiben die gleiche
Strategie, . . .
Wie nicht anders zu erwarten, geht diese Legende auf einen Mathematiker zurück
(http://de.wikipedia.org/wiki/T%C3%BCrme_von_Hanoi).
Warum gerade die Türme von Hanoi?
• Der Turm als Klasse und somit als ein Beispiel für objektorientierte Programmierung
• und die Bewegung der Scheiben zwischen den Türmen als klassisches Beispiel für rekursive Programmierung
Was ist eine Klasse (innerhalb der objektorientierten Programmierung)?
Eine Klasse ist ein abgeschlossenes Bündel Quelltext, welches für eine bestimmte Aufgabe verwendet wird
und
• Variablen (Zahlen, Texte, . . . )
• und Methoden (Funktionen, Unterprogramme, Subroutines, . . . )
enthält, die öffentlich oder privat sein können (Zugriffsschutz). Nach dem Muster einer Klasse (Puddingform) kann dann ein Objekt (Pudding) hergestellt werden.
1
Was muß ein Objekt der Klasse Turm haben und können? Es muß eine Anzahl (in der int-Variable hoehe)
Scheiben einer bestimmten Größe (hier als Integer) aufnehmen können (in einer Variablen namens stapel
des Typs Vector), das passiert mit den Methoden hinlegen(Integer) und Integer wegnehmen() . . .
Listing 1: Eine Klasse mit Daten und Methoden
1
public class Turm {
2
private Vector stapel;
public int hoehe;
3
4
5
public void
{ ... }
6
7
hinlegen (Integer scheibe)
8
public Integer wegnehmen ()
{ ... }
9
10
11
/* 1x ganz am Anfang wird der Konstruktor abgearbeitet */
public Turm ( )
{ ... }
12
13
14
15
16
}
. . . und natürlich muß so ein Turm auch schön gezeichnet werden, was mit einer Methode namens
paint(Graphics g) geschieht:
Listing 2: Diese Klasse als Graphik-Komponente
1
public class Turm extends Canvas{
2
... /* Daten und Methoden wie oben */
3
4
/* und das Zeichnen der Komponente: */
public void paint (Graphics g)
{ ... }
5
6
7
8
}
Jetzt kann man diesen Turm einfach so in ein Window (also in eine komplette Graphikapplikation der
Klasse Frame) hineinstecken, diese Klasse nennen wir TestTurm, weil in ihr die Graphikkomponente Turm
getestet wird:
Listing 3: Eine Grafikapplikation mit einer eingebetteten Komponente
1
2
public class TestTurm extends Frame{
3
Turm t;
4
5
public static void main(String [] a) {
TestTurm h = new TestTurm();
...
}
6
7
8
9
10
public TestTurm() {
t = new Turm();
add(t);
...
}
11
12
13
14
15
16
}
2
Wie die Klasse Turm komplett aussieht, zeigt das folgende Listing:
Listing 4: Die komplette Klasse Turm
1
2
import java.util.*;
import java.awt.*;
3
4
public class Turm extends Canvas {
5
private Vector<Integer> stapel = new Vector<Integer>();
6
7
public Turm () {
8
9
}
10
11
public Turm (int hoehe) {
for (int i = hoehe; i > 0; i--) {
hinlegen(new Integer(i));
}
}
12
13
14
15
16
17
public void hinlegen (Integer scheibe) {
stapel.add(scheibe);
invalidate();
repaint();
}
18
19
20
21
22
23
public Integer wegnehmen () {
Integer s;
s = stapel.remove(stapel.size()-1);
invalidate();
repaint();
return s;
}
24
25
26
27
28
29
30
31
private int hoehe () {
return stapel.size();
}
32
33
34
35
public void drucke() {
for (int i=0; i<hoehe();i++) {
System.out.print(stapel.get(i) + " ");
}
System.out.println();
}
36
37
38
39
40
41
42
public void paint(Graphics g) {
// quick and kind of dirty
int breit = getSize().width;
int hoch = getSize().height;
int stab = breit / 2;
int deltax = breit / 20;
int deltay = hoch / 11;
g.drawRect(0, 0, breit-1, hoch-1);
for (int i=0; i<hoehe();i++) {
int b = stapel.get(i);
int h = hoch - (i+1)*deltay;
g.fillRect( stab - b*deltax , h, 2*b*deltax, deltay - 5 );
}
}
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
}
3
Da das Problem der Türme von Hanoi drei nebeneinanderstehende Türme benötigt, stecken wir diese Anordnung in eine Klasse DreiTuerme, die sonst gar nichts weiter kann, aber schon eine Graphikapplikation
(Klasse Frame) ist:
Listing 5: Die Klasse DreiTuerme
1
2
import java.awt.*;
import java.awt.event.*;
3
4
class DreiTuerme extends Frame {
5
static Turm QUELLE;
static Turm ZIEL;
static Turm HILFS;
6
7
8
9
static int ANZAHL = 0;
10
11
public DreiTuerme () {
12
13
super("Türme von Hanoi");
14
15
QUELLE = new Turm(ANZAHL);
HILFS = new Turm();
ZIEL = new Turm();
16
17
18
19
addWindowListener(
...
);
20
21
22
23
setLayout(new GridLayout(1,3));
add (QUELLE);
add (HILFS);
add (ZIEL);
24
25
26
27
28
setSize(600,300);
setLocation(10,10);
setVisible(true);
29
30
31
32
}
33
34
public static void pause(int ms) {
try { Thread.sleep(ms) ; }
catch (Exception e) {System.out.println(e);}
}
35
36
37
38
39
40
}
Variablen (und Methoden), vor denen ein static steht, gehören zur Klasse und existieren somit, ohne
daß ein Objekt benötigt wird. Somit existiert so eine Variable genau einmal pro Klasse!
Da DreiTuerme nicht mal eine Methode
public static void main(...)
hat, brauchen wir zum Testen . . .
4
Listing 6: Die Klasse TestDreiTuerme
1
2
3
4
public class TestDreiTuerme extends DreiTuerme {
public static void main (String [] a) {
ANZAHL = 7;
TestDreiTuerme t = new TestDreiTuerme();
5
ZIEL.hinlegen(5);
HILFS.hinlegen(7);
QUELLE.wegnehmen();
Integer s = QUELLE.wegnehmen();
ZIEL.hinlegen(s);
6
7
8
9
10
}
11
12
}
. . . und zu guter letzt versehen wir DreiTuerme mit der genialen Intuition des obersten Mönchs, der
den Hauptteil der Aufgabe mal so einfach nach unten delegiert hat. Das stecken wir in die Methode
bewegeStapel(...):
Listing 7: Die Klasse Hanoi
1
2
3
import java.util.*;
import java.awt.*;
import java.awt.event.*;
4
5
public class Hanoi extends DreiTuerme{
6
static int pausendauer = 1000;
7
8
public static void main(String [] a) {
9
10
ANZAHL = 10;
11
12
Hanoi h = new Hanoi();
13
14
bewegeStapel(QUELLE, ZIEL, HILFS, ANZAHL);
15
16
}
17
18
public static void bewegeStapel (Turm quelle, Turm ziel, Turm hilfs, int anzahl) {
if (anzahl == 1) bewegeScheibe (quelle,ziel);
else if (anzahl > 1){
bewegeStapel (quelle,hilfs,ziel,anzahl-1);
bewegeScheibe(quelle,ziel);
bewegeStapel (hilfs,ziel,quelle,anzahl-1);
}
}
19
20
21
22
23
24
25
26
27
public static void bewegeScheibe(Turm quelle, Turm ziel) {
Integer s;
pause(pausendauer);
s = quelle.wegnehmen();
ziel.hinlegen(s);
}
28
29
30
31
32
33
34
35
}
5