Mittwoch, 27. Mai 2009

 

Xref (Cross Referenz)

Erstellen Sie ein Java-Program, welches für jedes gelesene Wort bestimmt, in welchen Zeilen es vorkommt.
Gegeben sei folgende Eingabe:
Das ist die erste Zeile und
das ist die zweite Zeile und
hier folgt die dritte Zeile.
Satzzeichen gelten als Trenner.
Folgendes ist kein W0rt und das
4uch nicht.

Das Programm soll dann diese Ausgabe liefern.

als (1): 4
Das (3): 1, 2, 5
die (3): 1, 2, 3
dritte (1): 3
erste (1): 1
Folgendes (1): 5
folgt (1): 3
gelten (1): 4
hier (1): 3
ist (3): 1, 2, 5
kein (1): 5
nicht (1): 6
Satzzeichen (1): 4
Trenner (1): 4
und (3): 1, 2, 5
Zeile (3): 1, 2, 3
zweite (1): 2

Es erzeugt also eine Liste von Worten (in der zuerst vorkommenden Schreibweise), mit der Anzahl der Vorkommnisse in Klammern und dann einer Liste von Zeilennummern.

Verwenden Sie dazu einen binären Baum für die Wörter und Listen für die Zeilennummern. Das bedeutet, dass wir einen Baum haben bei dem jeder Knoten eine Liste von Zeilennummern "enthält".

Labels: , ,


 

Wörter in der Eingabe erkennen

Nehmen wir an, es müssen Wörter aus der Eingabe erkannt werden. Wir definieren (damit ist nicht gesagt, dass diese Definition in der Realität sinnvoll ist), dass ein Wort aus mindestens 3 Buchstaben besteht. Zum Lesen verwenden wir die Klasse Scanner. Mit Hilfe dieser Klasse kann man aus Streams (z.B. System.in) oder auch aus Strings lesen. Hier ein Scrapbook:
class T {
  boolean isWord(String word) {
    return word.matches("^[A-Za-zÄäÖöÜüß]{3,}$");
  }
}
T t = new T();
String string = "zwei Wörter, noch eins ke1nes schluss. a.xxx";
string = string.replaceAll("[\\.,?!;]", " "); // Satzzeichen sind Worttrenner...
java.util.Scanner in = new java.util.Scanner(string);
while (in.hasNext()) {
  String word = in.next();
  if (t.isWord(word)) {
    System.out.printf("%s ist ein Wort\n", word);
  } else {
    System.out.printf("%s ist KEIN Wort\n", word);
  }
}

Die Klasse T exisitiert in diesem Scapbook nur, um die Methode isWord() bereitzustellen. Diese Methode verwendet einen Regulären Ausdurck zu Erkennung eines Wortes: ^[A-Za-zÄäÖöÜüß]{3,}$. Dieser Ausdruck passt zu allen Strings, die aus mindestens drei ({3,}) Buchstaben (Liste der Zeichen in eckigen Klammern) bestehen.

Die Methode relpaceAll() ersetzt alle Satzzeichen durch Leerzeichen (sind Worttrenner). Dadurch bekommt man durch Leerzeichen getrennte Strings.

Wollte man aus der Standardeingabe lesen, dann müsste man das so machen:
java.util.Scanner in = new java.util.Scanner(System.in);
while (in.hasNext()) {
  String word = in.next();
  if (t.isWord(word)) {
    System.out.printf("%s ist ein Wort\n", word);
  } else {
    System.out.printf("%s ist KEIN Wort\n", word);
  }
}

Das heißt, die Logik bleibt gleich, nur die Initialisierung des Scanners ist unterschiedlich.

Labels: ,


Montag, 25. Mai 2009

 

Vererbung

Vererbung ist eines der wichtigsten Prinzipen der objektorientierten Programmierung. Man versteht darunter, dass von bereits bestehenden Klassen neue abegeleitet werden können, die zunächst die gleichen Eigenschaften und Methoden besitzen wie ihre Vorgänger. Zusätzlich werden sie jedoch mit neuen Elementen ausgestattet, die entweder Erweiterungen ihrer "Eltern" darstellen oder aber alte Elemente ersetzen.

