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: , , ,


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: , , ,


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: , , ,


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: , , , ,


Mittwoch, 8. Oktober 2014

 

Musterlösung zu Unit-Tests

Musterlösung zur Aufgabe zu Unit-Tests

Folgende Grenzfälle werden getestet:
Sonderfälle:
"Normalfälle":
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: , , ,


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 ${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: , , , ,


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.
  1. 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.
  2. 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.
  3. Die Gewichtung einer Kante soll eingegeben werden können.
  4. Die Gewichtung einer Kante soll geändert werden können.
  5. 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!
  6. Knoten sollen (de-)selektiert/markiert werden können (Mausklick). Markierung/Selektion soll umgeschaltet werden (toggle).
  7. Kanten sollen (de-)selektiert/markiert werden können (Mausklick). Markierung/Selektion soll umgeschaltet werden (toggle).
  8. Markierte Elemente sollen gelöscht werden können (Menüpunkt, Shortcut Entf).
  9. Ein Graph soll in einer Datei gespeichert werden können.
  10. Ein Graph soll aus einer Datei geladen werden können.
  11. Die Zeichenfläche soll gelöscht werden können.
  12. Im Hilfemenü soll eine Infobox mit Ihrem Namen, der Klasse und dem Erstellungsjahr aufgerufen werden können.
  13. Optional: Eine Hilfe zur Bedienung (Dialogfenster mit HTML-Text).
  14. Optional: Die Sprache soll geändert werden können (Englisch, Deutsch, weitere mit Sprachdateien). Standard ist die Systemeinstellung.
  15. Scrollbars sollen eingeblendet werden, wenn das Fenster kleiner als die benötigte Zeichenfläche ist.
  16. Ein Graph soll gedruckt werden können. Gegebenenfalls auf eine Seite skalieren.
  17. 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.
  18. 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.
  19. Optional: Die Simulation(en) sollen abgebrochen werden können.
  20. Optional: Es soll eine undo/redo-Funktionalität implementiert werden (Command Pattern).
  21. 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.jar möglich sein (meist kann man die graphische Oberfläche so einstellen, dass ein Klick auf die Datei genügt).

Links

Literatur

Labels: , , , , ,


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: , , , ,


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: , , ,


 

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: , , ,


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: , , , ,


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.

  1. Linke Maustaste zum Erzeugen und verschieben von Knoten.
  2. Mittlere Maustaste zum Löschen von Knoten.
  3. 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: 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:

Labels: , , ,


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: , , ,


Sonntag, 6. Januar 2013

 

Abgaben im Schuljahr 2012/13

Bei den Abgabenamen jjjj_Nbhif_aufgabeN_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: , , ,


Mittwoch, 5. Dezember 2012

 

Aufgabe Syntaxanalyse (POS1: 3BHIF)

Abgabename: 2012_3bhif_aufgabe5_Name_Vorname.tgz

  1. Untersuchen Sie die Syntax einer Gleitkomma-Konstanten in Java.
  2. Konstruieren Sie dazu einen endlichen Automaten, der diese Konstanten akzeptiert.
  3. Entwicklen Sie daraus den Minimalautomaten.
  4. Ausgehend von diesem Minimalautomaten schreiben Sie ein Programm (Automat mit switch und tabellengesteuerter Automat), das eine Zeichenkette akzeptiert, falls diese eine ganzzahlige Konstante der untersuchten Programmiersprache ist.
  5. 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: , , ,


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: , , ,


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: , , ,


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<Item> iterator();
}
Denken Sie bei der Erstellung der Tests an Grenzfälle, Sonderfälle und erst dann an den "Normalfall".

Labels: , ,


Sonntag, 30. September 2012

 

Abgaben (POS1: 2BHIF, 3BHIF)

Bis es ein zentrales Repository für Abgaben gibt, gilt folgende Form der Abgabe:
  1. Erzeugen Sie ein Mercurial-Repository (hg init).
  2. Arbeiten Sie an der Lösung der Aufgabe und nehmen Sie die neuen Dateien in Ihr Repository auf (hg add).
  3. Übergeben Sie einen fertigen Arbeitsschritt an das Repository (hg commit).
  4. 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
  5. 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.tgz
Dieses 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: , , ,


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<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
    
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
doTaskHelpings
    
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

Labels: , , ,


This page is powered by Blogger. Isn't yours?

Abonnieren Posts [Atom]