© APSIS GmbH |
Seite 216
Seite 216
Seite 216
try { ... // Normalfall } catch (...) { ... // Sonderfall }
if (normalfall) { ... // Normalfall } else { ... // Sonderfall }
if (eimer.gefuellt()) // (10.1) Eimer.meldung("Eimer ist gefüllt"); else Eimer.meldung("Eimer ist leer");
if (! eimer.gefuellt()) // ¬ Eimer.meldung("Eimer ist leer"); else Eimer.meldung("Eimer ist gefüllt");
if (a == b) // (10.2) a = 0;
Übung 10.1: Programmieren Sie eine rekursive Funktion fak, die die Fakultät einer gegebenen natürlichen Zahl berechnet. Sie wird als das Produkt der ersten n natürlichen Zahlen durch die Formel
n! = 1 × 2 × 3 × ... × (n-1) × n
definiert. Durch die Angabe verschiedener natürlicher Zahlen als Parameter der Funktion kann eine Wertetabelle zusammengestellt werden:
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ... |
n! | 1 | 1 | 6 | 24 | 120 | 720 | 5040 | 40320 | ... |
Abb. 10.1: Fakultät
Die rekursive Berechnung ist auf Grund der Formel
n! = 1 × 2 × 3 × ... × (n-1) × n = (n-1)! × n
möglich: fak(n) == n * fak(n-1). Wir können diesmal keine Ausnahme für den Abbruch der Rekursion bei 1 verwenden; eine Einweg-Alternative ist nötig:
if (n > 1) ... // Rekursion else ... // Abbruch
Seite 218
int anzahl = finden(); // (10.3) switch (anzahl) { // ¬ case 0 : meldung("Nichts gefunden"); break; case 1 : meldung("Eins gefunden"); break; case 2 : meldung("Mehrere gefunden"); break; default: meldung("Fehler"); }
switch (zeichen) { case '?': case '.': case ',': case '!': meldung("Satzzeichen"); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': meldung("Ziffer"); break; default: meldung("anderes Zeichen"); }
Seite 218
Seite 218
if (bedingung1 & bedingung2) ...
if (j > 0) // (10.4) if (i/j > k) ... ; // Aktion
if (j > 0 && i/j > k) ... ; // Aktion
if (bedingung1() || bedingung2()) ...
Seite 219
public void fuellen() throws VollAusn { // (10.5) if (eimerGefuellt) // Ausnahmesituation erkannt ¬ throw new VollAusn(); else { // keine Ausnahme, erforderliche Aktion wird durchgeführt: ¬ eimerGefuellt = true; if (eimerPos > 0) // wenn Eimer sichtbar Anim.eimerFuellen(); // Animation } } public Getraenk inhalt() throws LeerAusn { if (! eimerGefuellt) // Ausnahmesituation ¬ throw new LeerAusn(); else // Normalfall ¬ return eimerInhalt; }
package lehrbuch.kapitel8; // (10.6) import lehrbuch.Aufz; public class DiskreteMengeGen implements DiskreteMenge { private Class klasse; // registrierte Klasse ¬ public DiskreteMengeGen(final Aufz reg) { // letzter Wert der Aufzählungsklasse klasse = reg.getClass(); // Methode getClass geerbt von Object ¬ ... // weiter im Programm (10.13) } private void pruefen(final Aufz element) { if (! klasse.isInstance(element)) // registrierte Klasse? ¬ throw new GenFehler(); } public void fuellen(final Aufz element) { // trägt element in die Menge ein pruefen(element); ... // weiter im Programm (10.13)
Übung 10.2: Implementieren Sie die folgende Schnittstelle:
interface LogBehaelter { // (10.7) void fuellen(boolean wert) throws VollAusn; void entleeren() throws LeerAusn; boolean inhalt() throws LeerAusn; boolean gefuellt(); }
Erkennen Sie die Ausnahmesituationen mit Abfrage.
Übung 10.3: Implementieren Sie die Schnittstelle Warteschlange aus dem Kapitel 8.3.3. auf Seite 182 als Reihung (Ringpuffer). Im Gegensatz zur Übung 9.3 auf Seite 202 programmieren Sie die Operationen ohne Indexüberlauf:
public void eintragen(final Object element) throws VollAusn { // (10.8) if (anzahl == inhalt.length) // Anzahl der gespeicherten Elemente throw new VollAusn(); else if (juengstes == inhalt.length) juengstes = 0; // auf das letzte Element folgt das Erste else juengstes ++; // ¬ inhalt[juengstes] = element; anzahl ++; }
Programmieren Sie einen menügesteuerten Testtreiber, mit dem Sie gerade und ungerade Zahlen in zwei getrennten Warteschlangen speichern können.
Seite 221
void fuellenLinks() { // (6.16) try { links.fuellen(Eimer.WASSER); } catch (VollAusn ausnahme) { Eimer.meldung("Eimer voll"); } }
void fuellenLinks() { // (10.9) if (links.gefuellt()) // ¬ Eimer.meldung("Eimer voll"); else try { links.fuellen(Eimer.WASSER); } catch (VollAusn ausnahme) { System.out.println(ausnahme); } // nicht erwartet }
Seite 222
Seite 222
for (int i = 1; i <= anzahl; i++) ... // (10.10)
for (int i = 0; i < reihung.length; i++) ...
for (char zeichen = 'a'; zeichen <= 'z'; zeichen++) System.out.print(zeichen); // Alphabet wird angezeigt
for (char zeichen = 'z'; zeichen >= 'a'; zeichen--) System.out.println(zeichen); // Alphabet wird rückwärts angezeigt
int anzahl = System.in.read(); for (int i = 1; i <= anzahl; i++) System.out.println("Hallo"); // wird nie ausgeführt, wenn 0 eingegeben wurde ¬
int fak(int n) { // n>0 // (10.11) int produkt = 1; for (int zahl = 2; zahl <= n; zahl++) // ¬ produkt *= zahl; return produkt; }
for (int i = 0, j = reihung.length; j >= 0; i++, j--) ... // zwei Indizes laufen entgegengesetzt durch
Übung 10.4: Entwickeln Sie eine Prozedur (Methode einer aufgeschobenen Klasse), die die Wertetabelle einer beliebigen Funktion im gegebenen Intervall erstellt. Die zu berechnende Funktion ist eine Rückruffunktion. Die Ergebnisse sollen auch von Rückrufprozeduren übernommen werden:
interface Wertetabelle { // (10.12) void wertetabelle(final float untergrenze, final float obergrenze, final int anzahlSchritte); float funktion(final float x) throws KeinFunktionswertAusn; // abstract // die zu berechnende Funktion void ergebnis(final float x, final float y); // abstract // Übernahme der Ergebnisse void keinFunktionswert(final float x); // abstract // Übernahme, wenn kein Funktionswert berechnet werden kann }
Die Schrittweite der Wertetabelle kann mit Hilfe der Formel berechnet werden:
Rufen Sie Ihre Prozedur in einem Testprogramm für die Funktion f(x) = 1/(x-k) und für einen Intervall [a, b] auf, dessen Grenzen (wie auch die Funktionskonstante k) interaktiv eingegeben werden. Die Übernahmeprozedur soll die Wertetabelle in zwei Spalten (x-Wert und y-Wert) ausgeben. An den x-Stellen, für die kein y-Wert berechnet werden kann (z.B. weil x=k), soll keinFunktionswert aufgerufen werden.
Seite 224
... // Anfang im Programm (10.6) // (10.13) private boolean enthalten[]; // ¬ public DiskreteMengeGen(final Aufz reg) { // letzter Wert der Aufzählungsklasse klasse = reg.getClass(); // Methode getClass geerbt von Object Aufz letzter = reg.letzter(); enthalten = new boolean[letzter.pos()+1]; // ¬ // für jeden Aufzählungswert ein boolean for (int i = 0; i < enthalten.length; i++) enthalten[i] = false; // Menge ist zu Anfang leer ¬ } public DiskreteMengeGen(final DiskreteMengeGen quelle) { klasse = quelle.klasse; enthalten = new boolean[quelle.enthalten.length]; for (int i = 0; i < enthalten.length; i++) enthalten[i] = quelle.enthalten[i]; // ¬ } public void fuellen(final Aufz element) { // trägt element in die Menge ein pruefen(element); enthalten[element.pos()] = true; // ¬ } public void entfernen(final Aufz element) { // löscht element aus der Menge pruefen(element); enthalten[element.pos()] = false; // ¬ } public boolean vorhanden(final Aufz element) { pruefen(element); return enthalten[element.pos()]; // ¬ } ...
class DiskreterSackGen implements DiskreterSack { // (10.14) private int[] anzahl; // ¬ public DiskreterSackGen(final Aufz reg) { ... // Registrierungsobjekt analysieren wie im Programm (10.6) anzahl = new int[letzter.pos()+1]; // für jeden Aufzählungswert ein int entleeren(); // Sack ist zu Anfang leer } public void entleeren() { // löscht alle Elemente aus dem Sack for (int i = 0; i < anzahl.length; i++) anzahl[i] = 0; } public void fuellen(final Aufz element) { pruefen(element); // wie im Programm (10.6) anzahl[element.pos()] ++; // ¬ } public void entfernen(final Aufz element) throws KeinEintragAusn { pruefen(element); if (anzahl[element.pos()] == 0) throw new KeinEintragAusn(); else anzahl[element.pos()] --; // ¬ } public void alleEntfernen(final Aufz element) { pruefen(element); anzahl[element.pos()] = 0; // ¬ } ...
Übung 10.5: Jetzt können Sie Ihre Klasse aus der Übung 8.8 auf Seite 181 (SackAlgebra) programmieren. Testen Sie Ihre Implementierung.
Seite 226
public void kopieren(Stapel quelle) throws VollAusn { // (10.15) try { StapelPol q = (StapelPol)quelle; inhalt = new Object[q.inhalt.length]; // neue Reihung erzeugen spitze = q.spitze; for (int index = 0; index < spitze; index++) inhalt[index] = q.inhalt[index]; } catch (OutOfMemoryError ausnahme) { throw new VollAusn(); } } public boolean gleich (Stapel stapel) { StapelPol s = (StapelPol)stapel; if (spitze != s.spitze) return false; for (int index = 0; index < spitze; index++) // ¬ if (inhalt[index] != s.inhalt[index]) return false; return true; }
Abb. 10.2: Relevante Komponenten
Seite 227
Abb. 10.3: Vektoraddition
public interface Element { // (10.16) public void plus(final Element element); public void mult(final Element element); public Element zero(); // liefert Nullelement der Klasse (z.B. 0 für Integer) } public class Vektor { // (10.16) public Element [] vektor; public Vektor(int laenge) { vektor = new Element [laenge]; } public void plus(final Vektor v) throws UnpassendAusn { if (vektor.length != v.vektor.length) { // vorbeugend throw new UnpassendAusn(); } else { for (int index = 0; index < vektor.length; index++) // ¬ vektor[index].plus(v.vektor[index]); } } public void mult(final Element zahl) { ... // multipliziert mit zahl, ähnlich public Element mult(final Vektor v) throws UnpassendAusn { if (vektor.length != v.vektor.length) // vorbeugende Ausnahmebehandlung throw new UnpassendAusn(); else { Element summe = v.vektor[0].zero(); // mit Nullwert vorbesetzen for (int index = 0; index < vektor.length; index++) { // ¬ Element produkt = vektor[index]; produkt.mult(v.vektor[index]); summe.plus(produkt); } return summe; } } public void kopieren(final Vektor quelle) throws VollAusn { try { if (vektor.length != quelle.vektor.length) { // ungleiche Größe vektor = new Element[vektor.length]; // neu belegen; throws OutOfMemoryError } for (int i = 0; i < vektor.length; i++) // elementweise kopieren ¬ vektor[i] = quelle.vektor[i]; } catch (OutOfMemoryError ausnahme) { throw new VollAusn(); } } class UnpassendAusn extends Exception {} }
for (int index = 0; index < vektor.length; index++) summe += vektor[index] * v.vektor[index];
class GanzElement implements Element { // (10.17) private int wert; public GanzElement() { wert = 0; } public GanzElement(final int wert) { this.wert = wert; } public void plus(final Element element) { GanzElement e = (GanzElement)element; wert += e.wert; } public void mult(final Element element) { ... // ähnlich public Element zero() { return new GanzElement(0); } } ...
Vektor ganzVektor = new Vektor(20); // Vektor für 20 Ganzzahlen ganzVektor.vektor[0] = new GanzElement(325); ganzVektor.vektor[1] = new GanzElement(-2315); ... // usw., alle Vektorelemente werden besetzt ganzVektor.mult(new GanzElement(-1)); // Vorzeichen wird umgekehrt
class EimerElement implements Element { // (10.18) private Eimer wert; public EimerElement() { wert = new Eimer(); } public void plus(final Element element) { ... // Summe zweier Eimer, wie auch immer sie errechnet wird public void mult(final Element element) { ... // Produkt ähnlich public Element zero() { ... // Nulleimer } Vektor eimerVektor = new Vektor(5); // Platz für fünf Eimer ... // Eimer werden im Vektor abgelegt, addiert sowie ihr Skalarprodukt errechnet
Seite 229
for (int zeile = 1; zeile <= 10; zeile++) { // (10.19) for (int spalte = 1; spalte <= 10; spalte++) System.out.print("\t" + zeile * spalte); // mit Tabulator System.out.println(); // nur Zeilenvorschub }
public class Matrix { // (10.20) public Element[][] matrix; public Matrix(int breite, int hoehe) { matrix = new Element[breite][hoehe]; } public void plus(final Matrix m) throws UnpassendAusn { if (matrix.length != m.matrix.length || matrix[0].length != m.matrix[0].length) throw new UnpassendAusn(); else { for (int zeile = 0; zeile < matrix.length; zeile++) // ¬ for (int spalte = 0; spalte < matrix[0].length; spalte++) // ¬ matrix[zeile][spalte].plus(m.matrix[zeile][spalte]); } } public void mult(final Element zahl) { ... // multipliziert mit zahl, ähnlich public void mult(final Matrix vektor) throws UnpassendAusn { ... }
Übung 10.6: Programmieren Sie das Produkt zweier Matrizen nach der folgenden Regel: Das Element in der Zeile i und der Spalte j der Produktmatrix C ist das Skalarprodukt der i-ten Zeile der ersten Matrix A mit der j-ten Spalte der zweiten Matrix B. Hieraus ergibt sich folgende Formel für alle Elemente ci,j des Produkts zweier Matrizen mit den Elementen ai,j und bi,j :
d.h. jedes Element ci,j der Summenmatrix C (des Ergebnisses) mit den Indizes i und j ist die Summe der i-ten Zeile der ersten Matrix A und der j-ten Spalte der zweiten Matrix B. Aus dieser Formel ist ersichtlich, dass die Anzahl der Spalten in A gleich sein soll mit der Anzahl der Zeilen in B. (Die erste Dimension von A muss mit der zweiten Dimension von B gleich sein). Die Ergebnismatrix C hat so viele Zeilen wie A und so viele Spalten wie B: Wenn A ein n . l und B eine l . m Matrix ist, dann wird das Produkt C eine n . m Matrix werden.
Für die Programmierung des Matrixprodukts ist eine dreifach geschachtelte Wiederholung nötig. Schreiben Sie auch ein Testprogramm für Ihre Matrixklasse, in welchem Sie sie mit einer Ganzzahl- und für einen Bruchklasse ausprägen. Im Stapeltesttreiber sollen Sie die Matrizen (der Größe 2 .3 und 3 .4) als Konstanten definieren, ihre Summen und Produkte errechnen und am Bildschirm in Matrixform ausgeben.
Seite 230
Seite 231
try { while (true); // leere Anweisung wird wiederholt ausgeführt, der Rechner tut nichts } catch (TasteGedruecktAusn ausnahme) { ... // hier wird der Wartezustand beendet
int fakWert = 0; // (10.21) double summand = 1.0; double e = 1.0; while (true) { // ¬ fakWert ++; summand /= fakWert; // dividieren und zuweisen e += summand; }
while (true) { // (10.22) ... // Eins-Block if (abbruchbedingungErfuellt) break; // ¬ ... // Null-Block }
double epsilon = 0.001; // (10.23) while (true) { fakWert ++; summand /= fakWert; if (summand < epsilon) break; // ¬ e += summand; }
while (true) { if ... ... break; // nicht empfehlenswert ... }
public static float bruchzahlLesen() throws java.io.IOException { // liest float-Wert // (10.24) StringBuffer puffer = new StringBuffer(); // Puffer, Eingabe zu sammeln while (true) { int i = System.in.read(); // ein Zeichen lesen char c = (char)i; // Zeichen konvertieren if (c == '\n' || c == '\r') break; // Ende der Zeile? ¬ puffer.append(c); // neues Zeichen am Ende des Puffers hinzufügen } int i = System.in.read(); // Rest der Eingabe verwerfen Float f = new Float(0).valueOf(new String(puffer)); // konvertieren return f.floatValue(); // float-Wert herauslesen }
Übung 10.7: Schreiben Sie eine Java-Prozedur, das Zählen von Buchstaben durchführt. Lesen Sie von der Tastatur eine Zeile ein und zählen Sie die Häufigkeit jedes Buchstabens in einer Tabelle. Geben Sie zum Schluss das Ergebnis auf der Konsole aus.
Übung 10.8: Entwickeln Sie ein Java-Dialogprogramm für das folgende Problem der Zeichenanalyse: Die von der Tastatur eingegebenen Zeichen sollen analysiert und eine Statistik ausgegeben werden. Folgende Informationen werden verlangt:
Seite 233
while (true) { // leerer Eins-Block // (10.25) if (abbruchbedingungErfuellt) break; ... // Null-Block };
while (! abbruchbedingungErfuellt) { ... // Null-Block }
do { ... // Eins-Block } while (! abbruchbedingungErfuellt);
double summand = 1.0; while (summand >= epsilon) { // ¬ fakWert ++; summand /= fakWert; e += summand; };
Seite 234
public void speichern(String dateiname) throws DateiAusn { // (10.26) SeqDateiImpl datei = new SeqDateiImpl(dateiname); datei.neuBeschreiben(); for (int i = 0; i < enthalten.length; i++) // ¬ if (enthalten[i]) datei.eintragen(new Character((char)i)); } public void laden (final String dateiname) throws DateiAusn { try { entleeren(); SeqDateiImpl datei = new SeqDateiImpl(dateiname); datei.zuruecksetzen(); while (! datei.endeDerDatei()) { // ¬ Character eintrag = (Character)datei.aktuellesElement(); enthalten[(int)eintrag.charValue()] = true; datei.naechstesElement(); } } catch (SeqDateiImpl.DateiendeAusn ausnahme) { throw new DateiAusn(); } }
Übung 10.9: Ergänzen Sie Ihre Lösung Übung 9.9 auf Seite 212 mit der Implementierung der Persistenzoperationen der Klasse PosListePol.
Seite 234
public void suchen(final Object element) throws NichtGefundenAusn { // (10.27) try { do // suche erst vom nächsten Knoten an aktuell = aktuell.vorwaerts; while (aktuell.wert != element); // ¬ } catch (NullPointerException ausnahme) { throw new NichtGefundenAusn(); } } public void speichern(final String dateiname) throws DateiAusn { // const try { SeqDateiImpl datei = new SeqDateiImpl(dateiname); datei.neuBeschreiben(); Knoten knoten = anfang; while (knoten != null) { // ¬ datei.eintragen(knoten.wert); // wert ist Serializable knoten = knoten.vorwaerts; } } catch (Exception ausnahme) { throw new DateiAusn(); } }
Übung 10.10: Implementieren Sie die Vergleichs- und die Zuweisungsmethode für die Stapelklasse StapelListe im Programm (9.10) auf Seite 204 mit Hilfe einer rumpfgesteuerten Schleife.
Seite 235
public void mischen( // vorsortierte Dateien werden gemischt // (10.28) final String eingabe1, final String eingabe2, final String ausgabe) { IFile band1 = new IFile(), band2 = new IFile(); OFile band3 = new OFile(); Element puffer1 = new Element(), puffer2 = new Element(); // geordnet band1.open(eingabe1); band2.open(eingabe2); band3.open(ausgabe); band1.write(puffer1); band2.write(puffer2); while (!band1.eof() && !band2.eof()) { // ¬ if (puffer1.kleiner(puffer2)) { // Operation für Element band3.write(puffer1); // den kleineren ausschreiben band1.write(puffer1); // und wieder einlesen } else { band3.write(puffer2); band2.write(puffer2); } } // band1 oder band2 ist zu Ende // Rest kopieren: while (!band1.eof()) { // ¬ band3.write(puffer1); band1.write(puffer1); } while (!band2.eof()) { // ¬ band3.write(puffer2); band2.write(puffer2); } }
Übung 10.11: Ergänzen Sie die Übung 10.10 auf Seite 235 mit der Implementierung der Persistenzmethoden laden und speichern für die Stapelklasse.
Seite 236
int hash(final schluessel); // const schluessel
Abb. 10.4: Assoziativtabelle
int verschiebungen();
Übung 10.12: Programmieren Sie einen Assoziativspeicher als Assoziativtabelle. Die Schnittstelle eines (allgemeinen) Assoziativspeichers finden Sie im Programm (8.27) auf Seite 192. Definieren Sie nun die Struktur der Klasse AssoTab und programmieren Sie ihre Operationen. Prägen Sie Ihren Assoziativspeicher für ein Telefonbuch aus, in dem Sie den Nachnamen als Schlüssel, Vornamen und Telefonnummer als Element speichern. Die Größe der Tabelle sollte als Konstruktorparameter angegeben werden. Schreiben Sie dazu ein einfaches Dialogtestprogramm. Vergessen Sie dabei die Persistenzoperationen nicht: Über einen Menüpunkt sollte Ihre Tabelle in eine Datei, deren Name eingegeben wird, gespeichert bzw. von dort gelesen werden können. Hierzu muss die Elementklasse die Schnittstelle java.io.Serializable implementieren.
Seite 237
while (! dateiende()) { // (10.29) element.lesen(); element.bearbeiten(); element.schreiben(); }
referenz = anker; while (referenz != null) { bearbeiten(referenz.wert); referenz = referenz.verbindung; }
while (true) { element.bearbeiten(); element.lesen(); if (keineMehrDa()) break; }
while (true) { element.lesen(); if (element.endekriterium()) break; element.bearbeiten(); }
Seite 238
while (true) { // (10.30) ... // Eins-Block if (abbruchbedingungErfuellt) break; ... // Null-Block };
... // Eins-Block verdoppelt while (! abbruchbedingungErfuellt) { ... // Null-Block ... // Eins-Block };
boolean weiter = true; while (weiter) { ... // Eins-Block weiter = ! abbruchbedingungErfuellt; if(weiter) ... // Null-Block };
for (int i = anfang; i <= ende; i++) // (10.31) ... // Rumpf
int i = anfang; while (i <= ende) { ... // Rumpf i ++; };
Seite 239
© APSIS GmbH |