Ober- und Unterklassen:

Die Oberklasse ist in der Vererbung sozusagen der Vorfahre der Unterklasse. Es ist also die Unterklasse, die alle Eigenschaften und Methoden der Oberklasse erbt.

Basis- und Superklassen:

Alternative Begriffe für das Wort Oberklasse.

Spezialisierung einer Klasse bzw. Verallgemeinerung einer Klasse:

Eine Unterklasse ist eine spezialisierte Version ihrer Oberklasse. Sie hat eben die gleichen Fähigkeiten wie die Oberklasse, erweitert sie aber um einige Neuerungen, die sie spezieller machen.

Beispiel Öffentliches Verkehrsmittel:

UML: Öffentliche verkehrsmittel

class OeffentlichesVerkehrsmittel {
int linie;
int anzahlSitzplaetze;

void tuerenOeffnen() {
System.out.println("öffne Türen");
}

void losfahren() {
System.out.println("fahre los");
}

void anhalten() {
System.out.println("halte an");
}

void blinken(boolean linksBlinken) {
if (linksBlinken)
System.out.println("blinke links");
else
System.out.println("blinke rechts");
}
}

class Bus extends OeffentlichesVerkehrsmittel {
int oelstand;
int tankinhalt;
int reifenProfiltiefe;

void hupen() {
System.out.println("tuut");
}

void abbiegen() {}
void tanken() {}
}

class Strassenbahn extends OeffentlichesVerkehrsmittel {
int fahrspannung;

void klingeln() {
System.out.println("Bim bim");
}

void stromabnehmerAusfahren() {}
void stromabnehmerEinfahren() {}
}

Die Methoden losfahren() und anhalten() wurden in der Oberklasse OeffentlichesVerkehrsmittel zugeordnet. Allerdings weiß jedes Kind, dass der Mechanismus zum Beschleunigen und Abbremsen beim Bus anders funktioniert als bei der Straßenbahn.

Daher sollte man die Methoden losfahren() und anhalten() abstrakt machen und damit auch die Klasse OeffentlichesVerkehrsmittel:

UML: Öffentliche Verkehrsmittel abstrakt

abstract class OeffentlichesVerkehrsmittel {
int linie;
int anzahlSitzplaetze;

void tuerenOeffnen() {
System.out.println("öffne Türen");
}

abstract void losfahren();

abstract void anhalten();

void blinken(boolean linksBlinken) {
if (linksBlinken)
System.out.println("blinke links");
else
System.out.println("blinke rechts");
}
}

class Bus extends OeffentlichesVerkehrsmittel {
int oelstand;
int tankinhalt;
int reifenProfiltiefe;

void losfahren() {
System.out.println("fahre los");
}

void anhalten() {
System.out.println("halte an");
}

void hupen() {
System.out.println("tuut");
}

void abbiegen() {}
void tanken() {}
}

class Strassenbahn extends OeffentlichesVerkehrsmittel {
int fahrspannung;

void losfahren() {
System.out.println("fahre los");
}

void anhalten() {
System.out.println("halte an");
}

void klingeln() {
System.out.println("Bim bim");
}

void stromabnehmerAusfahren() {}
void stromabnehmerEinfahren() {}
}

Polymorphismus:

Damit ist die Möglichkeit gemeint, den gleichen Namen für (mehr oder weniger) gleichartige Operationen zu verwenden, die auf Objekte unterschiedlicher Klassen angewendet werden. Man spricht auch vom überladen (overloading) einer Operation.

Polymorphismus bezeichnet also die Fähigkeit von Objekten, auf die gleichen Anweisungen unterschiedlich zu reagieren. Das gilt sowohl für Objekte unterschiedlicher Klassen als auch für Objekte gleicher Klassen oder sogar für ein und dasselbe Objekt (Überladung).

