Donnerstag, 28. Mai 2015
Aufgabe JTable (POS1:3BHIF)
Erstellen Sie ein Java-Programm, welches einfache CSV-Dateien erzeugen, darstellen und ändern kann. Verwenden Sie zur Darstellung der CSV-Datei eine JTable. Beim Neuanlegen wird die Anzahl der Spalten festgelegt. Beim Laden einer CSV-Datei wird die Anzahl der Zeilen/Spalten durch die Datei gegeben. Das Programm soll es ermöglichen, jede Zelle (definiert durch Zeile und Spalte) zu ändern. Sehen Sie eine Möglichkeit zum Einfügen neuer Zeilen vor. Eine markierte Zeile soll gelöscht werden können.
Die Beschriftung der Spalten soll in der ersten Zeile der Datei gespeichert werden bzw. wird beim Laden aus der ersten Zeile der Datei entnommen.
Labels: Aufgabe, GUI, Java, POS1-3
Mittwoch, 20. Mai 2015
Berechnungen mit Threads parallelisieren (POS1: 3BHIF)
π kann mit Hilfe der folgenden Formel von Strömer (1986) berechnet werden:
π / 4 = 44 · arctan 1/57 + 7 · arctan 1/239 - 12 · arctan 1/682 + 24 · arctan 1/12943
Der Arcustangens wird mit der folgenden Reihenentwicklung
arctan(x) = x - x3/3 + x5/5 - x7/7 + ...
berechnet.
Folgendes Java-Programm verwendet die Klasse BigDecimal zur Berechnung von π. Es soll mittels Threads parallelisiert werden.
import java.math.*;
/**
* @author Hans Joachim Pflug, RZ, RWTH Aachen
*
* Berechnet PI auf eine beliebige Anzahl von Stellen genau
*/
public class Pi {
private int dec; //Anzahl der Dezimalstellen
private BigDecimal pi = new BigDecimal(0); //Ergebnis
private MathContext m; //Zur Bestimmung der Laenge der Brueche
private static final BigDecimal one = new BigDecimal(1);
private static final BigDecimal four = new BigDecimal(4);
private BigDecimal r57; // 1/57
private BigDecimal r239; // 1/239
private BigDecimal r682; // 1/682
private BigDecimal r12943; // 1/12943
/**
* Erzeugt ein Objekt mit PI auf die angegebene Stellenzahl genau
* @param dec Die Stellenzahl, auf die PI berechnet werden soll.
*/
public Pi(int dec) {
this.dec = dec;
m = new MathContext(dec + 5); //5 als Reserve
r57 = one.divide(new BigDecimal(57), m);
r239 = one.divide(new BigDecimal(239), m);
r682 = one.divide(new BigDecimal(682), m);
r12943 = one.divide(new BigDecimal(12943), m);
calculate();
}
/**
* Berechnet den Wert von PI nach der Formel von Stoermer (1896):
* pi/4 = 44 * arctan(1/57) + 7 * arctan(1/239) - 12 * arctan(1/682)
* + 24 * arctan(1/12943)
*/
private void calculate() {
BigDecimal sum1 = arctan(r57).multiply(new BigDecimal(44), m);
BigDecimal sum2 = arctan(r239).multiply(new BigDecimal(7), m);
sum1 = sum1.add(sum2, m);
sum2 = arctan(r682).multiply(new BigDecimal(12), m);
sum1 = sum1.subtract(sum2, m);
sum2 = arctan(r12943).multiply(new BigDecimal(24), m);
sum1 = sum1.add(sum2, m);
pi = sum1.multiply(four, m);
}
/**
* Berechnet den Arcustangens einer BigDecimal-Zahl.
* Benutzt die Reihe:
* arctan(x) = x - x^3/3 + x^5/5 - x^7/7 + ...
* @param arg Eingabewert
* @return arctan(arg)
*/
private BigDecimal arctan(BigDecimal arg) {
BigDecimal result = new BigDecimal(0);
BigDecimal z;
//Abschätzung der Anzahl der Iterationen
//Nach der Formel n = - d / log10(x)
// n: Anzahl der Iterationen
// d: Anzahl der Stellen fuer Genauigkeit
// x: Argument des Arcustangens
//Zwei Stellen Genauigkeit zur Sicherheit
int iter = (int) -((dec + 2)/ Math.log10(arg.doubleValue()));
//Reihenentwicklung
for (int i = 0; i < iter; i++) {
int pow = 2 * i + 1;
z = arg.pow(pow, m).divide(new BigDecimal(pow), m);
if (i % 2 == 1) {
z = z.negate();
}
result = result.add(z, m);
}
return result;
}
/**
* Gibt PI formatiert in 100er Bloecken zurueck
*/
public String toString() {
String piS = pi.toString();
StringBuffer b = new StringBuffer();
b.append("3.1");
for (int i = 1; i < dec; i++) {
if (i % 100 == 0) {
b.append("\n ");
} else if (i % 10 == 0) {
b.append(" ");
}
b.append(piS.charAt(i + 2));
}
return b.toString();
}
public static void main(String args[]) {
Pi p = new Pi(1000);
System.out.println(p);
}
}
Wieviele Threads sind hier sinnvoll?
Vergleich zwischen serieller und paralleler Berechnung:
Zeitmessungen unter Linux auf einem Intel(R) Core(TM) i3-2100 CPU @ 3.10GHz (Quadcore) (Intel(R) Core(TM) i3 CPU 560 @ 3.33GHz) mit 8GB RAM. Daneben wurden eclipse und chrome verwendet.
| Stellen | sequentiell | 4 Threads |
|---|---|---|
| 1000 | 4,395s | 2,530s |
| 2000 | 35,814s | 19,260s |
| 3000 | 126,145s | 65,693s |
| 4000 | 321,916s | 163,393s |
| 5000 | 626,394s | 323,876s |
| 6000 | 1132,480s | 568,905s |
| 7000 | 1800,781s | 912,360s |
| 8000 | 2753,654s | 1462,561s |
| 9000 | 3941,230s | 2134,741s |
| 10000 | 5527,330s | 2878,974s |
Bei der JVM kann man nicht bestimmen, ob und wie die Threads auf die CPUs (Kerne) aufgeteilt werden sollen. Die JVM und das Betriebssystem bestimmen, wie die Threads auf CPU-Kerne abgebildet werden. Das ist natürlich auch von der allgemeinen Systemlast abhängig.
Die Messungen zeigen, dass sich die Rechenzeit bei 4 Threads halbiert. Die CPU trägt zwar im Namen "Quadcore", tatsächlich ist es aber nur ein Dualcore mit vier Threads (siehe Link oben). Das bedeutet bei dieser Anwendung, dass nur zwei Threads echt parallel laufen können (weiter geteilt mit den anderen Prozessen, die auf dem System laufen).
Labels: algorithmen, Aufgabe, Java, POS1-3
Mittwoch, 3. Dezember 2014
Java Swing Währungsumrechner (POS1: 3BHIF)
Erstellen Sie eine Swing-GUI mit Texteingabefeldern und Labels für 5 verschiedene Währungen. Es soll ein Neu/Löschen und ein Umrechen-Button vorhanden sein.
Wenn der Umrechen-Button gedrückt wird, wird erkannt in welcher
Währung die Eingabe erfolgte und diese Währung in alle anderen
umgerechnet.
Der Neu/Löschen-Button löscht alle Eingaben.
Fehler wie z.B. keine Eingabe, Eingaben in mehreren Feldern,
falsche Eingabe (Text statt Zahl) u.s.w. sollen zu keinem
Programmabsturz führen sondern den Benutzer mit einem Fehlerdialog darauf hinweisen.
Labels: Aufgabe, GUI, Java, POS1-3
Mittwoch, 19. November 2014
Java Grafik/GUI (POS1: 3BHIF)
Schreiben Sie eine Klasse
PaintQuadrats, welche in einem Fenster (JFrame) eine Reihe von Quadraten zeichnet, die fortlaufend nummeriert sind:
Verwenden Sie eine Liste (
ArrayList oder ein Feld) von Knoten (Node), die "sich zeichnen" (paint(Graphics g)) können. In der Methode paintComponents(Graphics g) des JPanels müssen dann für alle Knoten deren paint()-Methode aufgerufen werden.
Experimente mit Swing-Komponenten
Alle Steuerelemente von Swing (
javax.swing.*) und AWT können auch als sogenannte Java Beans verwendet werden und damit auch dynamisch geladen werden. Eine JavaBean besitzt immer einen Standardkonstruktor. Dies ermöglicht eine standardisierte Instanzierung (vgl. Java Beans).
Im
public/3bhif/java-gui-experiments/.hg finden Sie ein Mercurial Repository, welches ein Eclipse-Projekt mit den nötigen Klassen für die folgenden Beispiele enthält. Sie können in der Schule das Repository einfach klonen:
$ hg clone /home/teachers/hp/public/3bhif/java-gui-experiments Zielverzeichnis: java-gui-experiments Aktualisiere auf Zweig default 25 Dateien aktualisiert, 0 Dateien zusammengeführt, 0 Dateien entfernt, 0 Dateien ungelöst $
Damit wird im aktuellen Verzeichnis ein Projektverzeichnis
java-gui-experiments erzeugt.
Das Projekt wird eventuell um neue Klassen ergänzt. Sie können sich die Änderungen holen:
$ cd java-gui-experiments $ hg pull /home/teachers/hp/public/3bhif/java-gui-experiments Hole von /home/teachers/hp/public/3bhif/java-gui-experiments Suche nach Änderungen Keine Änderungen gefunden $
Sollten Sie nicht auf einem Schul-PC das Repository klonen wollen, dann müssen Sie das Verzeichnis erst "mounten" und dann klonen:
$ mkdir ~/edvossh $ sshfs user@edvossh.htlwrn.ac.at:/home/teachers/hp/public/3bhif ~/edvossh $ hg clone ~/edvossh/java-gui-experiments Zielverzeichnis: java-gui-experiments Aktualisiere auf Zweig default 25 Dateien aktualisiert, 0 Dateien zusammengeführt, 0 Dateien entfernt, 0 Dateien ungelöst $
user ist natürlich mit Ihrem Usernamen in der Schule zu ersetzen. Die Eingabe des Passworts ist in obigen Beispielen nicht angezeigt,
Neue Versionen bekommen Sie sinngemäß mit
hg pull ins lokale Repository.
Zurück zu den Steuerelementen/JavaBeans.
Im Projekt
java-gui-experiments/bin können Sie Klassen nun aufrufen. Ein Beispiel zum Demonstrieren eines JButtons mit der Beschriftung "Hello World!" in einer 48 Punkt Schriftart:
$ cd java-gui-experiments/bin/ $ java je3.gui.ShowBean javax.swing.JButton 'text=Hello World!' font=helvetica-bold-48
Das sieht etwa folgendermaßen aus:
Sie können auch mehrere Steuerelemente auf der Kommandozeile angeben. Z.B.:
$ cd java-gui-experiments/bin/ $ java je3.gui.ShowBean javax.swing.JButton 'text=Hello World!' font=helvetica-bold-48\ javax.swing.JRadioButton 'text=pick me'\ java.awt.Button label=Hello javax.swing.JSlider
Experimentieren Sie mit weiteren Steuerelementen (Lesson: Getting Started with Swing).
Experimentieren Sie mit folgender Klasse. Sie zeigt die Verschachtelung verschiedener Container:
package je3.gui;
import javax.swing.*;
import java.awt.*;
/**
* A component subclass that demonstrates nested containers and components.
* It creates the hierarchy shown below, and uses different colors to
* distinguish the different nesting levels of the containers
*
* Containers---panel1----button1
* | |---panel2----button2
* | | |----panel3----button3
* | |------panel4----button4
* | |----button5
* |---button6
*/
public class Containers extends JPanel {
public Containers( ) {
this.setBackground(Color.white); // This component is white
this.setFont(new Font("Dialog", Font.BOLD, 24));
JPanel p1 = new JPanel( );
p1.setBackground(new Color(200, 200, 200)); // Panel1 is darker
this.add(p1); // p1 is contained by this component
p1.add(new JButton("#1")); // Button 1 is contained in p1
JPanel p2 = new JPanel( );
p2.setBackground(new Color(150, 150, 150)); // p2 is darker than p2
p1.add(p2); // p2 is contained in p1
p2.add(new JButton("#2")); // Button 2 is contained in p2
JPanel p3 = new JPanel( );
p3.setBackground(new Color(100, 100, 100)); // p3 is darker than p2
p2.add(p3); // p3 is contained in p2
p3.add(new JButton("#3")); // Button 3 is contained in p3
JPanel p4 = new JPanel( );
p4.setBackground(new Color(150, 150, 150)); // p4 is darker than p1
p1.add(p4); // p4 is contained in p1
p4.add(new JButton("#4")); // Button4 is contained in p4
p4.add(new JButton("#5")); // Button5 is also contained in p4
this.add(new JButton("#6")); // Button6 is contained in this component
}
}
Starten/verwenden kann man diese Klasse mit:
$ cd java-gui-experiments/bin/ $ java je3.gui.ShowBean je3.gui.Containers
Im package
je3.gui finden Sie ein paar Klassen *Layout*, welche die verschiedenen Layoutmanager demonstrieren. Dokumentation finden Sie hier: Lesson: Laying Out Components Within a Container
Ein Beispiel für das GroupLayout finden Sie in
layout.Find, welches etwa so aussieht:
Verwenden Sie diese Beispiele als Basis für eigene Projekte!
Labels: Aufgabe, GUI, Java, POS1-3, User Interface
Mittwoch, 8. Oktober 2014
Musterlösung zu Unit-Tests
Folgende Grenzfälle werden getestet:
- 0 Elemente aufnehmen, Anzahl muss gleich bleiben
- 0 Elemente löschen, Anzahl muss gleich bleiben
- negative Anzahl Elemente aufnehmen, Exception
- negative Anzahl Elemente löschen, Exception
- nicht vorhandenes Element löschen, Exception
- 10 Elemente aufnehmen, Anzahl testen
- 10 Elemente aufnehmen, Ergebnis des Iterators testen
Die Artikel (Elemente/Items) werden in der Reihenfolge und der gegebenen Anzahl gespeichert, als
addItems() aufgerufen wurde. Wird ein Element
mit der Anzahl 5 in den Warenkorb aufgenommen, so wird das Element 5 mal
gespeichert.Testklasse:
Die Kommentare wurden für diesen Beitrag weggelassen, da die Klasse ohnedies schon sehr lange ist.
import java.util.Iterator;
import junit.framework.TestCase;
public class ShoppingCartTest extends TestCase {
private ShoppingCart cart;
private ItemCount itemCount;
private Item[] items = { new Item(1), new Item(2), new Item(3) };
protected void setUp() throws Exception {
cart = new MyCart();
itemCount = (ItemCount) cart;
}
protected void tearDown() throws Exception {
}
public void testAddItems0() {
try {
cart.addItems(items[0], 0);
assertEquals("testDeleteItems0: 0 Elemente", 0, cart.itemCount());
} catch (NegativeCountException e) {
fail("testAddItem0: es dürfte keine NegativeCountException"
+ " geworfen werden");
}
}
public void testAddItemNegativ() {
try {
cart.addItems(items[0], -23);
fail("testAddItemNegativ: es müsste eine NegativeCountException"
+ " geworfen werden");
} catch (NegativeCountException e) {
}
}
public void testDeleteItems0() {
try {
cart.addItems(items[0], 1);
cart.deleteItems(items[0], 0);
assertEquals("testDeleteItems0: 1 Element", 1, cart.itemCount());
} catch (NegativeCountException e) {
fail("testDeleteItems0: es dürfte keine NegativeCountException"
+ " geworfen werden");
} catch (NoSuchItemException e) {
fail("testDeleteItems0: es dürfte keine NoSuchItemException"
+ " geworfen werden");
}
}
public void testDeleteItemsNoSuchItem() {
try {
cart.addItems(items[0], 1);
cart.deleteItems(items[1], 1);
fail("testDeleteItemsNoSuchItem: es müsste NoSuchItemException"
+ " geworfen werden");
} catch (NegativeCountException e) {
fail("testDeleteItemsNoSuchItem: es dürfte keine"
+ " NegativeCountException geworfen werden");
} catch (NoSuchItemException e) {
assertEquals("testDeleteItemsNoSuchItem: 1 Element", 1,
cart.itemCount());
}
}
public void testDeleteItemNegativ() {
try {
cart.addItems(items[0], 2);
cart.deleteItems(items[0], -23);
fail("testAddItemNegativ: es müsste eine NegativeCountException"
+ " geworfen werden");
} catch (NegativeCountException e) {
} catch (NoSuchItemException e) {
fail("testAddItemNegativ: es dürfte keine NoSuchItemException"
+ " geworfen werden");
}
}
public void testAddItems10() {
try {
cart.addItems(items[0], 3);
cart.addItems(items[1], 3);
cart.addItems(items[2], 4);
assertEquals("testAddItems10: 10 Elemente", 10, cart.itemCount());
} catch (NegativeCountException e) {
fail("testAddItems10: es dürfte keine NegativeCountException"
+ " geworfen werden");
}
}
public void testAddDeleteItems5() {
try {
cart.addItems(items[0], 3);
cart.addItems(items[1], 3);
cart.addItems(items[2], 4);
cart.deleteItems(items[0], 2);
cart.deleteItems(items[2], 2);
cart.deleteItems(items[1], 1);
assertEquals("testAddDeleteItems5: 5 Elemente", 5, cart.itemCount());
} catch (NegativeCountException e) {
fail("testAddDeleteItems5: es dürfte keine NegativeCountException"
+ " geworfen werden");
} catch (NoSuchItemException e) {
fail("testAddDeleteItems5: es dürfte keine NoSuchItemException"
+ " geworfen werden");
}
}
public void testIterator() {
try {
cart.addItems(items[0], 3);
cart.addItems(items[1], 3);
cart.addItems(items[2], 4);
Iterator<Item> iterator = cart.iterator();
int cntItems = 0;
Item[] stroredItems = new Item[10];
while (iterator.hasNext()) {
stroredItems[cntItems] = iterator.next();
cntItems++;
}
assertEquals("testIterator: 10 Elemente", 10, cntItems);
for (int i = 0; i < 3; i++) {
assertEquals("testIterator: Element sollte gleich sein",
items[0], stroredItems[i]);
System.out.println(stroredItems[i]);
}
for (int i = 3; i < 6; i++) {
assertEquals("testIterator: Element sollte gleich sein",
items[1], stroredItems[i]);
System.out.println(stroredItems[i]);
}
for (int i = 6; i < 10; i++) {
assertEquals("testIterator: Element sollte gleich sein",
items[2], stroredItems[i]);
System.out.println(stroredItems[i]);
}
} catch (NegativeCountException e) {
fail("testIterator: es dürfte keine NegativeCountException"
+ " geworfen werden");
}
}
public void testItemCount() {
try {
cart.addItems(items[0], 3);
cart.addItems(items[1], 3);
cart.addItems(items[0], 1);
cart.addItems(items[2], 4);
cart.addItems(items[0], 2);
assertEquals("testItemCount: Anzahl falsch", 6,
itemCount.itemCount(items[0]));
assertEquals("testItemCount: Anzahl falsch", 3,
itemCount.itemCount(items[1]));
assertEquals("testItemCount: Anzahl falsch", 4,
itemCount.itemCount(items[2]));
} catch (NegativeCountException e) {
fail("testItemCount: es dürfte keine NegativeCountException"
+ " geworfen werden");
} catch (NoSuchItemException e) {
fail("testItemCount: es dürfte keine NoSuchItemException"
+ " geworfen werden");
}
}
public void testItemCountNosuchItem() {
@SuppressWarnings("unused")
int cnt;
try {
cnt = itemCount.itemCount(items[0]);
fail("testItemCountNosuchItem: es müsste eine NoSuchItemException"
+ " geworfen werden");
} catch (NoSuchItemException e) {
}
try {
cart.addItems(items[1], 3);
cnt = itemCount.itemCount(items[0]);
fail("testItemCountNosuchItem: es müsste eine NoSuchItemException"
+ " geworfen werden");
} catch (NegativeCountException e) {
fail("testItemCountNosuchItem: es dürfte keine"
+ " NegativeCountException geworfen werden");
} catch (NoSuchItemException e) {
}
}
}
Implementierung des Interfaces ShoppingCart:
Zusätzlich zur originalen Aufgabenstellung wurde noch folgendes Interface implementiert, damit man einfach die Anzahl eines bestimmten Items bestimmen kann:
public interface ItemCount {
public int itemCount(Item anItem) throws NoSuchItemException;
}
Zur Speicherung der
Items wird einfach eine ArrayList verwendet.
import java.util.ArrayList;
import java.util.Iterator;
public class MyCart implements ShoppingCart, ItemCount {
private ArrayList<Item> items = new ArrayList<Item>();
public void addItems(Item anItem, int quantity)
throws NegativeCountException {
if (quantity < 0) {
throw new NegativeCountException();
}
for (int i = 0; i < quantity; i++) {
items.add(anItem);
}
}
public void deleteItems(Item anItem, int quantity)
throws NegativeCountException, NoSuchItemException {
if (quantity < 0) {
throw new NegativeCountException();
}
for (int i = 0; i < quantity; i++) {
if (!items.remove(anItem))
throw new NoSuchItemException();
}
}
public int itemCount() {
return items.size();
}
public Iterator<Item> iterator() {
return items.iterator();
}
@Override
public int itemCount(Item anItem) throws NoSuchItemException {
int cnt = 0;
if (!items.contains(anItem)) {
throw new NoSuchItemException();
}
for (Item item : items) {
if (item == anItem) {
cnt++;
}
}
return cnt;
}
}
Weiters müssen natürlich die geforderten Exceptions implementiert werden (Kommentare wurden auch hier weggelassen):
public class NegativeCountException extends Exception {
}
public class NoSuchItemException extends Exception {
}
Die Klasse für die Artikel (
Item) wurde einfach durch eine Nummer ergänzt, die beim Konstruktor angegeben werden muss. Für eine realistische Anwendung müsste diese Klasse noch erweitert werden:
public class Item {
private int itemNr = 0;
public Item(int itemNr) {
this.itemNr = itemNr;
}
@Override
public String toString() {
return "Item " + itemNr;
}
}
Labels: Java, Lösung, POS1-3, Testen
Mittwoch, 4. Dezember 2013
Java Console mit eclipse
Java bietet seit Version 1.6 eine einfachere Methode (als java.io.BufferedReader) von der Konsole zu lesen: java.io.Console. Leider kann Console nicht innerhalb von eclipse verwendet werden, da sich diese Entwicklungsumgebung direkt mit der Ein- und Ausgabe verbindet.
Es gibt eine - etwas umständliche - Lösung dieses Problems, die hier auf stackoverflow.com beschrieben wird.
Hier eine für eclipse unter Linux adaptierte Variante. Nehmen wir an, die folgende Klasse, soll mit dem Debugger getestet werden:
import java.io.Console;
public class TestDebugging {
public static void main(String[] args) {
Console console = System.console();
if (console != null) {
String line = console.readLine(">>> ");
System.out.println(line);
} else {
System.err.println("sorry, no console available!");
}
}
}
Startet man diese Klasse in eclipse (egal ob "Run" oder "Debug"), dann wird immer "sorry, no console available!" angezeigt. Startet man die Klasse in einem Terminal, so funktioniert sie problemlos:
hp@if211l $ java TestDebugging >>> test test hp@if211l $
Das Prinzip
Man muss das Java-Programm in einem Terminal starten und sich mit eclipse "remote" verbinden. Dazu verwendet man für den Start der Klasse folgenden Aufruf:
hp@if211l $ java -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y TestDebugging Listening for transport dt_socket at address: 8787
suspend=y) nun darauf, dass man sich mit dem Debugger über Port 8787 (address=8787) hin verbindet. Im eclipse ruft man dann bei geöffnetem Sourcecode der Klasse "Run>Debug Configurations" auf und legt eine neue "Remote Java Application" an.
Nachdem man (sinnvollerweise) einen Breakpoint gesetzt hat, kann man diese Debug-Konfiguration aufrufen und die Klasse debuggen. Die Ein- und Ausgabe erfolgt dann im Terminal:
Umsetzung
Um die Sache etwas komfortabler zu machen, sollte man folgendes Shell-Script erstellen:
#!/bin/bash
# launch java for external debugging
export D_PORT=8787
export D_DBG="-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,\
address=${D_PORT},server=y,suspend=y"
x-terminal-emulator -x java ${D_DBG} -cp ./bin/ $1 &
Dieses Script kann nun unter "Run>External Tools Configuration" eingerichtet werden:
Das Script startet ein Terminal-Fenster (x-terminal-emulator -x) mit dem entsprechenden Java-Aufruf (java -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y).
Debugging funktioniert wie oben beschrieben, indem man "Remote Java Application" verwendet.
Labels: eclipse, Java, POS1-2, POS1-3, POS1-4
Donnerstag, 23. Mai 2013
Graph Algorithms (POS1:3BHIF)
Anforderungen
Die Nummerierung dient nur, um auf die einzelnen Anforderungen zu referenzieren.- Anlegen von Knoten - Knoten sollen durch einfachen Klick auf die leere Zeichenfläche angelegt werden. Die Beschriftung soll automatisch erfolgen (A, B, C, ...). Die Knoten dürfen sich nicht überlappen.
- Anlegen von gerichteten Kanten - durch Ziehen mit der rechten Maustaste soll eine Kante von einem Knoten zu einem anderen Knoten gezeichnet werden können (gerade Linie mit Pfeilspitze). Alternativ kann die linke Maustaste mit gedrückter
Strg-Taste verwendet werden. - Die Gewichtung einer Kante soll eingegeben werden können.
- Die Gewichtung einer Kante soll geändert werden können.
- Knoten sollen durch ziehen verschoben werden können (linke Maustaste), die Kanten von/zu diesem Knoten müssen entsprechend neu gezeichnet werden können. Die Knoten dürfen sich nicht überlappen!
- Knoten sollen (de-)selektiert/markiert werden können (Mausklick). Markierung/Selektion soll umgeschaltet werden (toggle).
- Kanten sollen (de-)selektiert/markiert werden können (Mausklick). Markierung/Selektion soll umgeschaltet werden (toggle).
- Markierte Elemente sollen gelöscht werden können (Menüpunkt, Shortcut
Entf). - Ein Graph soll in einer Datei gespeichert werden können.
- Ein Graph soll aus einer Datei geladen werden können.
- Die Zeichenfläche soll gelöscht werden können.
- Im Hilfemenü soll eine Infobox mit Ihrem Namen, der Klasse und dem Erstellungsjahr aufgerufen werden können.
- Optional: Eine Hilfe zur Bedienung (Dialogfenster mit HTML-Text).
- Optional: Die Sprache soll geändert werden können (Englisch, Deutsch, weitere mit Sprachdateien). Standard ist die Systemeinstellung.
- Scrollbars sollen eingeblendet werden, wenn das Fenster kleiner als die benötigte Zeichenfläche ist.
- Ein Graph soll gedruckt werden können. Gegebenenfalls auf eine Seite skalieren.
- Simulation der Tiefensuche - Ist ein Knoten markiert, so soll die Tiefensuche beginnend nur mit diesem Knoten durchgeführt werden und die einzelnen Schritte dargestellt werden (weiß/grau/schwarz). Ist kein Knoten markiert, so soll der Algorithmus auf alle Knoten angewendet werden.Verwenden Sie dazu einen Thread.
- Simulation der Breitensuche - Ist ein Knoten markiert, so soll die Breitensuche beginnend nur mit diesem Knoten durchgeführt werden und die einzelnen Schritte dargestellt werden (weiß/grau/schwarz). Ist kein Knoten markiert, so soll der Algorithmus auf alle Knoten angewendet werden.Verwenden Sie dazu einen Thread.
- Optional: Die Simulation(en) sollen abgebrochen werden können.
- Optional: Es soll eine undo/redo-Funktionalität implementiert werden (Command Pattern).
- Das Programm soll als "ausführbare" Jar-Datei geliefert werden, d.h. alle nötigen Klassen, Bibliotheken, Grafiken und Konfigurationsdateien (Sprachdateien) sollen sich in dem Jar-Archiv befinden. Aufruf soll nur über
java -jar graph.jarmöglich sein (meist kann man die graphische Oberfläche so einstellen, dass ein Klick auf die Datei genügt).
Links
- Graphen und Graphenalgorithmen
- Graphentheorie (Wikipedia)
- Tiefensuche (Wikipedia)
- Breitensuche (Wikipedia)
- Command Pattern (Wikipedia)
Literatur
- Robert Sedgewick; Kevin Wayne: Algorithms, Fourth Edition (Addison-Wesley Professional)
- Gunter Saake; Kai-Uwe Sattler: Algorithmen und Datenstrukturen, 4th Edition (dpunkt)
- Eric Freeman; Elisaebth Freeman; Kathy Sierra; Bert Bates: Entwurfsmuster von Kopf bis Fuß (O'Reilly Verlag)
Labels: algorithmen, Aufgabe, GUI, Java, POS1-3, User Interface
Donnerstag, 7. März 2013
Deadlocks (POS1: 3BHIF)
Implementieren Sie in Java eine Simulation des Philosophenproblems (Aufgabenstellung über den Link).
Zeigen Sie das Auftreten eines Deadlocks.
Entwickeln Sie eine Lösung ohne Deadlock.
Labels: algorithmen, Aufgabe, Informatik, Java, POS1-3
Mittwoch, 27. Februar 2013
Berechnungen mit Threads parallelisieren (POS1: 3BHIF)
π kann mit Hilfe der folgenden Formel von Strömer (1986) berechnet werden:
π / 4 = 44 · arctan 1/57 + 7 · arctan 1/239 - 12 · arctan 1/682 + 24 · arctan 1/12943
Der Arcustangens wird mit der folgenden Reihenentwicklung
arctan(x) = x - x3/3 + x5/5 - x7/7 + ...
berechnet.
Folgendes Java-Programm verwendet die Klasse BigDecimal zur Berechnung von π. Es soll mittels Threads parallelisiert werden.
import java.math.*;
/**
* @author Hans Joachim Pflug, RZ, RWTH Aachen
*
* Berechnet PI auf eine beliebige Anzahl von Stellen genau
*/
public class Pi {
private int dec; //Anzahl der Dezimalstellen
private BigDecimal pi = new BigDecimal(0); //Ergebnis
private MathContext m; //Zur Bestimmung der Laenge der Brueche
private static final BigDecimal one = new BigDecimal(1);
private static final BigDecimal four = new BigDecimal(4);
private BigDecimal r57; // 1/57
private BigDecimal r239; // 1/239
private BigDecimal r682; // 1/682
private BigDecimal r12943; // 1/12943
/**
* Erzeugt ein Objekt mit PI auf die angegebene Stellenzahl genau
* @param dec Die Stellenzahl, auf die PI berechnet werden soll.
*/
public Pi(int dec) {
this.dec = dec;
m = new MathContext(dec + 5); //5 als Reserve
r57 = one.divide(new BigDecimal(57), m);
r239 = one.divide(new BigDecimal(239), m);
r682 = one.divide(new BigDecimal(682), m);
r12943 = one.divide(new BigDecimal(12943), m);
calculate();
}
/**
* Berechnet den Wert von PI nach der Formel von Stoermer (1896):
* pi/4 = 44 * arctan(1/57) + 7 * arctan(1/239) - 12 * arctan(1/682)
* + 24 * arctan(1/12943)
*/
private void calculate() {
BigDecimal sum1 = arctan(r57).multiply(new BigDecimal(44), m);
BigDecimal sum2 = arctan(r239).multiply(new BigDecimal(7), m);
sum1 = sum1.add(sum2, m);
sum2 = arctan(r682).multiply(new BigDecimal(12), m);
sum1 = sum1.subtract(sum2, m);
sum2 = arctan(r12943).multiply(new BigDecimal(24), m);
sum1 = sum1.add(sum2, m);
pi = sum1.multiply(four, m);
}
/**
* Berechnet den Arcustangens einer BigDecimal-Zahl.
* Benutzt die Reihe:
* arctan(x) = x - x^3/3 + x^5/5 - x^7/7 + ...
* @param arg Eingabewert
* @return arctan(arg)
*/
private BigDecimal arctan(BigDecimal arg) {
BigDecimal result = new BigDecimal(0);
BigDecimal z;
//Abschätzung der Anzahl der Iterationen
//Nach der Formel n = - d / log10(x)
// n: Anzahl der Iterationen
// d: Anzahl der Stellen fuer Genauigkeit
// x: Argument des Arcustangens
//Zwei Stellen Genauigkeit zur Sicherheit
int iter = (int) -((dec + 2)/ Math.log10(arg.doubleValue()));
//Reihenentwicklung
for (int i = 0; i < iter; i++) {
int pow = 2 * i + 1;
z = arg.pow(pow, m).divide(new BigDecimal(pow), m);
if (i % 2 == 1) {
z = z.negate();
}
result = result.add(z, m);
}
return result;
}
/**
* Gibt PI formatiert in 100er Bloecken zurueck
*/
public String toString() {
String piS = pi.toString();
StringBuffer b = new StringBuffer();
b.append("3.1");
for (int i = 1; i < dec; i++) {
if (i % 100 == 0) {
b.append("\n ");
} else if (i % 10 == 0) {
b.append(" ");
}
b.append(piS.charAt(i + 2));
}
return b.toString();
}
public static void main(String args[]) {
Pi p = new Pi(1000);
System.out.println(p);
}
}
Wieviele Threads sind hier sinnvoll?
Vergleich zwischen serieller und paralleler Berechnung:
Zeitmessungen unter Linux auf einem Intel(R) Core(TM) i3-2100 CPU @ 3.10GHz (Quadcore) (Intel(R) Core(TM) i3 CPU 560 @ 3.33GHz) mit 8GB RAM. Daneben wurden eclipse und chrome verwendet.
| Stellen | sequentiell | 4 Threads |
|---|---|---|
| 1000 | 4,395s | 2,530s |
| 2000 | 35,814s | 19,260s |
| 3000 | 126,145s | 65,693s |
| 4000 | 321,916s | 163,393s |
| 5000 | 626,394s | 323,876s |
| 6000 | 1132,480s | 568,905s |
| 7000 | 1800,781s | 912,360s |
| 8000 | 2753,654s | 1462,561s |
| 9000 | 3941,230s | 2134,741s |
| 10000 | 5527,330s | 2878,974s |
Bei der JVM kann man nicht bestimmen, ob und wie die Threads auf die CPUs (Kerne) aufgeteilt werden sollen. Die JVM und das Betriebssystem bestimmen, wie die Threads auf CPU-Kerne abgebildet werden. Das ist natürlich auch von der allgemeinen Systemlast abhängig.
Die Messungen zeigen, dass sich die Rechenzeit bei 4 Threads halbiert. Die CPU trägt zwar im Namen "Quadcore", tatsächlich ist es aber nur ein Dualcore mit vier Threads (siehe Link oben). Das bedeutet bei dieser Anwendung, dass nur zwei Threads echt parallel laufen können (weiter geteilt mit den anderen Prozessen, die auf dem System laufen).
Labels: algorithmen, Aufgabe, Java, POS1-3
Einführung in die O-Notation (Big O Notation)
Dies ist eine Mini-Einführung in die O-Notation. Wer es genau wissen will, soll gleich unten bei der Literaturliste bzw. den Links weitermachen.
Die O-Notation (engl. Big O Notation) wird in der Informatik zu Beschreibung der Komplexität (und damit der Laufzeit) eines Algorithmus. Die O-Notation beschreibt den schlechtesten Fall (worst-case scenario). Sie beschreibt die Ausführungszeit oder den Speicheraufwand, den ein Algorithmus benötigt.
Alle, die Programming Pearls (Programming Pearls (ACM Press)) oder irgend ein anderes Informatik Buch lesen und kein fundiertes mathematisches Wissen haben, werden sich den Kopf stoßen, sobald sie Kapitel erreichen, in denen so komische Dinge wie O(N log N) vorkommen. Ich hoffe, dieser Beitrag wird zu einem besseren Verständnis der Grundlagen zur O-Notation verhelfen.
Für Programmierer sind wahrscheinlich kurze Code-Beispiele am Besten geeignet die O-Notation zu verstehen. Die Beispiele zeigen die übliche Reihenfolge der Komplexität, von einfachen bis zu aufwendigen Beispielen.
O(1)
O(1) beschreibt einen Algorithmus, der immer die selbe Zeit (oder den selben Speicher) benötigt, unabhängig von der Größe der Eingabedaten.
boolean isFirstElementNull(String[] strings) {
if (strings[0] == null) {
return true;
}
return false;
}
Es ist (hoffentlich) offensichtlich, dass diese Funktion immer gleich lange braucht, egal wie groß das String-Array ist (Außer natürlich, wenn strings == null oder strings.length == 0 ist, denn da kommt es zu einer Exception! Diese Sonderfälle lassen wir weg.).
O(N)
O(N) beschreibt Algorithmen, deren Laufzeit linear in direkter Proportionalität zur Größe des Eingabedatensatzes wachsen (z.B. dreimal so große Eingabe bewirkt eine dreimal so lange Laufzeit). Das folgende Beispiel zeigt auch, dass die O-Notation den schlechtesten (worst-case) Fall beschreibt. Die gesuchte Zahl kann natürlich in jedem Schleifendurchlauf gefunden werden und die Funktion frühzeitig beenden, aber die O-Notation beschreibt die obere Grenze, bei der die maximale Anzahl Schleifendurchläufe benötigt wird.
boolean containsValue(int[] values, int value) {
for (int i = 0; i < values.length; i++) {
if (value == values[i]) { // always the same time!
return true;
}
}
return false;
}
O(N2)
O(N2) repräsentiert Algorithmen, deren Laufzeit direkt proportional zum Quadrat der Größe des Eingabedatensatzes sind. Das ist üblicherweise bei verschachtelten Schleifen der Fall. Tiefer verschachtelte Schleifen resultieren demnach in O(N3), O(N4) etc.
boolean containsDuplicates(int[] values) {
for (int i = 0; i < values.length; i++) {
for (int j = 0; j < values.length; j++) {
if (i == j) { // don't compare self
continue;
}
if (values[i] == values[j]) {
return true;
}
}
}
return false;
}
O(2N)
O(2N) beschreibt Algorithmen, deren Laufzeit sich für jedes weitere Element der Eingabedaten verdoppelt. Die Ausführungszeit einer O(2N) Funktion wird sehr schnell sehr groß. Das folgende Beispiel zeigt einen Algorithmus, der alle möglichen Zeichenfolgen eines Strings liefert (permutiert). Permutationen sind üblicherweise exponentiell (allgemein O(kN) für ein bestimmtes k).
public static void permutation(String str) {
permutation("", str);
}
private static void permutation(String prefix, String str) {
int n = str.length();
if (n == 0) {
System.out.println(prefix);
} else {
for (int i = 0; i < n; i++) {
permutation(prefix + str.charAt(i),
str.substring(0, i) + str.substring(i+1, n));
}
}
}
Primzahlenbestimmung hat auch exponentiellen Aufwand (O(kN)).
Logarithmus O(log N)
Ich werde logarithmisches Verhalten an einem üblichen Beispiel erklären:
Denken Sie an ein Telefonbuch, welches Einträge sortiert nach Name, Vorname und Adresse (in dieser Reihenfolge) enthält. Wie würde man die Suche nach der Telefonnummer eines bestimmten Teilnehmers mit gegebenem Namen programmieren? Nehmen wir an, das Telefonbuch hätte 1.000.000 Einträge.
Wenn man linear sucht, dann ist der Aufwand O(N), also maximal 1.000.000 Schleifendurchläufe (im Durchschnitt 500.000). Geschickter ist jedoch die Binäre Suche, bei der man zunächst den 500.000 Eintrag nimmt und prüft. Ist das der gesuchte Name, dann ist man fertig. Ist der gesuchte Name größer (also alphabetisch weiter hinten), dann teilt man die obere Hälfte wieder in zwei Teile und vergleicht mit dem mittleren Eintrag (750.000). Ist der Eintrag nun größer als der gesuchte, dann teilt man die untere Hälfte und nimmt die Mitte ((750.000 - 500.000) / 2 + 500.000 = 625.000) usw. Nach spätestens 20 Teilungen hat man den Namen gefunden (oder er ist nicht im Telefonbuch).
Bei 3 Einträgen benötigt man höchstens 2 Schritte, bei 7 höchstens 3, bei 15 höchstens 4, bei 1000 höchstens 10 und bei 1.000.000 höchstens 20 Schritte.
Der Aufwand ist also O(log N), wobei es egal ist, welche Basis man für den Logarithmus nimmt, weil sich die Logarithmen nur durch Konstante unterscheiden, welche man bei der O-Notation weglassen kann. Die O-Notation gilt nämlich nur für große Werte von N, so dass sich Konstante praktisch nicht mehr auswirken. Die binäre Suche hat eigentlich den Aufwand von log2 N, welcher sich nur durch den konstanten Wert log 2 (Logarithmus von 2) unterscheidet.
// nums ... sorted array
public static int binarySearch(int[] nums, int check) {
int hi = nums.length - 1;
int lo = 0;
while (hi >= lo) {
guess = lo + ((hi - lo) / 2);
if (nums[guess] > check) {
hi = guess - 1;
} else if (nums[guess] < check) {
lo = guess + 1;
} else {
return guess;
}
}
return -1;
}
Obiges Beispiel zeigt die binäre Suche mit einem sortierten int-Arrays nums und der zu suchenden Zahl check. Zurückgeliefert wird der Index oder -1, wenn die Zahl nicht in dem Array vorkommt.
Der log N ist die Umkehrung von 10N und log2 N die Umkehrung von 2N.
Es gilt:
O(1) < O(log N) < O(N) < O(N log N) < O(N2) < O(N3) < O(kN)
Quellen
Jon Bentley: Programming Pearls
Binäre Suche (Wikipedia)
Donald E. Knuth: The Art of Computer Programming
BIG-O Notation
Plain English explanation of Big O
Big O, how do you calculate/approximate it?
A Beginner’s Guide to Big O Notation
Labels: algorithmen, Informatik, POS1-2, POS1-3
Mittwoch, 20. Februar 2013
JTable (POS1: 3BHIF)
Labels: Aufgabe, GUI, Java, POS1-3, User Interface
Mittwoch, 16. Januar 2013
Java zeichnen mit Swing (POS1: 3BHIF)
Vervollständigen Sie die Zeichen-Applikation aus der letzten Aufgabe so, dass das hier ersichtliche Verhalten ermöglicht wird (Sie benötigen ein Java-Plugin für den Browser):
Das Applet ist nicht perfekt, aber die wichtigste Funktionalität ist vorhanden.
- Linke Maustaste zum Erzeugen und verschieben von Knoten.
- Mittlere Maustaste zum Löschen von Knoten.
- Rechte Maustaste (ziehen) zum Zeichnen von Kanten zwischen den Knoten.
Beachten Sie, dass sich Knoten nicht überlappen dürfen.
Das Fenster soll ein Menü mit
File und Help haben.
Im
File-Menü sollen folgende Funktionen ausgewählt werden können:
Newzum Anlegen eines neuen Graphen (löscht Zeichenfläche).SaveundSave as...zum Speichern des Graphen (Sie können die Funktionalität zunächst weglassen).Exitzum beenden.
Help-Menu soll einen Eintrag mit About haben, mit dem ein Info-Dialog geöffnet werden soll. Dieser Dialog soll Name, Klasse, eine Kurzbeschreibung des Programmes und eine Versionsnummer enthalten.
Erstellen Sie eine Liste der (Teil-)Aufgaben und schätzen Sie den Aufwand.
Stellen Sie sich Fragen wie:
- Welche Bereiche sind wichtig?
- Wo werden wahrscheinlich Probleme auftreten?
- Welche Teile könnte man vereinfachen?
Die Pfeile sind möglicherweise kompliziert, aber es ist wichtig, die Richtung der Kanten irgendwie zu kennzeichnen.
Labels: Aufgabe, GUI, Java, POS1-3
Mittwoch, 9. Januar 2013
Java Grafik/GUI (POS1: 3BHIF)
Schreiben Sie eine Klasse
PaintQuadrats, welche in einem Fenster (JFrame) eine Reihe von Quadraten zeichnet, die fortlaufend nummeriert sind:
Verwenden Sie eine Liste (
ArrayList oder ein Feld) von Knoten (Node), die "sich zeichnen" (paint(Graphics g)) können. In der Methode paintComponents(Graphics g) des JPanels müssen dann für alle Knoten deren paint()-Methode aufgerufen werden.
Experimente mit Swing-Komponenten
Alle Steuerelemente von Swing (
javax.swing.*) und AWT können auch als sogenannte Java Beans verwendet werden und damit auch dynamisch geladen werden. Eine JavaBean besitzt immer einen Standardkonstruktor. Dies ermöglicht eine standardisierte Instanzierung (vgl. Java Beans).
Im
public/3bhif/java-gui-experiments/.hg finden Sie ein Mercurial Repository, welches ein Eclipse-Projekt mit den nötigen Klassen für die folgenden Beispiele enthält. Sie können in der Schule das Repository einfach klonen:
$ hg clone /home/teachers/hp/public/3bhif/java-gui-experiments Zielverzeichnis: java-gui-experiments Aktualisiere auf Zweig default 25 Dateien aktualisiert, 0 Dateien zusammengeführt, 0 Dateien entfernt, 0 Dateien ungelöst $
Damit wird im aktuellen Verzeichnis ein Projektverzeichnis
java-gui-experiments erzeugt.
Das Projekt wird eventuell um neue Klassen ergänzt. Sie können sich die Änderungen holen:
$ cd java-gui-experiments $ hg pull /home/teachers/hp/public/3bhif/java-gui-experiments Hole von /home/teachers/hp/public/3bhif/java-gui-experiments Suche nach Änderungen Keine Änderungen gefunden $
Sollten Sie nicht auf einem Schul-PC das Repository klonen wollen, dann müssen Sie das Verzeichnis erst "mounten" und dann klonen:
$ mkdir ~/edvossh $ sshfs user@edvossh.htlwrn.ac.at:/home/teachers/hp/public/3bhif ~/edvossh $ hg clone ~/edvossh/java-gui-experiments Zielverzeichnis: java-gui-experiments Aktualisiere auf Zweig default 25 Dateien aktualisiert, 0 Dateien zusammengeführt, 0 Dateien entfernt, 0 Dateien ungelöst $
user ist natürlich mit Ihrem Usernamen in der Schule zu ersetzen. Die Eingabe des Passworts ist in obigen Beispielen nicht angezeigt,
Neue Versionen bekommen Sie sinngemäß mit
hg pull ins lokale Repository.
Zurück zu den Steuerelementen/JavaBeans.
Im Projekt
java-gui-experiments/bin können Sie Klassen nun aufrufen. Ein Beispiel zum Demonstrieren eines JButtons mit der Beschriftung "Hello World!" in einer 48 Punkt Schriftart:
$ cd java-gui-experiments/bin/ $ java je3.gui.ShowBean javax.swing.JButton 'text=Hello World!' font=helvetica-bold-48
Das sieht etwa folgendermaßen aus:
Sie können auch mehrere Steuerelemente auf der Kommandozeile angeben. Z.B.:
$ cd java-gui-experiments/bin/ $ java je3.gui.ShowBean javax.swing.JButton 'text=Hello World!' font=helvetica-bold-48\ javax.swing.JRadioButton 'text=pick me'\ java.awt.Button label=Hello javax.swing.JSlider
Experimentieren Sie mit weiteren Steuerelementen (A Visual Guide to Swing Components).
Experimentieren Sie mit folgender Klasse. Sie zeigt die Verschachtelung verschiedener Container:
package je3.gui;
import javax.swing.*;
import java.awt.*;
/**
* A component subclass that demonstrates nested containers and components.
* It creates the hierarchy shown below, and uses different colors to
* distinguish the different nesting levels of the containers
*
* Containers---panel1----button1
* | |---panel2----button2
* | | |----panel3----button3
* | |------panel4----button4
* | |----button5
* |---button6
*/
public class Containers extends JPanel {
public Containers( ) {
this.setBackground(Color.white); // This component is white
this.setFont(new Font("Dialog", Font.BOLD, 24));
JPanel p1 = new JPanel( );
p1.setBackground(new Color(200, 200, 200)); // Panel1 is darker
this.add(p1); // p1 is contained by this component
p1.add(new JButton("#1")); // Button 1 is contained in p1
JPanel p2 = new JPanel( );
p2.setBackground(new Color(150, 150, 150)); // p2 is darker than p2
p1.add(p2); // p2 is contained in p1
p2.add(new JButton("#2")); // Button 2 is contained in p2
JPanel p3 = new JPanel( );
p3.setBackground(new Color(100, 100, 100)); // p3 is darker than p2
p2.add(p3); // p3 is contained in p2
p3.add(new JButton("#3")); // Button 3 is contained in p3
JPanel p4 = new JPanel( );
p4.setBackground(new Color(150, 150, 150)); // p4 is darker than p1
p1.add(p4); // p4 is contained in p1
p4.add(new JButton("#4")); // Button4 is contained in p4
p4.add(new JButton("#5")); // Button5 is also contained in p4
this.add(new JButton("#6")); // Button6 is contained in this component
}
}
Im package
je3.gui finden Sie ein paar Klassen *Layout*, welche die verschiedenen Layoutmanager demonstrieren. Dokumentation finden Sie hier: Lesson: Laying Out Components Within a Container
Ein Beispiel für das GroupLayout finden Sie in
layout.Find, welches etwa so aussieht:
Verwenden Sie diese Beispiele als Basis für eigene Projekte!
Labels: Aufgabe, Java, POS1-3, User Interface
Sonntag, 6. Januar 2013
Abgaben im Schuljahr 2012/13
Sollten wir auch im Schuljahr 2013/14 kein brauchbares zentrales Repository haben, dann werden wir Dateinamen mit 2013 im Namen haben...
Labels: allgemeines, Aufgabe, POS1-2, POS1-3
Mittwoch, 5. Dezember 2012
Aufgabe Syntaxanalyse (POS1: 3BHIF)
- Untersuchen Sie die Syntax einer Gleitkomma-Konstanten in Java.
- Konstruieren Sie dazu einen endlichen Automaten, der diese Konstanten akzeptiert.
- Entwicklen Sie daraus den Minimalautomaten.
- Ausgehend von diesem Minimalautomaten schreiben Sie ein Programm (Automat mit
switchund tabellengesteuerter Automat), das eine Zeichenkette akzeptiert, falls diese eine ganzzahlige Konstante der untersuchten Programmiersprache ist. - Testen Sie die Funktionsweise des Automaten (beider Implementierungen) mit Unit-Tests.
double r = 1.e3; double s = 1.; double u = 1e3; double v = +.2; double w = -.2e-2; double x = .1; double y = -.2; double z = -123.123e-2;
Die Variablendeklarationen sollen nicht analysiert werden.
Abgabe:
Der Automat sowie der dazugehörige Minimalautomat müssen grafisch dargestellt werden (scannen Sie eine Skizze oder zeichnen Sie mit einem Grafikprogramm). Die Konstruktionsschritte müssen ebenfalls abgegeben werden. Am Besten erzeugen Sie ein PDF, welches die Automaten und die Konstruktion enthält. Sie können auch einzelne Grafiken (PNG, JPEG) abgeben. Jedenfalls muss jedes Dokument Klasse und Namen enthalten.
Diese Dokumente müssen im Projektordner enthalten sein (Versioniert!).
Geben Sie das Projekt in gewohnter Form ab.
Labels: Aufgabe, Informatik, Java, POS1-3
Donnerstag, 29. November 2012
Einführung Objektorientiertes Programmieren
Es folgt ein Beispiel:
Aufgabe: Finden Sie Klassen bzw. Objekte zu folgenden Begriffen: Leo, Tiger, Samurai, Elefant, Taigon, Benjamin Blümchen, Tier, Löwe
Lösung: Beim Programmieren muss man aus einer Aufgabenstellung immer wieder Objekte und Klassen identfizieren, die man zur Implementierung benötigt. Man kann z.B. alle Subjekte der Aufgabenstellung suchen und sich dazu fragen "ist das ein Objekt oder sind das viele Objekte?". In letzterem Fall hat man einen Kandidaten für eine Klasse. Sonst ist es ein Objekt. Nicht alle Objekte werden für das Programm benötigt.
Bei diesem Beispiel ist nur eine Liste von Tieren und Namen gegeben. Namen sind normalerweise Objekte, die verschiedenen Tiere sind natürlich Klassen.
Wir werden also die Klassen Tier, Tiger, Löwe und Elefant haben. Die anderen Begriffe sind konkrete Namen. Weiters können wir die "is-a" ("ist-ein") Beziehung Tiger is-a Tier, Löwe is-a Tier und Elefant is-a Tier ausmachen.
Damit ergibt sich folgendes Bild:

Kästchen mit unterstrichenen Namen stellen konkrete Objekte dar.
Die strichlierten Pfeile stellen die Instanzierung dar. Sie sind in Pfeilrichtung zu lesen: Leo ist-ein-konkreter Löwe (Leo is-instance-of Löwe) oder Samurai ist-ein-konkreter Tiger.
Die Pfeile mit Dreiecksspitzen und durchgehenden Linien stehen für die Vererbung (is-a, ist-ein), Beispielsweise Löwe ist-ein Tier (Löwe is-a Tier).
Die Klassen könnte man so implementieren (z.B. Scrapbook):
class Tier {
String name;
public String toString() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Löwe extends Tier {
}
class Elefant extends Tier {
}
class Tiger extends Tier {
}
Löwe leo = new Löwe();
leo.setName("Leo");
Elefant benjamin = new Elefant();
benjamin.setName("Benjamin Blümchen");
Tiger samurai = new Tiger();
samurai.setName("Samurai");
Tiger taigon = new Tiger();
taigon.setName("Taigon");
System.out.println(leo);
System.out.println(benjamin);
System.out.println(samurai);
System.out.println(taigon);
Die Ausgabe könnte so aussehen:
Leo
Benjamin Blümchen
Samurai
Taigon
In Python könnte man das Beispiel so implementieren:
class Tier:
def __init__(self):
self.name = ""
def setName(self, name):
self.name = name
def __str__(self): # toString() in Java
return self.name
class Loewe(Tier):
pass
class Elefant(Tier):
pass
class Tiger(Tier):
pass
leo = Loewe()
leo.setName("Leo")
benjamin = Elefant()
benjamin.setName("Benjamin Blümchen")
samurai = Tiger()
samurai.setName("Samurai")
taigon = Tiger()
taigon.setName("Taigon");
print(leo)
print(benjamin)
print(samurai)
print(taigon)
Labels: Java, POS1-2, POS1-3, Python
Montag, 12. November 2012
Python Objekte, Aufrufstack, Klassen (POS1)
Objekte
In Python sind alle Elemente Objekte. Folgendes Beispiel enthält ein paar solche Objekte:def fun(x): # auch ein Objekt!
y = x * x
return y
f2 = fun # f1 ist eine Refernz auf fun
lst = [1, 2, fun] # Liste mit 3 Objekten
l2 = lst # l2 ist eine Refernz auf obige Liste
erg = fun(2)
print(erg)
erg = f2(3)
print(erg)
print(f2 == fun)
print(l2 == lst)
print(lst)
Folgendes Bild zeigt den Speicher, wenn das Progarmm in der Zeile 3 angelangt ist, nach dem
f2(3) (ist ja fun(3)) in der Zeile 12 Aufgerufen wurde.
Sie können den Ablauf unter folgendem Link testen: objects.py
Sie sehen auf der rechten Seite die Objekte, welche Python angelegt hat. Der linke Bereich ("Frames") zeigt den Speicher, den Python für die (globalen) Variablen anlegt sowie den Speicher für die aufgerufene Funktion. Für jede Funktion wird so ein Bereich (Frame) angelegt und beim beenden wieder zerstört.
Pfeile stellen Referenzen dar. Zum Beispiel stellt der Pfeil von
lst zum Listenobjekt [1, 2, fun] dar. Auch l2 ist eine Referenz zu dieser Liste.
In dieser Liste gibt es eine weitere Referenz zur Funktion
fun. Funktionsnamen sind also Referenzen zu "Funktions-Objekten" (die den Code der Funktion "enthalten").
Aufrufstack
Beim folgenden Beispiel können Sie sehen, wie Frames sich bei den Aufrufen der Funktionfact() stapeln und dann in Folge wieder zerstört werden:
def fact(n):
if n > 1:
return n * fact(n - 1)
else:
return 1
f = fact(5)
print(f)
Folgendes Bild zeigt die Frames, wenn das Programm die Zeile 5 erreicht (aber nicht ausgeführt) hat. Sie sehen einen Stapel von Frames. Ein Frame pro Aufruf. Diese Frames werden anschließend bei jedem
return wieder zerstört. Man nennt diesen Stapel Aufrufstack:
Sie können den Ablauf unter folgendem Link testen: fact.py
Klassen
Bei Klassen handelt es sich um benutzerdefinierte Typen. D.h. es werden Typen ange- legt, für die benutzerdefinierte Methoden (also objektgebundene Funktionen) definiert werden können:class Car:
def __init__(self, cartype, kind, serNr):
self.cartype = cartype
# die Attribute type,
self.kind = kind
# kind und
self.serNr = serNr
# serNr sind in der Instanz
def maxSpeed(self):
if self.kind == "pickup": # Wenn Pickup, dann langsamer
return 100.0
else:
return 130.0
passat1 = Car("VW/Passat", "regular_car", 11142)
jetta1 = Car("VW/Jetta", "small_car", 11143)
ram1 = Car("Dodge/Ram", "pickup", 22242)
fleet = [passat1, jetta1, ram1] # Fuhrpark
for car in fleet:
print(car.cartype, car.maxSpeed(), "km/h")
Mittels der Anweisung
class wird ein neuer Typ definiert. Dieser heißt in diesem Fall ‘Car’. Wir sehen, dass es sich bei der class Anweisung ebenfalls um eine Block - Anweisung handelt, da nach der Bezeichnung der Klasse (Car) der Doppelpunkt folgt und die danach folgenden Methoden eingerückt sind.
Die einzelnen Methoden werden wie Funktionen definiert. D.h. die Definition erfolgt durch das Schlüsselwort
def. Als Unterschied gibt wird jedoch ein zusätzlicher Parameter an erster Stelle angeführt, der zur Ausführungszeit der Methode mit der Referenz des aktuellen Objektes vom Laufzeitsystem belegt wird.
Das folgende Bild zeigt, wie Python den Speicher für die Objekte und die Klasse angelegt hat.
Sie können das Programm hier ausführen car.py.
Die Ausgabe wäre wie folgt:
VW/Passat 130.0 km/h VW/Jetta 130.0 km/h Dodge/Ram 100.0 km/h
Durch Aufrufen der Klasse (durch Angabe des Klassennamens) und Übergabe der Initialisierungsparameter wird eine neue Instanz angelegt. Die übergebenen Parameter werden verwendet, um die spezielle Methdode
__init__ nach dem Anlegen der Instanz aufzurufen. D.h. zuerst wird das neue Objekt angelegt und danach wird diese spezielle Methode aufgerufen. Dazu wird auch dieser Methode die Referenz auf das gerade angelegte Objekt mitgegeben (als Parameter self).
D.h. in der Methode
__init__ werden die Attribute cartype, kind und serNr für das gerade erzeugte Objekt angelegt. D.h. ab diesem Zeitpunkt besitzt das Objekt diese Attribute. Die Methode maxSpeed kann danach ebenfalls auf diese Attribute zugreifen.
Attribute gehören also zu einem Objekt und können in den Methoden abgefragt und verändert werden. Anders als in anderen Programmiersprachen ist es jedoch so, dass der Zugriff auf diese Attribute nicht nur ausschließlich in den Methoden erfolgen kann. Diese Attribute können prinzipiell auch von außerhalb gelesen und verändert werden, wie man in obigem Beispiel in der Zeile 21 (
print(...)) sehen kann.
Labels: allgemeines, POS1-2, POS1-3, Python
Donnerstag, 18. Oktober 2012
Aufgabe Unit-Tests (POS1: 3BHIF)
In einem Online-Shop wird unter anderem ein Warenkorb benötigt. Schreiben Sie Unit-Tests zu folgendem Interface (welches Sie natürlich auch implementieren müssen):
import java.util.Iterator;
/**
* @author (c) 2012, Harald R. Haberstroh 18.10.2012
*/
public interface ShoppingCart {
/**
* legt die übergebene Anzahl von Elementen in den Warenkorb
*
* @param anItem
* Element
* @param quantity
* Anzahl
* @throws NegativeCountException
* negative Anzahl gibt's nicht
*/
public void addItems(Item anItem, int quantity) throws NegativeCountException;
/**
* entfernt die Anzahl von Elementen vom Warenkorb.
*
* @param anItem
* welches Element
* @param quantity
* Anzahl
* @throws NegativeCountException
* negative Anzahl gibt's nicht
* @throws NoSuchItemException
* das Element existiert nicht im Warenkorb
*/
public void deleteItems(Item anItem, int quantity)
throws NegativeCountException, NoSuchItemException;
/**
* wieviele Element gibt's überhaupt
*
* @return Anzahl Elemente
*/
public int itemCount();
/**
* Iterator für alle Elemente (siehe Collection-API).
*
* @return Iterator über alle Elemente
*/
public Iterator<Item> iterator();
}
Denken Sie bei der Erstellung der Tests an Grenzfälle, Sonderfälle und erst dann an den "Normalfall".
Sonntag, 30. September 2012
Abgaben (POS1: 2BHIF, 3BHIF)
- Erzeugen Sie ein Mercurial-Repository (
hg init). - Arbeiten Sie an der Lösung der Aufgabe und nehmen Sie die neuen Dateien in Ihr
Repository auf (
hg add). - Übergeben Sie einen fertigen Arbeitsschritt an das Repository (
hg commit). - Sind alle Teilaufgaben gelöst bzw. der Abgabetermin erreicht, packen Sie das Repository in ein Archiv mit dem Namen jjjj_Nbhif_aufgabeN_name_vorname.tgz
- Kopieren Sie dieses Archiv in das Abgabeverzeichnis
/home/teachers/hp/abgabe/Nbhif/.
Dabei bedeuten:
- jjjj
- Die Jahreszahl des Schuljahresbeginns. Für das Schuljahr 2012/13 also 2012.
- N
- Bei der Klasse, der Jahrgang (also 2bhif, 3bhif) und bei der Aufgabe die Aufgabennummer (aufgabe1, aufgabe2...).
- name_vorname
- Ihr Name und Vorname in Kleinbuchstaben sowie ohne Umlaute, scharfes s (ß) und Akzente.
Beispiel:
2012_3bhif_aufgabe2_mayer_juergen.tgzDieses Archiv sollte in das Verzeichnis
/home/teachers/hp/abgabe/3bhif/kopiert werden.
Bei jeder Aufgabe steht nun zur Erinnerung ganz oben der Abgabename. Zum Beispiel:
Abgabename: 2012_3bhif_aufgabe2_name_vorname.tgz
Erstellen Sie auch für die bereits per Mail abgegebenen Aufgaben Repositories, packen Sie diese in ein entsprechendes Archiv und geben Sie es wie oben beschrieben ab!
Labels: allgemeines, Aufgabe, POS1-2, POS1-3
Dienstag, 25. September 2012
Aufgabe Regex (POS1: 3BHIF)
ClassFinder
Erstellen Sie ein Projekt class_finder und erzeugen Sie ein Mercurial Repository dafür.Schreiben Sie eine Java-Klasse
MyClassFinder, welche folgendes Interface implementiert (Header wurde weggelassen):
package classfinder;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.LinkedList;
public interface ClassFinder {
/**
* finds Classes using the Pattern.
*
* The pattern could be a simple String, i.e. "MyList" which matches all
* classnames (java-filenames) containing "MyList" (i.e. "ThatMyList",
* "Mylist", "MylistMaker",...) ignoring case, or it is a pattern like "SCM"
* (all capital letters) which means names like "SimulateComputerModel",
* "SCM", "StrCatMaker" and so on.
*
* @param pattern
* @return List of filenames
* @throws FileNotFoundException
* @throws IOException
*/
public LinkedList<String> findClassFiles(String pattern)
throws FileNotFoundException, IOException;
/**
* finds files containing methods using the Pattern.
*
* The pattern could be a simple String, i.e. "doThis" which matches all
* classnames (filenames) containing method declarations "doThis" ignoring
* case, or it is a pattern like "scm" (ignore case) which means methods
* like "searchClassMembers", "scM", "strCatMerger" and so on. So pattern
* should be interpreted in both ways.
*
* @param pattern
* @return List of filenames
* @throws FileNotFoundException
* @throws IOException
*/
public LinkedList<String> findMethodFiles(String pattern)
throws FileNotFoundException, IOException;
}
Ihre Klasse darf im Prinzip einen beliebigen Namen haben, nur nicht
classfinder.ClassFinder, denn das ist der Name des Interfaces, welches implementiert werden muss (in einem anderen Paket, darf die Klasse natürlich gleich heißen).
Die Idee der Methode
findClassFiles(String pattern) ist es, das Aktuelle Verzeichnis und die Unterverzeichnisse nach Klassen (Java-Dateien) zu durchsuchen, die dem gegebenen Muster entsprechen.
Beispiele für Muster und das Ergebnis:
- MyList
- liefert folgende Dateien:
MyList.java myList.java ThatMyList.java MylistMaker.java - CF
- liefert folgende Dateien:
CF.java ClassFinder.java CharacterFixer.java CharFinderFactory.java
findMethodFiles(String pattern) liefert die Klassennamen (ohne .java), welche die gesuchten Methodendeklarationen enthalten.
Eine Methode passt, wenn der Name einfach den String pattern (Groß-/Kleinschreibung ignorierend) enthält oder einfach nur die Buchstaben in der gegebenen Reihenfolge enthält. In diesem Fall muss der Methodenname mit dem ersten Buchstaben beginnen.
Beispiele für Muster und die passenden Methoden:
- doThis
- passt zu folgenden Methodennamen:
doThis dothis tryToDoThis doTaskHelpings - ScM
- passt zu folgenden Methodennamen:
scm doScm searchClassMembers setCornerMarker
classfinder.MyClassFinder).
Testen
Schreiben Sie eine (JUnit-) TestklasseClassFinderTest, welche die Methoden ausreichend testet.
Main
Schreiben Sie eine HauptklasseFinder, welche ein passendes Konsoleninterface zu ClassFinder bietet:
hh@knuth:~$ java Finder -c MyList MyList.java myList.java ThatMyList.java maker/MylistMaker.java hh@knuth:~$ java Finder -m ScM MyClass helpers.Helper gui.Main gui.Frame hh@knuth:~$ java Finder -h Finder, a java-tool for finding classes and methods. (c) 2012, Harald R. Haberstroh hh@knuth:~$
Hinweise
- Verwenden Sie reguläre Ausdrücke zum Suchen (Java ist eine Insel: Reguläre Ausdrücke).
- Testen Sie mit geeigneten Unit-Tests (JUnit 4 Tutorial).
Labels: Aufgabe, Informatik, Java, POS1-3
Abonnieren Posts [Atom]






