Programmieren mit Harald R. Haberstroh (original) (raw)
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
# Eingestellt von Harald R. Haberstroh @ 11:54 0 Kommentare
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
# Eingestellt von Harald R. Haberstroh @ 11:02 0 Kommentare
Mittwoch, 19. November 2014
Java Grafik/GUI (POS1: 3BHIF)
Einfache Grafik
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
# Eingestellt von Harald R. Haberstroh @ 11:22 0 Kommentare
Mittwoch, 8. Oktober 2014
Musterlösung zu Unit-Tests
Musterlösung zur Aufgabe zu Unit-Tests
Folgende Grenzfälle werden getestet:
- 0 Elemente aufnehmen, Anzahl muss gleich bleiben
- 0 Elemente löschen, Anzahl muss gleich bleiben
Sonderfälle:
- negative Anzahl Elemente aufnehmen, Exception
- negative Anzahl Elemente löschen, Exception
- nicht vorhandenes Element löschen, Exception
"Normalfälle":
- 10 Elemente aufnehmen, Anzahl testen
- 10 Elemente aufnehmen, Ergebnis des Iterators testen
Weitere Annahmen, die aus der Angabe nicht unbedingt hervorgehen:
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
# Eingestellt von Harald R. Haberstroh @ 11:48 0 Kommentare
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
Java wartet (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 DDBG−cp./bin/{D_DBG} -cp ./bin/ DDBG−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
# Eingestellt von Harald R. Haberstroh @ 13:11 0 Kommentare
Donnerstag, 23. Mai 2013
Graph Algorithms (POS1:3BHIF)
Erstellen Sie ein Java-Programm zum Zeichnen von gerichteten Graphen. Das folgende Bild zeigt ein mögliches User-Interface.
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
Labels: algorithmen, Aufgabe, GUI, Java, POS1-3, User Interface
# Eingestellt von Harald R. Haberstroh @ 10:45 0 Kommentare
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
# Eingestellt von Harald R. Haberstroh @ 09:01 0 Kommentare
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
# Eingestellt von Harald R. Haberstroh @ 22:45 0 Kommentare
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
# Eingestellt von Harald R. Haberstroh @ 19:44 0 Kommentare
Mittwoch, 20. Februar 2013
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.
Labels: Aufgabe, GUI, Java, POS1-3, User Interface
# Eingestellt von Harald R. Haberstroh @ 20:24 0 Kommentare
Mittwoch, 16. Januar 2013
Java zeichnen mit Swing (POS1: 3BHIF)
Abgabename: 2012_3bhif_aufgabe6_ Name_Vorname.tgz
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):
Hier ist GraphApplet.class
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.
Das 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
# Eingestellt von Harald R. Haberstroh @ 17:40 0 Kommentare
Mittwoch, 9. Januar 2013
Java Grafik/GUI (POS1: 3BHIF)
Einfache Grafik
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
# Eingestellt von Harald R. Haberstroh @ 21:15 0 Kommentare
Sonntag, 6. Januar 2013
Abgaben im Schuljahr 2012/13
Bei den Abgabenamen jjjj_ N_bhif_aufgabe_N_ name_vorname.tgz wird für das Schuljahr 2012/13 weiterhin jjjj durch 2012 ersetzt, auch wenn wir bereits das Jahr 2013 haben. Siehe auch Abgaben (POS1: 2BHIF, 3BHIF).
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
# Eingestellt von Harald R. Haberstroh @ 15:36 0 Kommentare
Mittwoch, 5. Dezember 2012
Aufgabe Syntaxanalyse (POS1: 3BHIF)
Abgabename: 2012_3bhif_aufgabe5_ Name_Vorname.tgz
- 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.
Beispiele für Gleitkommazahlen in Java:
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
# Eingestellt von Harald R. Haberstroh @ 17:28 0 Kommentare
Donnerstag, 29. November 2012
Einführung Objektorientiertes Programmieren
Sie finden hier ein paar Informationen zum objektorientierten Programmieren und den von uns verwendeten Begriffen:
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
# Eingestellt von Harald R. Haberstroh @ 09:37 0 Kommentare
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 Funktion fact() 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
# Eingestellt von Harald R. Haberstroh @ 14:52 0 Kommentare
Donnerstag, 18. Oktober 2012
Aufgabe Unit-Tests (POS1: 3BHIF)
Abgabename: 2012_3bhif_aufgabe3_ Name_Vorname.tgz
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 iterator();
}
Denken Sie bei der Erstellung der Tests an Grenzfälle, Sonderfälle und erst dann an den "Normalfall".
# Eingestellt von Harald R. Haberstroh @ 10:00 0 Kommentare
Sonntag, 30. September 2012
Abgaben (POS1: 2BHIF, 3BHIF)
Bis es ein zentrales Repository für Abgaben gibt, gilt folgende Form der Abgabe:
- 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_ N_bhif_aufgabe_N_ name_vorname.tgz**
- Kopieren Sie dieses Archiv in das Abgabeverzeichnis
/home/teachers/hp/abgabe/_N_bhif/.
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.tgz
Dieses Archiv sollte in das Verzeichnis
/home/teachers/hp/abgabe/_3_bhif/
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
# Eingestellt von Harald R. Haberstroh @ 21:21 0 Kommentare
Dienstag, 25. September 2012
Aufgabe Regex (POS1: 3BHIF)
Abgabename: 2012_3bhif_aufgabe2_ Name_Vorname.tgz
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 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 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
Die Methode 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 doTaskHelp_i_ngs
ScM
passt zu folgenden Methodennamen:
scm doScm searchClassMembers setCornerMarker
Natürlich müssen aber die dazugehörigen Klassennamen geliefert werden, ggf. mit package (z.B. classfinder.MyClassFinder).
Testen
Schreiben Sie eine (JUnit-) Testklasse ClassFinderTest, welche die Methoden ausreichend testet.
Main
Schreiben Sie eine Hauptklasse Finder, 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:$
Die Hervorhebung der passenden Zeichen ist optional. Die Hilfe sollte natürlich etwas ausführlicher sein und muss Ihren Namen enthalten.
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
# Eingestellt von Harald R. Haberstroh @ 12:19 0 Kommentare
Abonnieren Kommentare [Atom]