OeffentlichesVerkehrsmittel fahrzeug1 = new Bus();
OeffentlichesVerkehrsmittel fahrzeug2 = new Strassenbahn();

fahrzeug1.losfahren();
fahrzeug2.losfahren();
fahrzeug1.anhalten();
fahrzeug2.anhalten();

Das alles als Scrapbook-Seite vererbung.jpage für eclipse.

Labels: ,


Samstag, 16. Mai 2009

 

Einfaches GUI-Beispiel ohne GUI-Builder mit Grafik

gui-demo_1_2.jar (Update) ist ein Jar-Archiv mit einem kleinen Beispiel einer einfachen Java-Applikation mit Menüs und einer Zeichenfläche.
Die Menü-Einträge wurden als anonyme Actions (AbstractAction) implementiert. Im (anonymen) Konstruktor werden die Eigenschaften des Menüeintrags definiert. Als Beispiel soll der "Öffnen"-Eintrag dienen:
    Action openAction = new AbstractAction() {
{
putValue(Action.NAME, "Öffnen");
putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, 1);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control O"));
putValue(Action.SMALL_ICON, smallOpenIcon);
}

public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser();
if (fileChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
// TODO: Action File open
}

}
};

Die Menü-Eigenschaften werden durch die putValue()-Aufrufe gesetzt. Action.NAME setzt den String für das Menü. Welches Zeichen unterstrichen wird, definiert Action.DISPLAYED_MNEMONIC_INDEX_KEY, wo der Index (hier 1 für das 'f') definiert wird. Action.ACCELERATOR_KEY setzt den Shortcut. Mit dem letzten (verwendeten) Wert wird ein Icon gesetzt, welches vorher natürlich geladen werden muss.

Hier das ganze Listing der Klasse WindowWithMenu:

package gui;

import java.awt.HeadlessException;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;

/**
* Applikationsfenster mit Menü.
*
* @author Harald R. Haberstroh (hh)
*
*/
public class WindowWithMenu extends JFrame {
private static final long serialVersionUID = 7874149670026759869L;

private JMenuBar menuBar;

private JMenu fileMenu;

private JMenu editMenu;

private JMenu optionsMenu;

private JMenu helpMenu;

public Action openAction;

public Action newAction;

public Action aboutAction;

public Action exitAction;

public WindowWithMenu() throws HeadlessException {
this("WindowWithMenu");
}

/**
* Ein neues Fenster mit Menü - nur ein Gerüst.
*
* @param title
* Fenstertitel
* @throws HeadlessException
* keine Maus/Bildschirm/Tastatur
*/
public WindowWithMenu(String title) throws HeadlessException {
super(title);
/** ** Öffnen */
final Icon smallOpenIcon = new ImageIcon(WindowWithMenu.class
.getResource("/images/fileopen16x16.png"));
openAction = new AbstractAction() {
{
putValue(Action.NAME, "Öffnen");
putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, 1);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control O"));
putValue(Action.SMALL_ICON, smallOpenIcon);
}

public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser();
if (fileChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
// TODO: Action File open
}

}
};
/** * Neu */
newAction = new AbstractAction() {
{
putValue(Action.NAME, "Neu");
putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, 1);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control N"));
}

public void actionPerformed(ActionEvent e) {
System.out.println("New");
}

};
/** * Exit */
exitAction = new AbstractAction() {
{
putValue(Action.NAME, "Beenden");
putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, 0);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control Q"));
}

public void actionPerformed(ActionEvent e) {
System.exit(0);
}
};

/** * Über */
aboutAction = new AbstractAction() {
{
putValue(Action.NAME, "Über das Programm");
putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, 1);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("F1"));
}

public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "(c) 2009, Harald R. Haberstroh",
"Über das Programm", JOptionPane.INFORMATION_MESSAGE);
}
};

reInititalize();
}

/**
* stelle "Anfangswerte" her und erzeuge Menü neu (die Actions sind public und
* daher änderbar).
*/
public void reInititalize() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 400);
setLocationRelativeTo(null); // zentrieren

/*
* Menü
*/
menuBar = new JMenuBar();

/*
* Dateimenü
*/
fileMenu = new JMenu("Datei");
fileMenu.setMnemonic('D');
menuBar.add(fileMenu);

/** ** Öffnen */
fileMenu.add(openAction);

fileMenu.add(newAction);
fileMenu.addSeparator();

/** ** Beenden */
fileMenu.add(exitAction);

/*
* Bearbeiten Menü
*/
editMenu = new JMenu("Bearbeiten");
editMenu.setMnemonic('B');
menuBar.add(editMenu);

/*
* Optionen Menü
*/
optionsMenu = new JMenu("Optionen");
optionsMenu.setMnemonic('O');
menuBar.add(optionsMenu);

/*
* Hilfe Menü
*/
helpMenu = new JMenu("Hilfe");
helpMenu.setMnemonic('H');
menuBar.add(helpMenu);
setJMenuBar(menuBar);

helpMenu.add(aboutAction);

}

/**
* @param args
*/
public static void main(String[] args) {
WindowWithMenu win = new WindowWithMenu();
win.setVisible(true);
}

}

Labels: , ,


Mittwoch, 13. Mai 2009

 

Speicherlayout - Lösung

Was bedeuten die Ausgaben des Programms bzw. welchen Zweck verfolgt dieses Programm?

Die Speicheradressen und die Inhalte an diesen Adressen werden ausgegeben. Es zeigt, wie der Speicher für Variable reserviert wird.

Von Programmieren mit Harald R. Haberstroh

Man sieht, dass int Variable jeweils 4 Bytes groß sind (bei 32 Bit rechnern).

Speicher wird in der Reihenfolge der Deklaration von oben nach unten angelegt, d.h. beginnend mit höheren Speicheradressen. Bei Arrays ist der niedrigste Index jedoch an der niedrigsten Stelle des entsprechenden Speicherbereichs.

Es ist extrem wichtig diese Speicherorganisation zu verstehen, da viele Programmierfehler und Sicherheitsprobleme (Einbrüche in Web-Server, Verbreitung von Würmern und Viren) auf diesem Speicherlayout basieren.

Was bedeuten die Argumente von main ?

Die Argumente entsprechen dem from sys import argv von Python. Da in C Arrays keine Zusatzinformationen über Länge (Anzahl) enthalten, wird die Anzahl der Elemente im Parameter argc übergeben. argc enthält die Anzahl der Programmparameter (incl. Programmname), argv ist ein Array mit den Argumenten.

Labels: , ,


 

Zeigerarray - Speicherlayout

Skizzieren Sie das Speicherlayout für das folgende Programm.


 /*
* File: zeigerarray.c
*
* Zweck: Beispiel fuer Zeigerarray
*
* Autor: Harald Haberstroh
*
* Algorithmus:
* Bei diesem Beispiel wird ein Array mit konstanten Strings
* angelegt (also ein Zeigerarray), dann wird ein
* 2-dimensionales Array angelegt, das dieselben Strings
* speichern kann. Als letztes ein 2-dimensionales Array,
* welches ebenfalls mit konstanten Strings initialisiert wird.
*
* Die Speichergroessen werden ermittelt.
*
* Die Zeiger des Zeigerarrays werden veraendert.
*
* Der Inhalt wird veraendert. Beim Aendern eines konstanten
* Strings gibt es einen Segmentation Fault.
*
* Es wird noch ein Makro __LINE__ verwendet, Der Preprozessor
* (Precompiler) ersetzt dieses Makro durch die aktuelle
* Zeilennummer.
*
* History: 2002-11-03
* 2007-02-14, Umlaute...
*/

/*--------------------------- includes ------------------------------*/
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>

/*--------------------------- defines -------------------------------*/

/*--------------------------- typedefs ------------------------------*/

/*--------------------------- globals -------------------------------*/

/*-------------------------- prototypes -----------------------------*/

/*----------------------------- main --------------------------------*/
int main( int argc, char *argv[] )
{
int i;
int size = 0;
char *texte[] = {
"Eine Zeile Text",
"Eine 2. Zeile Text",
"Diese Art der Initialisierung kann auch fuer Fehlermeldungen "
"verwendet werden",
"Die letzte Zeile"
};
char aTexte[sizeof(texte)/sizeof(texte[0])][80] = { { 0 } };
/* 76 Zeichen braucht der laengste
* String */
char aTexteInit[sizeof(texte)/sizeof(texte[0])][80] = {
"Eine Zeile Text",
"Eine 2. Zeile Text",
"Diese Art der Initialisierung kann auch fuer Fehlermeldungen "
"verwendet werden",
"Die letzte Zeile"
};

for (i = 0; i < sizeof(aTexte) / sizeof(aTexte[0]); i++)
strcpy(aTexte[i], texte[i]); /* Die Strings auch in's 2-dim Array
* kopieren */

printf("sizeof(texte) = %d\n", sizeof(texte));
/* 4 Zeiger, der Speicher fuer die
* konstanten Strings ist hier
* nicht mitgerechnet -> 16 Bytes */
for (i = 0, size = 0; i < sizeof(texte) / sizeof(texte[0]); i++)
size += strlen(texte[i]) + 1; /* tatsaechlicher Mindestspeicher-
* verbrauch ist die Laenge des
* Strings plus 1 fuer '\0' */
printf("... tats. Speicherbedarf sizeof(texte) + "
"Laengen der Strings (%d) = %d\n", size, sizeof(texte)+size);

for (i = 0; i < sizeof(texte) / sizeof(texte[0]); i++)
printf("sizeof(texte[%d]) = %d, strlen(texte[%d]) = %d\n"
, i, sizeof(texte[i]), i, strlen(texte[i]));
/* Speicherbedarf und Laenge des
* Strings ausgeben */

printf("sizeof(aTexte) = %d\n", sizeof(aTexte));
/* Hier wird wirklich der gesamte
* Speicherbereich ausgegeben,
* das sind 4 * 80 Bytes */

for (i = 0; i < sizeof(aTexte)/sizeof(aTexte[0]); i++)
printf("sizeof(aTexte[%d]) = %d, strlen(aTexte[%d]) = %d\n"
, i, sizeof(aTexte[i]), i, strlen(aTexte[i]));
/* Speicherbedarf und Laenge des
* Strings ausgeben */

printf("sizeof(aTexteInit) = %d\n", sizeof(aTexteInit));
/* Hier wird wirklich der gesamte
* Speicherbereich ausgegeben,
* das sind 4 * 80 Bytes */

for (i = 0; i < sizeof(aTexteInit)/sizeof(aTexteInit[0]); i++)
printf("sizeof(aTexteInit[%d]) = %d, strlen(aTexteInit[%d]) = %d\n"
, i, sizeof(aTexteInit[i]), i, strlen(aTexteInit[i]));
/* Speicherbedarf und Laenge des
* Strings ausgeben */

strcpy(aTexte[2], "Eine 3. Zeile Text");
/* In das Array kann natuerlich
* etwas kopiert werden (solange
* der String nicht zu lange
* ist...) */
printf("aTexte[2] wurde geaendert in \"%s\"\n", aTexte[2]);

printf("Zeile %d: Aenderung eines Strings (aTexte[0][0] klein)\n"
, __LINE__ + 1);
aTexte[0][0] = tolower(aTexte[0][0]);
/* Anfangsbuchstabe klein, das
* darf sein, da dies ja ein
* Array ist */

for (i = 0; i < sizeof(texte) / sizeof(texte[0]); i++)
printf("texte[%d] = \"%s\"\n", i, texte[i]);

texte[2] = aTexte[2]; /* Zeiger "verbiegen", texte[2]
* zeigt nun auf das Array
* aTexte[2] */
printf("texte[2] -> aTexte[2] = \"%s\"\n", texte[2]);

printf("Zeile %d: Aenderung nun nicht mehr konstanten Strings (texte[2][0]"
" klein)\n"
, __LINE__ + 1);
texte[2][0] = tolower(texte[2][0]); /* Das sollte gehen... */
printf("texte[2] = \"%s\"\n", texte[2]);

printf("Zeile %d: Aenderung eines konstanten Strings (texte[0][0] klein)\n"
, __LINE__ + 1);
texte[0][0] = tolower(texte[0][0]); /* Anfangsbuchstabe klein in den
* konstanten Strings -> SegFault,
* da Konstante Strings nicht am
* Stack angelegt werden */
return 0;
}

Sourcecode von zeigerarray.c (Versionsnummer entfernen).

Labels: , ,


 

Speicherlayout

Geben Sie folgendes Programm ein:
#include <stdio.h>
int main( int argc, char *argv[] )
{
int a = 23;
int b = 42;
int feld[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
int i;

printf("a = %2d &a = %x (%u)\n", a,
(unsigned int)&a, (unsigned int)&a);
printf("b = %2d &b = %x (%u)\n", b,
(unsigned int)&b, (unsigned int)&b);

for (i = 0; i < sizeof(feld) / sizeof(int); i++) {
printf("feld[%d] = %2d &feld[%d] = %x (%u)\n", i, feld[i], i,
(unsigned int)&feld[i], (unsigned int)&feld[i]);
}

printf("feld = %x (%u)\n", (unsigned int)feld, (unsigned int)feld);

return 0;
}
Diskutieren Sie diese Fragestellungen in der Gruppe (2 bis 3 Personen)!

Labels: , ,


Donnerstag, 7. Mai 2009

 

Binäre Bäume - Suchen von Namen und/oder Telefonnummern

Immer wieder will man nicht nur die Telefonnummer zu einem Namen suchen sondern auch umgekehrt zu einer Telefonnummer den Namen. Folgendes Programm hilft:

Erstellen Sie ein Programm Telefonsuche.java, welches aus einer (oder mehreren) Datei(en) Telefondaten einliest (Name, Vorname, Telefonnummer - entsprechende Klasse) und in zwei Bäume einträgt. Ein Baum sortiert nach Name, der zweite sortiert nach Telefonnummer. Dabei gibt es grundsätzlich zwei Möglichkeiten:

- die Klasse hat jeweils zwei Paare von Nachfolgern, für jede Sortierung
- die Klasse hat nur ein Paar von Nachfolgern, eine Kopie wird im zweiten Baum eingetragen

Das Programm soll dann einfach einen String einlesen und zunächst im Namenbaum suchen. Kommt der String nicht vor, so ist dann im Nummernbaum zu suchen.

Wie kann man nur nach Teilen des Namens bzw. der Nummer suchen?

Nennen Sie das Projekt klasse-telefonsuche-name, also z.B. 2ad-telefonsuche-haberstroh.

Labels: , ,


Mittwoch, 6. Mai 2009

 

Einführungsbeispiel C

Erstellen Sie ein Programm in C, welches nach Ihrem Namen, der Klasse und der Katalognummer die Größe in Bytes sowie den kleinsten und größten Wert für jeden der folgenden Datentypen ausgibt: char, int, long, short, float und double. Verwenden Sie den sizeof-Operator.

Beispielausgabe (die Minimal und Maximalwerte fehlen bei fast allen Datentypen):
Mario Maierhofer, 3DD, 23
char 1 0..255
int 4 ...
long 4 ...
short 2
float 4
double 8

Da die Größen der Datentypen vom System/Compiler abhängen, werden sich auf jedem System andere Werte ergeben.

Nennen Sie das Programm klasse-sizes-name.c (z.B. 3ad-sizes-maierhofer.c und schicken Sie es per Mail.

Labels: , ,


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

Abonnieren Posts [Atom]