Donnerstag, 31. März 2011

 

Quadcopter spielen Tennis


Video: Quadrocopter spielen Tennis (0:59)
Coole Sache, diese Quad(ro)copter!

Labels:


Mittwoch, 23. März 2011

 

Literaturverwaltung - RandomAccessFile (POS1: 2A, 2C)

Jemand will Zeitschriftenartikel, Bücher und Internet-Links für seine Recherchen verwalten. Schreiben Sie dazu eine Java-Klasse (plus eventuelle Hilfsklassen), welche es ermöglicht diese Daten in einer RandomAccess-Datei zu speichern. Folgende Daten sollen in der Klasse Eintrag erfasst werden:
  1. autor - String(100)
  2. titel - String(100)
  3. erscheinungsjahr - int
  4. verlag - String(30)
  5. zeitschrift - String(30)
  6. seiten - String(10)
  7. link - String(60)
String(100) bedeutet, dass dafür maximal 100 (genau 100) Zeichen gespeichert werden soll. Sie können das so implementieren, dass kürzere Strings hinten mit Leerzeichen gefüllt werden. Längere Strings müssen abgeschnitten werden. Für eine eventuelle Anzeige/Ausgabe müssen aber die Füllzeichen entfernt werden. Die Klasse Eintrag ist mit entsprechenden Getter- und Setter-Methoden auszustatten (die bei den Strings das Abschneiden und Auffüllen bewerkstelligen).

Die Klasse Literatur für die Verwaltung der Datensätze soll folgende Methoden bereitstellen:
Der Konstruktor der Klasse Literatur soll eine geöffnete RandomAccess-Datei als Parameter haben:
public Literatur(RandomAccessFile file)

Implementieren Sie das Löschen so, dass der jeweilige Eintrag als gelöscht markiert wird (zusätzliches Feld) und diese Änderung in der Datei gespeichert wird. Der Datensatz ist also physisch noch in der Datei. Bei der Methode lese() muss also im Falle eines gelöschten Eintrags null geliefert werden.
Umgekehrt muss bei neuerEintrag() zunächst ein gelöschter Eintrag gesucht werden. Wird ein solcher gefunden, so wird er durch den neuen ersetzt (Lösch-Markierung muss wieder zurückgesetzt werden). Wird kein gelöschter Datensatz gefunden, so ist der neue an die Datei anzuhängen.

Schreiben Sie ein kleines Testprogramm zum Testen Ihrer Klassen.

Anmerkung: Die Daten sind für eine echte Anwendung nicht ausreichend und müssten unterteilt werden, da es z.B. mehrere Autoren für einen Artikel gibt.

Hinweishier finden Sie eine Testklasse zum Testen der Klasse Literatur. Die Klasse Literatur muss wie diese Testklasse im Package literatur sein. Weiters muss die Library für JUnit 3 angegeben werden. Diese Testklasse trifft gewissen Annahmen zur Implementierung von Literatur und Eintrag. Bitte passen Sie entweder die Testklasse an Ihre Implementierung an oder Sie ändern Ihre Literatur bzw. Eintrag-Klassen entsprechend (z.B. nimmt der Test an, dass es eine read() und eine write()-Methode gibt, die einen Datensatz ("in sich hinein") liest bzw. einen Datensatz ("sich selbst") schreibt (jeweils an die Position, die in Literatur bestimmt wurde).

Ihre Klasse Eintrag könnte etwa so beginnen:
public class Eintrag {

  // Feldlängen
  private final static int autorLen = 100;
  private final static int titelLen = 100;
  private final static int verlagLen = 30;
  private final static int zeitschriftLen = 30;
  private final static int seitenLen = 10;
  private final static int linkLen = 60;

  // Felder
  private String autor;
  private String titel;
  private int erscheinungsjahr;
  private String verlag;
  private String zeitschrift;
  private String seiten;
  private String link;

  // gelöscht?
  private boolean istGeloescht = false;
Da eine fixe Datensatzlänge gefordert ist, müssen Sie die Länge ermitteln, indem Sie die Längen der einzelnen Felder zusammenzählen. Wir schätzen für Boolean die Länge von int (bestimmt mit Hilfe der Wrapperklasse Integer und der Konstante SIZE, welche die Länge in Bits angibt).
public static int eintragLen() {
    int len = Integer.SIZE
        / 8
        + (autorLen + titelLen + verlagLen + zeitschriftLen + seitenLen + linkLen)
        * 3 + 6 * Long.SIZE / 8 + Integer.SIZE / 8;
    return len;
  }
Der Faktor 3 entsteht durch die Verwendung der UTF-8-Kodierung. UTF-8 ist eine Kodierung des Unicodes und speichert alle Zeichen des ASCII-Codes auch in der selben Kodierung wie ASCII, daher werden dann für andere Zeichen 2, 3 bzw. 4 Bytes verwendet (Unicode verwendete in den ersteren Versionen 16 Bit, in Java werden daher Zeichen mit 16 Bit kodiert). Um ganz sicher zu sein, müsste man den Faktor 4 verwenden. Long.SIZE / 8 ist die Längeninformation, die in jedem String-Objekt gespeichert ist.
Strings werden mit file.writeUTF(string); geschrieben bzw. mit string = file.readUTF(); gelesen.
public void write(RandomAccessFile out) throws IOException {
    out.writeBoolean(istGeloescht);
    out.writeUTF(autor);
    out.writeUTF(link);
    out.writeUTF(seiten);
    out.writeUTF(titel);
    out.writeUTF(verlag);
    out.writeUTF(zeitschrift);
    out.writeInt(erscheinungsjahr);
  }

  public void read(RandomAccessFile in) throws IOException, EOFException {
    istGeloescht = in.readBoolean();
    autor = in.readUTF();
    link = in.readUTF();
    seiten = in.readUTF();
    titel = in.readUTF();
    verlag = in.readUTF();
    zeitschrift = in.readUTF();
    erscheinungsjahr = in.readInt();
  }
Die String-Felder müssen natürlich auf eine fixe Länge gebracht werden, d.h. zu lange Strings müssen abgeschnitten, zu kurze evtl. verlängert werden. Dies erledigt man am Besten in den Setter- bzw. Getter-Methoden.
Das Lesen eines Datensatzes in der Klasse Literatur könnte etwa so funktionieren:
public Eintrag lese(int nummer) throws IOException {
    long pos = nummer * Eintrag.eintragLen();
    if (pos < file.length()) {
      file.seek(pos);
      Eintrag eintrag = new Eintrag();
      eintrag.read(file);
      if (!eintrag.istGeloescht()) {
        return eintrag;
      } else {
        return null; // gelöschter Eintrag gilt als nicht vorhanden
      }
    } else {
      return null; // über die Dateilänge hinaus
    }
  }
Vervollständigen Sie mit diesen Informationen Ihre Klassen.
Nennen Sie das Projekt klasse-name-literatur (klasse ... 2a, 2b, 2c, name ... Ihr Familienname), z.B. 2a-haberstroh-literatur.

Labels: , ,


Freitag, 18. März 2011

 

Experimente mit Queues (POS1-2: 2A, 2C, PR: 5A, 5B)

Implementieren Sie im Package ringbuffer eine Klasse Queue für Integer-Werte. Implementieren Sie die Queue in Form eines Ringpuffers. Es soll die Exception QueueFullException geworfen werden, wenn der Ringpuffer voll ist und die Exception QueueEmptyException, wenn der Ringpuffer leer ist.
Als Testklasse verwenden Sie bitte die folgende Klasse QueueTest, welche zwei Threads erzeugt. Einen Sender, der zufällige "Nachrichten" (Integerwerte) in die Warteschlange steckt und einen Empfänger, der Werte aus der Warteschlange entnimmt. Beide geben jeweils die gesendete bzw. gelesene Nachricht aus. Beide Threads haben unterschiedliche zufällige Wartezeiten.
Experimentieren Sie mit unterschiedlichen Wartezeiten, sodass die Queue (Ringpuffer) voll bzw. leer werden kann. Was passiert?
Wenn die Queue nicht synchronisiert wird, was kann passieren? Können Sie das Verhalten in Experimenten nachweisen?
Bauen Sie entsprechendes synchronized in Ihre Queue ein (5. Klasse). Was muss synchronisiert werden?
Source Code
/**
 * java-queue: ringbuffer.QueueTest.java
 * 
 * 17.03.2011, Harald R. Haberstroh
 */

package ringbuffer;

import java.util.Random;

/**
 * Testen der Queue mit 2 Threads.
 * 
 * @author Harald R. Haberstroh (hp)
 * 
 */
public class QueueTest extends Thread {
  private final static int MAXRUNS = 20;
  private Queue messages;
  private Random rnd = new Random();

  public QueueTest(String name, Queue messages) {
    super(name);
    this.messages = messages;
  }

  public void run() {
    if (getName().equalsIgnoreCase("Sender")) {
      for (int i = 0; i < MAXRUNS; i++) {
        int msg = rnd.nextInt(100);
        long sleepTime = (long) rnd.nextInt(500) + 500; // 500-1000ms
        try {
          System.out.println(getName() + " sending " + msg);
          messages.put(msg);
        } catch (QueueFullException e) {
          System.err.println(getName() + ":Oops - Queue full");
        }
        try {
          sleep(sleepTime);
        } catch (InterruptedException e) {
          System.err.println(getName() + ":Oops - interrupted");
        }
      }
    } else { // Receiver
      setName("                    " + getName()); // Einrückung
      for (int i = 0; i < MAXRUNS || !messages.isEmpty(); i++) {
        long sleepTime = (long) rnd.nextInt(900) + 600; // 900-1500ms
        try {
          if (!messages.isEmpty()) {
            System.out.println(getName() + " read " + messages.get());
          }
        } catch (QueueEmptyException e) {
          System.err.println(getName() + ": Oops - Queue empty");
        }
        try {
          sleep(sleepTime);
        } catch (InterruptedException e) {
          System.err.println(getName() + ":Oops - interrupted");
        }
      }
    }
    System.out.println(getName() + " finished");
  }

  public String toString() {
    return getName();
  }

  /**
   * @param args
   */
  public static void main(String[] args) {
    Queue messages = new Queue();
    QueueTest sender = new QueueTest("Sender", messages);
    QueueTest receiver = new QueueTest("Receiver", messages);
    sender.start();
    receiver.start();
    try {
      sender.join();
      receiver.join();
    } catch (InterruptedException e) {
      System.err.println(e.getLocalizedMessage());
    }
    System.out.println(".... all finished ....");
  }

}
Beispielausgabe bei Standardeinstellung
Sender sending 73
Sender sending 46
                    Receiver read 73
Sender sending 63
                    Receiver read 46
Sender sending 11
                    Receiver read 63
Sender sending 89
Sender sending 21
                    Receiver read 11
Sender sending 99
                    Receiver read 89
Sender sending 79
Sender sending 41
                    Receiver read 21
Sender sending 17
                    Receiver read 99
Sender sending 92
                    Receiver read 79
Sender sending 89
                    Receiver read 41
Sender sending 52
                    Receiver read 17
Sender sending 60
                    Receiver read 92
Sender sending 25
                    Receiver read 89
Sender sending 82
Sender sending 60
                    Receiver read 52
Sender sending 13
Sender sending 34
                    Receiver read 60
Sender sending 10
                    Receiver read 25
Sender finished
                    Receiver read 82
                    Receiver read 60
                    Receiver read 13
                    Receiver read 34
                    Receiver read 10
                    Receiver finished
.... all finished ....

Labels: , , ,


Mittwoch, 16. März 2011

 

RMI-Client/Server mit Java (PR: 5A, 5B)

Schreiben Sie eine Klasse Server, welche über RMI folgende Objekte bereitstellt:
localhost kann natürlich (theoretisch) jede beliebige IP-Adresse bzw. jeder beliebige Hostname sein, wenn unter dieser Adresse ein RMI-Server erreichbar ist.

Die zu öffnende Datei soll als Parameter der Kommandozeile angegeben werden. Aufruf:
java Server dateiname
Also z.B.:
java Server machines.db

Schreiben Sie weiters eine Klasse Client, welche über RMI die vom Server bereitgestellten Objekte verwendet, um einfache "Datenbank"-Zugriffe zu machen. Beim Aufruf soll über einen optionalen Kommandozeilenparameter die Adresse des Servers angegeben werden. Wird kein Parameter angegeben, so ist localhost zu verwenden. Aufruf:
java Client [hostaddress]
Also z.B.:
java Client
oder
java Client 127.0.0.1

Client soll einfach den Inhalt der Datenbank ausgeben (nur nicht gelöschte Datensätze). Die Spaltenüberschriften sind entsprechend der Informationen aus der Datei (über Header) ausgeben. Zeilen müssen gemäß der Feldlängen formatiert sein.

In weiterer Folge soll der Client eine Minute lang zufällige Datensätze auswählen und ausgeben (falls diese existieren). Zwischen zwei Zugriffen soll eine zufällige Zeit zwischen 1 und 5 Sekunden liegen (d.h. es werden mindestens 12 und höchstens 60 weitere Zeilen ausgegeben).

Labels: , ,


 

Binärdateien bearbeiten mit Java - Zusatz für Client (PR: 5A, 5B)

Wie unter "Binärdateien bearbeiten mit Java (PR: 5A, 5B)" beschrieben, ist in der Binärdatei auch die Feldbeschreibung enthalten. An Server bzw. in der Klasse Data wird aber nur die Typ bzw. Längeninformation benötigt. Implementieren Sie das Interface Header (entweder eigene Klasse oder Klasse Data), um die nötigen Informationen für den Client, der ja die Daten darstellen soll, bereitzustellen.
package data;
/**
 * RuD-Übung 2011
 *
 * data.Header.java
 * @author (c) 2011, Harald R. Haberstroh
 * 16.3.2011
 */

import java.rmi.Remote;
import java.rmi.RemoteException;

/**
 * @author Harald R. Haberstroh (hh)
 * 
 */
public interface Header extends Remote {
  /**
   * Liefert die Anzahl der Felder.
   *
   * @return Anzahl der Felder
   * @throws RemoteException
   */
  public int getNumFields() throws RemoteException;

  /**
   * Liefert den Feldnamen zur gegebenen Feldnummer
   * 
   * @param fieldNo
   *          Nummer Feldes.
   * @return Feldname
   * @throws RemoteException
   */
  public String getFieldName(int fieldNo) throws RemoteException;

  /**
   * Liefert die Beschreibung des Feldes zur gegebenen Feldnummer
   * 
   * @param fieldNo
   *          Nummer Feldes.
   * @return Feldbeschreibung
   * @throws RemoteException
   */
  public String getFieldDesc(int fieldNo) throws RemoteException;

  /**
   * Liefert den Typ des Feldes zur gegebenen Feldnummer.
   * 'F'...fix, 'V'...Variabel, 'C'...Zeichen
   * 
   * @param fieldNo
   *          Nummer Feldes.
   * @return Typ des Feldes ('F', 'V' oder 'C')
   * @throws RemoteException
   */
  public char getFieldType(int fieldNo) throws RemoteException;

  /**
   * Liefert die (maximale) Länge des Feldes zur gegebenen Feldnummer.
   * 
   * @param fieldNo
   *          Nummer Feldes.
   * @return (maximale) Feldlänge
   * @throws RemoteException
   */
  public int getFieldLen(int fieldNo) throws RemoteException;

}

Labels: , ,


Donnerstag, 10. März 2011

 

Lösungen zu "Übungsbeispiele zu Sequenzen, Schleifen und Funktionen in Python" (POS1: 1B)

Hier mögliche Lösungen zu der Aufgabe "Übungsbeispiele zu Sequenzen, Schleifen und Funktionen in Python (POS1: 1B)". Einige der Aufgaben enthalten Suchaufgaben. Die Funktionen dazu sind teilweise nicht "strukturiert" programmiert, das heißt, dass sie in diesen Fällen nicht nur ein return sondern mehrere Ausgänge haben. Um das zu verdeutlichen, beachten Sie folgendes Beispiel:
def isint(string):
    """prüft, ob der String eine ganze Zahl ist (Vorzeichen optional, 
    nur Ziffern).
    Prüfe nur Ziffernbereich."""
    if len(string) > 0:
        if string[0] == '+' or string[0] == '-':
            for c in string[1:]:
                if c < '0' or c > '9':
                    return False
        else:
            for c in string:
                if c < '0' or c > '9':
                    return False
    else:
        return False
    return True
Hier erfolgt ein return False aus den Schleifen über den String string, sobald ein Fehler gefunden wurde. Das vereinfacht den Algorithmus, widerspricht aber der strukturierten Programmierung, die bei Funkionen nur einen Ausgang erlaubt (bei Schleifen gilt das auch, die Schleife verlässt man nur durch die Schleifenbedingung, nicht durch break oder eben return). Beim Debuggen größerer Systeme ist es von Vorteil, wenn man sich darauf verlassen kann, dass jede Funktion nur einen Ausgang hat, denn man braucht dann nur diesen einen beobachten und nicht mehrere.
Bei obigem Beispiel müsste man while-Schleifen verwenden:
def isint(string):
    """prüft, ob der String eine ganze Zahl ist (Vorzeichen optional, 
    nur Ziffern).
    Prüfe nur Ziffernbereich."""
    ok = True
    if len(string) > 0:
        if string[0] == '+' or string[0] == '-':
            i = 1
            while ok and i < len(string):
                if string[i] < '0' or string[i] > '9':
                    ok = False
                i += 1
        else:
            i = 0
            while ok and i < len(string):
                if string[i] < '0' or string[i] > '9':
                    ok = False
                i += 1
    else:
        ok = False
    return ok
Diese Variante könne man noch "vereinfachen", weil sich die beiden Zweige mit oder ohne Vorzeichen nur im Startwert der Laufvariable i unterscheiden:
def isint(string):
    """prüft, ob der String eine ganze Zahl ist (Vorzeichen optional, 
    nur Ziffern).
    Prüfe nur Ziffernbereich."""
    ok = True
    if len(string) > 0:
        if string[0] == '+' or string[0] == '-':
            i = 1
        else:
            i = 0
        while ok and i < len(string):
            if string[i] < '0' or string[i] > '9':
                ok = False
            i += 1
    else:
        ok = False
    return ok
Die "nicht strukturierte" Variante ist aber bei weitem schneller entwickelt und durch die automatischen Tests ist auch sichergestellt, dass sie funktioniert.

Die Tests verwenden assert(), um die Ergebnisse mit den erwarteten Ergebnissen zu vergleichen. assert() bricht das Programm auf der Stelle ab, wenn die Bedingung nicht erfüllt ist. Dadurch hat man die Möglichkeit, den Fehler einzugrenzen. Passt die Bedingung, so läuft das Programm einfach weiter.

"""
author: Haberstroh, Harald
matnr:  d09666
file:   sequenzenbeispiele.py
desc:   Beispiele einfacher Funktionen mit Sequenzen (vgl. Programmierblog)
        Einige Funktionen bauen auf den anderen auf.
        
        Zu jeder Funktion (ausgenommen Hilfsfunktion) gibt es eine Testfunktion
        die die jeweilige Funktion mit Testwerten prüft.
        
        Die Funktion assert() prüft den Wahrheitswert des Parameters. Ist dieser
        True, so läuft das Programm einfach weiter. Ist er jedoch False, wird
        das Programm mit einer Fehlermeldung und Zeilennummer abgebrochen.
        Man kann dadurch feststellen, welcher Test nicht funktioniert hat.
date:   7.3.2011
class:  Lehrer
catnr:  33
"""
#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def isint(string):
    """prüft, ob der String eine ganze Zahl ist (Vorzeichen optional, 
    nur Ziffern).
    Prüfe nur Ziffernbereich."""
    if len(string) > 0:
        if string[0] == '+' or string[0] == '-':
            for c in string[1:]:
                if c < '0' or c > '9':
                    return False
        else:
            for c in string:
                if c < '0' or c > '9':
                    return False
    else:
        return False
    return True

def test_isint():
    assert(isint("") == False)
    assert(isint("a") == False)
    assert(isint("1a") == False)
    assert(isint("a1") == False)
    assert(isint("1+1") == False)
    assert(isint("1+") == False)
    assert(isint("1") == True)
    assert(isint("+1") == True)
    assert(isint("-1") == True)
    assert(isint("11212") == True)
    assert(isint("-11212") == True)
    assert(isint("+11212") == True)
    print("test_isint() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def isintb(string, basis):
    """prüft, ob der String eine ganze Zahl zur Basis basis ist (Vorzeichen
    optional, nur Ziffern).
    Baue String mit gültigen Zeichen zusammen und prüfe dann, ob jedes
    Zeichen in diesem String vorkommt."""
    if basis > 1:
        gueltig = "" # "Liste" der gültigen Zeichen für geg. Basis
        if basis > 10: # Ziffern und Buchstaben (immer groß und klein)
            for z in range(10):
                gueltig += chr(z + ord('0'))
            for z in range(basis - 10):
                gueltig += chr(z + ord('a')) # Kleinbuchstabe
                gueltig += chr(z + ord('A')) # Großbuchstabe
        else: # nur Ziffern
            for z in range(basis):
                gueltig += chr(z + ord('0'))
        if len(string) > 0:
            if string[0] == '+' or string[0] == '-': # Vorzeichen
                for c in string[1:]:
                    if c not in gueltig:
                        return False
            else:
                for c in string:
                    if c not in gueltig:
                        return False
        else:
            return False
    else:
        return False
    return True # alles OK

def test_isintb():
    assert(isintb("", 2) == False)
    assert(isintb("a", 2) == False)
    assert(isintb("1a", 2) == False)
    assert(isintb("a1", 2) == False)
    assert(isintb("1+1", 2) == False)
    assert(isintb("1+", 2) == False)
    assert(isintb("1", 2) == True)
    assert(isintb("+1", 2) == True)
    assert(isintb("-1", 2) == True)
    assert(isintb("11212", 2) == False)
    assert(isintb("-11212", 2) == False)
    assert(isintb("+11212", 2) == False)
    assert(isintb("11212", 3) == True)
    assert(isintb("-11212", 3) == True)
    assert(isintb("+11212", 3) == True)
    assert(isintb("aBCdeF", 16) == True)
    assert(isintb("-aBCdeF", 16) == True)
    assert(isintb("+aBCdeF", 16) == True)
    print("test_isintb() ok")
    
#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----

def sumdigits(zahl):
    """Berechnet die Ziffernsumme aus der gegebenen Zahl (int)"""
    sum = 0
    while zahl != 0:
        sum += zahl % 10 # Rest ist immer Einerstelle
        zahl = zahl // 10
    return sum

def test_sumdigits():
    assert(sumdigits(0) == 0)
    assert(sumdigits(1) == 1)
    assert(sumdigits(7) == 7)
    assert(sumdigits(10) == 1)
    assert(sumdigits(11) == 2)
    assert(sumdigits(123) == 6)
    assert(sumdigits(1010) == 2)
    print("test_sumdigits() ok")
    
#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def wert_der_ziffer(ziffer):
    """liefert den Wert der Ziffer ziffer (0..9, bzw. A, B, C, ...)"""
    if ord(ziffer) <= ord("9"):
        wert = ord(ziffer) - ord("0")
    else:
        wert = ord(ziffer) - ord("A") + 10
    return wert

def horner(zahl, basis):
    """berechnet Dezimalzahl aus zahl (String) zur Basis basis.
    liefert Zahl. Hornerschema!"""
    dez = 0
    for ziffer in zahl:
        dez *= basis
        dez += wert_der_ziffer(ziffer)
    return dez

def test_horner():
    assert(horner("", 10) == 0)
    assert(horner("1", 10) == 1)
    assert(horner("12", 10) == 12)
    assert(horner("123", 10) == 123)
    assert(horner("1", 2) == 1)
    assert(horner("10", 2) == 2)
    assert(horner("11", 2) == 3)
    assert(horner("4", 16) == 4)
    assert(horner("A", 16) == 10)
    assert(horner("10", 16) == 16)
    print("test_horner() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def isipv4(string):
    """Prüft, ob der gegebene String eine IPv4-Adresse enthält.
    IPv4-Adressen sind 4 Zahlen zwischen 0 und 255, die durch einen
    Punkt getrennt sind.
    Beispiele: 127.0.0.1, 193.170.149.129, 81.200.64.185
    Ungültige: 81.2010.64.185, 123.23.12, 127,0,0,1"""
    zahl = ""
    anzpunkte = 0
    anzziffer = 0
    for z in string:
        if '0' <= z <= '9': 
            zahl += z
            anzziffer += 1
        elif z == '.' and anzziffer > 0: # mindestens 1 Ziffer vor Zahl
            if not (0 <= horner(zahl, 10) <= 255):
                return False
            zahl = "" # neu beginnen
            anzziffer = 0
            anzpunkte += 1
        else:
            return False
    if anzpunkte == 3 and anzziffer > 0: # nach 3. Punkt eine Zahl!
        if not (0 <= horner(zahl, 10) <= 255):
            return False
        return True
    else:
        return False

def test_isipv4():
    assert(isipv4("127.0.0.1"))
    assert(isipv4("193.170.149.129"))
    assert(isipv4("81.200.64.185"))
    assert(not isipv4(""))
    assert(not isipv4("81.2010.64.185"))
    assert(not isipv4("123.23.12"))
    assert(not isipv4("123.23.12."))
    assert(not isipv4("123.23.12.3.3"))
    assert(not isipv4("123.23.12.3."))
    assert(not isipv4("127,0,0,1"))
    print("test_isipv4() ok")
    
#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def anz_zahlen(string):
    """ermittelt die Anzahl der ganzen Zahlen im String"""
    anzahl = 0
    in_zahl = False
    for zeichen in string:
        if zeichen in "0123456789":
            if not in_zahl:
                anzahl += 1
                in_zahl = True
        else:
            in_zahl = False
    return anzahl

def test_anz_zahlen():
    string = "das ist eine Zahl 123 und noch eine 1 und noch eine 3213x23123"
    assert(4 == anz_zahlen(string))
    assert(0 == anz_zahlen("keine"))
    assert(1 == anz_zahlen("eine Zahl 123"))
    assert(2 == anz_zahlen("zwei 23 und 324"))
    assert(3 == anz_zahlen("drei 23 34 22"))
    assert(4 == anz_zahlen("vier 234 234 234 45"))
    assert(5 == anz_zahlen("5 234 234 234 45"))
    print("test_anz_zahlen() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def sum_zahlen(string):
    """ermittelt die Summe der ganzen Zahlen im String. Eine Zahl
    ist eine ununterbrochene Folge von Ziffern.
    Beispiel: sum_zahlen("x 34 3 12k33 44 hallo 23") liefert 149"""
    summe = 0
    zahl = ""
    for zeichen in string:
        if zeichen in "0123456789":
            zahl += zeichen
        else:
            summe += horner(zahl, 10) # umrechnen und dazuzählen
            zahl = "" # neu beginnen
    if len(zahl) > 0: # zuletzt war Zahl, daher dazurechnen
        summe += horner(zahl, 10)
    return summe

def test_sum_zahlen():
    string = "das ist eine Zahl 123 und noch eine 1 und noch eine 3213x23123"
    assert(26460 == sum_zahlen(string))
    assert(0 == sum_zahlen("keine"))
    assert(123 == sum_zahlen("eine Zahl 123"))
    assert(347 == sum_zahlen("zwei 23 und 324"))
    assert(79 == sum_zahlen("drei 23 34 22"))
    assert(747 == sum_zahlen("vier 234 234 234 45"))
    assert(752 == sum_zahlen("5 234 234 234 45"))
    print("test_sum_zahlen() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def iskonto(string):
    """Prüft, ob der String eine gültige Kontonummer enthält. Dabei
    gelten folgende Regeln:
    1. genau 9 Ziffern (es dürfen Leerzeichen dazwischen sein
    2. die letzte Ziffer ist eine Prüfziffer und es gilt
       Die Kontonummer ist genau 9 Stellen lang und ist dann richtig, 
       wenn die folgendermaßen gebildete Summe auf Null endet:
       Ziffern mit ungeradem Index werden addiert(=Teilsumme1);
       Ziffern mit geradem Index werden verdoppelt und das jeweilige 
       Produkt addiert, wenn es einstellig ist, andernfalls wird die
       Quersumme des Produkts addiert(=Teilsumme2);
       Summe= teilsumme1 + Teilsumme2
    Beispiel:
       123456782 1+    3+    5+      7+     2  = 18 Teilsumme1
                   2*2+  4*2+  (1+2)+  (1+6)   = 22 Teilsumme2
                               6*2=12  8*2=16
                                               ------
                                                 40 Summe mod 10= Null
    Beispiele für Kontonummern:
       697199107 richtige Kontonummer
       723016689 richtige Kontonummer
       723016699 falsche Kontonummer
    """
    # entferne alle Leerzeichen (sind ja erlaubt)
    kontonummer = ""
    for c in string:
        if '0' <= c <= '9':
            kontonummer += c
        elif c != ' ':
            return False # nur Ziffern und Leerzeichen erlaubt!
    if len(kontonummer) != 9:
        return False
    
    summe = 0 # alles in einer Summe, aufteilen ist nicht nötig
    for i in range(len(kontonummer)):
        ziffer = ord(kontonummer[i]) - ord('0')
        if (1 + i) % 2 == 1: # ungerade
            summe += ziffer
        else: # gerade
            doppelt = ziffer * 2
            if doppelt >= 10: # Zwei Ziffern
                summe += doppelt % 10 # Einer
                summe += doppelt // 10 # Zehner
            else: # immer noch eine Ziffer
                summe += doppelt
    return summe % 10 == 0 # Summe muss durch 10 teilbar sein

def test_iskonto():
    assert(not iskonto(""))
    assert(not iskonto("a"))
    assert(not iskonto("69719a9107"))
    assert(not iskonto("69719917"))
    assert(not iskonto("723016699"))
    assert(iskonto("723016689"))
    assert(iskonto("697199107"))
    assert(iskonto("123456782"))
    assert(iskonto("12 345 6782"))
    print("test_iskonto() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def reverse_s(string):
    """liefert String in umgekehrter Reihenfolge zurück"""
    s = ""
    for c in string:
        s = c + s
    return s

def test_reverse_s():
    assert("" == reverse_s(""))
    assert("a" == reverse_s("a"))
    assert("ba" == reverse_s("ab"))
    assert("abc" == reverse_s("cba"))
    print("test_reverse() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def toupper(string):
    """liefert String in Großbuchstaben zurück"""
    s = ""
    for c in string:
        if 'a' <= c <= 'z':
            s += chr(ord(c) - ord('a') + ord('A'))
        elif c == 'ä':
            s += 'Ä'
        elif c == 'ö':
            s += 'Ö'
        elif c == 'ü':
            s += 'Ü'
        else:
            s += c
    return s

def test_toupper():
    assert("" == toupper(""))
    assert("A" == toupper("a"))
    assert("AB" == toupper("ab"))
    assert("ÄBC" == toupper("äbc"))
    assert("A" == toupper("a"))
    assert("AB" == toupper("Ab"))
    assert("ÄBC" == toupper("äBc"))
    print("test_toupper() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def tolower(string):
    """liefert String in Kleinbuchstaben zurück"""
    pass
    s = ""
    for c in string:
        if 'A' <= c <= 'Z':
            s += chr(ord(c) - ord('A') + ord('a'))
        elif c == 'Ä':
            s += 'ä'
        elif c == 'Ö':
            s += 'ö'
        elif c == 'Ü':
            s += 'ü'
        else:
            s += c
    return s

def test_tolower():
    assert("" == tolower(""))
    assert("a" == tolower("A"))
    assert("ab" == tolower("AB"))
    assert("äbc" == tolower("ÄBC"))
    assert("a" == tolower("A"))
    assert("ab" == tolower("Ab"))
    assert("äbc" == tolower("äBc"))
    print("test_tolower() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def isupper(string):
    """prüft, ob String nur Großbuchstaben enthält"""
    for c in string:
        if 'a' <= c <= 'z' or c in "äöü": # ein Kleinbuchstabe
            return False
    return True

def test_isupper():
    assert(isupper(""))
    assert(isupper("A"))
    assert(isupper("AEÄÖ"))
    assert(not isupper("a"))
    assert(not isupper("abcö"))
    assert(not isupper("AbC"))
    print("test_isupper() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def islower(string):
    """prüft, ob String nur Kleinbuchstaben enthält"""
    for c in string:
        if 'A' <= c <= 'Z' or c in "ÄÖÜ": # ein Großbuchstab
            return False
    return True

def test_islower():
    assert(islower(""))
    assert(islower("a"))
    assert(islower("aeöä"))
    assert(not islower("A"))
    assert(not islower("ABCÖ"))
    assert(not islower("AbC"))
    print("test_islower() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def ispalindrom(string):
    """Prüft, ob der String ein Palindrom ist (von vorne und hinten
    gelesen gleich)."""
    for i in range(len(string)):
        if string[i] != string[-1 - i]:
            return False
    return True

def test_ispalindrom():
    assert(ispalindrom(""))
    assert(ispalindrom("a"))
    assert(ispalindrom("aa"))
    assert(ispalindrom("aba"))
    assert(ispalindrom("abba"))
    assert(not ispalindrom("ab"))
    assert(not ispalindrom("abb"))
    assert(not ispalindrom("abab"))
    print("test_ispalindrom() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def ispalindrom_l(liste):
    """Prüft, ob die Liste von vorne und hinten gelesen gleich ist."""
    return ispalindrom(liste) # sollte äquivalent sein!

def test_ispalindrom_l():
    assert(ispalindrom_l([]))
    assert(ispalindrom_l([1]))
    assert(ispalindrom_l([1, 1]))
    assert(ispalindrom_l([1, 2, 1]))
    assert(ispalindrom_l([1, 2, 2, 1]))
    assert(not ispalindrom_l([1, 2]))
    assert(not ispalindrom_l([1, 2, 2]))
    assert(not ispalindrom_l([1, 2, 1, 2]))
    print("test_ispalindrom_l() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def reverse_l(liste):
    """liefert die Liste in umgekehrter Reihenfolge zurück"""
    l = []
    for e in liste:
        l = [e] + l
    return l

def test_reverse_l():
    assert([] == reverse_l([]))
    assert([1] == reverse_l([1]))
    assert([1, 2] == reverse_l([2, 1]))
    assert([1, 2, 3] == reverse_l([3, 2, 1]))
    print("test_reverse_l() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def sum(liste):
    """liefert die Summe aller Zahlen in der Liste"""
    summe = 0
    for e in liste:
        summe += e
    return summe

def test_sum():
    assert(0 == sum([]))
    assert(12 == sum([12]))
    assert(12 == sum([4, 8]))
    assert(12 == sum([4, 4, 4]))
    print("test_sum() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def mittelwert(liste):
    """liefert den Mittelwert (Durchschnitt) aller Zahlen in der Liste"""
    if len(liste) > 0:
        return sum(liste) / len(liste)
    else:
        return 0

def test_mittelwert():
    assert(0 == mittelwert([]))
    assert(1 == mittelwert([1]))
    assert(1.5 == mittelwert([1, 2]))
    assert(2 == mittelwert([2, 2, 2]))
    assert(2 == mittelwert([1, 2, 3]))    
    print("test_mittelwert() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def rmdups(liste):
    """(ReMove DUPlicates) liefert Liste, aus der alle Vielfachen entfernt
    (hintereinander).
    Beispiele: [1, 2, 2, 2, 5, 2, 3, 2, 2, 23] -> [1, 2, 5, 2, 3, 2, 23]
               [99, 99, 23, 99, 99, 12, 2] -> [99, 23, 99, 12, 2]
               [] -> []
               [1, 2, 4, 10] -> [1, 2, 4, 10]"""
    if len(liste) > 0:
        ret = [liste[0]]
        for e in liste:
            if ret[-1] != e: # letztes Element ungleich?
                ret.append(e)
        return ret
    else:
        return liste

def test_rmdups():
    assert([1, 2, 5, 2, 3, 2, 23] == rmdups([1, 2, 2, 2, 5, 2, 3, 2, 2, 23]))
    assert([99, 23, 99, 12, 2] == rmdups([99, 99, 23, 99, 99, 12, 2]))
    assert([] == rmdups([]))
    assert([1, 2, 4, 10] == rmdups([1, 2, 4, 10]))
    print("test_rmdups() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def isin(item, liste):
    """ist item in liste enthalten?"""
    for e in liste:
        if item == e:
            return True
    return False

def rmalldups(liste):
    """(ReMove ALL DUPlicates) liefert Liste, aus der alle Vielfachen
    entfernt (müssen nicht hintereinander stehen).
    Beispiele: [1, 2, 2, 2, 5, 2, 3, 2, 2, 23] -> [1, 2, 5, 3, 23]
               [99, 99, 23, 99, 99, 12, 2] -> [99, 23, 12, 2]
               [] -> []
               [1, 2, 4, 10] -> [1, 2, 4, 10]"""
    if len(liste) > 0:
        ret = [liste[0]]
        for e in liste:
#            if e not in ret: # schon vorhanden?
            if not isin(e, ret):
                ret.append(e)
        return ret
    else:
        return liste

def test_rmalldups():
    assert([] == rmalldups([]))
    assert([1, 2, 5, 3, 23] == rmalldups([1, 2, 2, 2, 5, 2, 3, 2, 2, 23]))
    assert([99, 23, 12, 2] == rmalldups([99, 99, 23, 99, 99, 12, 2]))
    assert([1, 2, 4, 10] == rmalldups([1, 2, 4, 10]))
    print("test_rmalldups() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def binsearch(liste, such):
    """liefert den Index von such in der sortierten Liste liste oder -1,
    wenn such in liste nicht vorkommt. Es ist die binäre Suche anzuwenden!
    """
    first = 0
    last = len(liste) - 1
    mid = (last - first) // 2 + first
    #print(first, mid, last)
    while len(liste) > 0 and first <= last:
        if such < liste[mid]:
            last =  mid - 1
        elif such > liste[mid]:
            first = mid + 1
        else:
            return mid #index found
        mid = (last - first) // 2 + first
        #print(first, mid, last)
    return -1 # not found

def test_binsearch():
    """static tests"""
    assert(binsearch([], 1) == -1)
    assert(binsearch([1], 2) == -1)
    assert(binsearch([1], 0) == -1)
    assert(binsearch([1], 1) == 0)
    assert(binsearch([1,3], 0) == -1)
    assert(binsearch([1,3], 2) == -1)
    assert(binsearch([1,3], 1) == 0)
    assert(binsearch([1,3], 3) == 1)
    assert(binsearch([1,3], 4) == -1)
    assert(binsearch([1,3,5], 2) == -1)
    assert(binsearch([1,3,5], 4) == -1)
    assert(binsearch([1,3,5], 1) == 0)
    assert(binsearch([1,3,5], 3) == 1)
    assert(binsearch([1,3,5], 5) == 2)
    assert(binsearch([1,3,5], 6) == -1)
    assert(binsearch([1,3,5,7], 0) == -1)
    assert(binsearch([1,3,5,7], 1) == 0)
    assert(binsearch([1,3,5,7], 3) == 1)
    assert(binsearch([1,3,5,7], 5) == 2)
    assert(binsearch([1,3,5,7], 7) == 3)
    assert(binsearch([1,3,5,7], 9) == -1)
    assert(binsearch([1,3,5,7], 2) == -1)
    assert(binsearch([1,3,5,7], 4) == -1)
    assert(binsearch([1,3,5,7], 6) == -1)
    import random
    for i in range(25):
        liste = [random.randint(0, 100) for i in range(100)]
        liste.sort()
        num = random.randint(0, 100)
        if num in liste:
            if binsearch(liste, num) < 0:
                print(liste)
                print("%d should be found in list" % num)
                assert(binsearch(liste, num) >= 0)
        else:
            if binsearch(liste, num) >= 0:
                print(liste)
                print("%d should not be found in list" % num)
                assert(binsearch(liste, num) < 0)
    print("test_binsearch() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def sumteilbar(liste, teiler):
    """Berechnet die Summe aller durch teiler teilbaren Zahlen der Liste"""
    summe = 0
    for e in liste:
        if e % teiler == 0:
            summe += e
    return summe

def test_sumteilbar():
    assert(0 == sumteilbar([], 2))
    assert(0 == sumteilbar([1], 2))
    assert(0 == sumteilbar([1, 3], 2))
    assert(0 == sumteilbar([1, 3, 23], 2))
    assert(2 == sumteilbar([2], 2))
    assert(2 == sumteilbar([1, 2], 2))
    assert(2 == sumteilbar([1, 2, 23], 2))
    assert(32 == sumteilbar([1, 8, 24], 2))
    assert(27 == sumteilbar([3, 8, 24], 3))
    print("test_sumteilbar() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def issorted(liste):
    """Prüft, ob die Liste sortiert ist, d.h. ob e1 <= e2 <= e3 <= ... <= en
    e1, e2, ... en sind die n Elemente der Liste mit Länge n"""
    for i in range(len(liste) - 1):
        if liste[i] > liste[i + 1]:
            return False
    return True

def test_issorted():
    assert(issorted([]))
    assert(issorted([1]))
    assert(issorted([1, 1]))
    assert(issorted([1, 3, 3, 4]))
    assert(not issorted([1, 0]))
    assert(not issorted([1, 2, 1]))
    print("test_issorted() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def printlxl(liste1, liste2):
    """gibt eine Multiplikationstabelle der beiden Listen aus.
    Zum Beispiel:
       a = [1, 2, 4, 5]
       b = [3, 2, 2]
       printlxl(a, b) gibt dann folgendes aus:
         3   2   2 
         6   4   4 
        12   8   8 
        15  10  10 
    Die erste Liste bestimmt die Anzahl der Zeilen, die zweite die Anzahl 
    der Spalten. Es wird immer ein Element der ersten Liste mit einem der
    zweiten Liste multipliziert.
    """
    erg = lxl(liste1, liste2)
    for zeile in erg:
        for spalte in zeile:
            print("%4d" % spalte, end = "")
        print()

def test_printlxl():
    a = [1, 2, 4, 5]
    b = [3, 2, 2]
    printlxl(a, b)
    print("test_printlxl() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
def lxl(liste1, liste2):
    """liefert eine Multiplikationstabelle der beiden Listen (Liste von 
    Listen).
    Zum Beispiel:
       a = [1, 2, 4, 5]
       b = [3, 2, 2]
       lxl(a, b) liefert folgende Liste zurück:
       [[3, 2, 2], [6, 4, 4], [12, 8, 8], [15, 10, 10]]
       übersichtlich formatiert:
       [[3, 2, 2], 
        [6, 4, 4], 
        [12, 8, 8], 
        [15, 10, 10]]
    Die erste Liste bestimmt die Anzahl der "Zeilen", die zweite die Anzahl 
    der "Spalten". Es wird immer ein Element der ersten Liste mit einem der
    zweiten Liste multipliziert.
    """
    erg = []
    if len(liste2) > 0:
        for a in liste1:
            zeile = []
            for b in liste2:
                zeile.append(a * b)
            erg.append(zeile)
    return erg

def test_1x1():
    a = [1, 2, 4, 5]
    b = [3, 2, 2]
    assert([[3, 2, 2], [6, 4, 4], [12, 8, 8], [15, 10, 10]] == lxl(a, b))
    assert([] == lxl([], []))
    assert([] == lxl([], [1, 2]))
    assert([] == lxl([1, 2], []))
    print("test_1x1() ok")

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----
# Testaufrufe:
test_isint()
test_isintb()
test_sumdigits()
test_horner()
test_isipv4()
test_anz_zahlen()
test_sum_zahlen()
test_iskonto()
test_reverse_s()
test_toupper()
test_tolower()
test_isupper()
test_islower()
test_ispalindrom()
test_ispalindrom_l()
test_reverse_l()
test_sum()
test_mittelwert()
test_rmdups()
test_rmalldups()
test_binsearch()
test_sumteilbar()
test_issorted()
test_printlxl()
test_1x1()

Die Funktionen isupper() und islower() sind eigentlich nicht korrekt, denn liefert z.B. isupper("A4C") True. Man müsste also eher schreiben:

def isupper(string):
    """prüft, ob String nur Großbuchstaben enthält"""
    for c in string:
        if not ('A' <= c <= 'Z' or c in "ÄÖÜ"):
            return False
    return True

Labels: , ,


Sonntag, 6. März 2011

 

Übungsbeispiele zu Sequenzen, Schleifen und Funktionen in Python (POS1: 1B)

Hier finden Sie eine Reihe von Aufgabenstellungen als Übung zur nächsten praktischen Arbeit.
Gegeben sind minimale Funktionsrümpfe mit der Aufgabenstellung als DocString.

Die Funktionen sind ohne Standardmethoden bzw. Funktionen zu implementieren (ausgenommen die Funktion len()). Sollte es eine Standardfunktion zur Lösung der Aufgabe geben, verwenden Sie diese, um das Ergebnis zu prüfen. Bereiche ([von:bis]) dürfen verwendet werden, sofern sie benötigt werden.

def isint(string)
    """prüft, ob der String eine ganze Zahl ist (Vorzeichen optional, 
    nur Ziffern)"""
    pass

def isintb(string, basis)
    """prüft, ob der String eine ganze Zahl zur Basis basis ist (Vorzeichen
    optional, nur Ziffern)"""
    pass

def isnum(string)
    """prüft, ob der String eine Zahl ist (Vorzeichen optional, Ziffern und
    Komma)"""
    pass

def horner(zahl, basis):
    """berechnet Dezimalzahl aus zahl (String) zur Basis basis.
    liefert Zahl."""
    pass

def sumdigits(zahl):
    """Berechnet die Ziffernsumme aus der gegebenen Zahl (int)"""
    pass

def sumdigits_s(string):
    """Berechnet die Ziffernsumme aus der gegebenen Zahl (str), wenn
    es überhaupt eine Zahl ist. Liefert -1, wenn keine gültige Zahl"""
    pass

def isipv4(string):
    """Prüft, ob der gegebene String eine IPv4-Adresse enthält.
    IPv4-Adressen sind 4 Zahlen zwischen 0 und 255, die durch einen
    Punkt getrennt sind.
    Beispiele: 127.0.0.1, 193.170.149.129, 81.200.64.185
    Ungültige: 81.2010.64.185, 123.23.12, 127,0,0,1"""
    pass

def anz_vokale(string):
    """Zähle die Selbstlaute im String und liefere Anzahl zurück"""
    pass

def anz_zahlen(string):
    """ermittelt die Anzahl der ganzen Zahlen im String. Eine Zahl
    ist eine ununterbrochene Folge von Ziffern.
    Beispiel: anz_zahlen("x 34 3 12k33 44 hallo 23") liefert 6"""
    pass

def sum_zahlen(string):
    """ermittelt die Summe der ganzen Zahlen im String. Eine Zahl
    ist eine ununterbrochene Folge von Ziffern.
    Beispiel: anz_zahlen("x 34 3 12k33 44 hallo 23") liefert 149"""
    pass

def iskonto(string):
    """Prüft, ob der String eine gültige Kontonummer enthält. Dabei
    gelten folgende Regeln:
    1. genau 9 Ziffern (es dürfen Leerzeichen dazwischen sein
    2. die letzte Ziffer ist eine Prüfziffer und es gilt
       Die Kontonummer ist genau 9 Stellen lang und ist dann richtig, 
       wenn die folgendermaßen gebildete Summe auf Null endet:
       Ziffern mit ungeradem Index werden addiert(=Teilsumme1);
       Ziffern mit geradem Index werden verdoppelt und das jeweilige 
       Produkt addiert, wenn es einstellig ist, andernfalls wird die
       Quersumme des Produkts addiert(=Teilsumme2);
       Summe= teilsumme1 + Teilsumme2
    Beispiel:
       123456782 1+    3+    5+      7+     2  = 18 Teilsumme1
                   2*2+  4*2+  (1+2)+  (1+6)   = 22 Teilsumme2
                               6*2=12  8*2=16
                                               ------
                                                 40 Summe mod 10= Null
    Beispiele für Kontonummern:
       697199107 richtige Kontonummer
       723016699 falsche Kontonummer
    """
    pass

def reverse_s(string):
    """liefert String in umgekehrter Reihenfolge zurück"""
    pass

def toupper(string):
    """liefert String in Großbuchstaben zurück"""
    pass

def tolower(string):
    """liefert String in Kleinbuchstaben zurück"""
    pass

def isupper(string):
    """prüft, ob String nur Großbuchstaben enthält"""
    pass

def islower(string):
    """prüft, ob String nur Kleinbuchstaben enthält"""
    pass

def ispalindrom(string):
    """Prüft, ob der String ein Palindrom ist (von vorne und hinten
    gelesen gleich)."""
    pass

def ispalindrom_l(liste):
    """Prüft, ob die Liste von vorne und hinten gelesen gleich ist."""
    pass

def reverse_l(liste):
    """liefert die Liste in umgekehrter Reihenfolge zurück"""
    pass

def sum(liste):
    """liefert die Summe aller Zahlen in der Liste"""
    pass

def mittelwert(liste):
    """liefert den Mittelwert (Durchschnitt) aller Zahlen in der Liste"""
    pass

def rmdups(liste):
    """(ReMove DUPlicates) liefert Liste, aus der alle Vielfachen entfernt
    (hintereinander).
    Beispiele: [1, 2, 2, 2, 5, 2, 3, 2, 2, 23] -> [1, 2, 5, 2, 3, 2, 23]
               [99, 99, 23, 99, 99, 12, 2] -> [99, 23, 99, 12, 2]
               [] -> []
               [1, 2, 4, 10] -> [1, 2, 4, 10]"""
    pass

def rmalldups(liste):
    """(ReMove ALL DUPlicates) liefert Liste, aus der alle Vielfachen
    entfernt (müssen nicht hintereinander stehen).
    Beispiele: [1, 2, 2, 2, 5, 2, 3, 2, 2, 23] -> [1, 2, 5, 3, 23]
               [99, 99, 23, 99, 99, 12, 2] -> [99, 23, 12, 2]
               [] -> []
               [1, 2, 4, 10] -> [1, 2, 4, 10]"""
    pass

def binsearch(liste, such):
    """liefert den Index von such in der sortierten Liste liste oder -1,
    wenn such in liste nicht vorkommt. Es ist die binäre Suche anzuwenden!
    """
    pass

def sumdigits(zahl):
    """Berechnet die Ziffernsumme aus der gegebenen Zahl (int)"""
    pass

def sumteilbar(liste, teiler):
    """Berechnet die Summe aller durch teiler teilbaren Zahlen der Liste"""
    pass

def issorted(liste):
    """Prüft, ob die Liste sortiert ist, d.h. ob e1 <= e2 <= e3 <= ... <= en
    e1, e2, ... en sind die n Elemente der Liste mit Länge n"""
    pass
def printlxl(liste1, liste2):
    """gibt eine Multiplikationstabelle der beiden Listen aus.
    Zum Beispiel:
       a = [1, 2, 4, 5]
       b = [3, 2, 2]
       printlxl(a, b) gibt dann folgendes aus:
         3   2   2 
         6   4   4 
        12   8   8 
        15  10  10 
    Die erste Liste bestimmt die Anzahl der Zeilen, die zweite die Anzahl 
    der Spalten. Es wird immer ein Element der ersten Liste mit einem der
    zweiten Liste multipliziert.
    """
    pass
def lxl(liste1, liste2):
    """liefert eine Multiplikationstabelle der beiden Listen (Liste von 
    Listen).
    Zum Beispiel:
       a = [1, 2, 4, 5]
       b = [3, 2, 2]
       lxl(a, b) liefert folgende Liste zurück:
       [[3, 2, 2], [6, 4, 4], [12, 8, 8], [15, 10, 10]]
       übersichtlich formatiert:
       [[3, 2, 2], 
        [6, 4, 4], 
        [12, 8, 8], 
        [15, 10, 10]]
    Die erste Liste bestimmt die Anzahl der "Zeilen", die zweite die Anzahl 
    der "Spalten". Es wird immer ein Element der ersten Liste mit einem der
    zweiten Liste multipliziert.
    """
    pass

Schreiben Sie zu den Funktionen jeweils passende Testprogramme.

Labels: , ,


Freitag, 4. März 2011

 

SAR (ROB: 2C)

SAR steht normalerweise für "Search and Rescue" - hier soll es "Search and Remove" heißen.
Ein Roboter soll in einem hellen rechteckigen Feld, das mit einer schwarzen Linie umgeben ist, einen Klotz finden und auf einer dunklen (schwarzen) Fläche in einem der Ecken abstellen. Das Feld sieht etwa so aus:
Finden Sie eine geeignete Strategie, den Klotz aufzuspüren.
Wie kann der Klotz transportiert werden?
Wie findet man die markierte Ecke (Fläche) zum Ablegen?

Labels: ,


 

Java Übungen und Aufgabe (POS1: 2A, 2C)

Klassen, Objekte

Erzeugen Sie eine Klasse SomeMaths, welche eine statische Konstante PI enthält sowie eine statische Methode double getSquare(double x), welche das Quadrat von x liefert.
Erstellen Sie weiters eine Klasse Circle, welche eine Kreis simuliert. Circle hat ein Attribut radius sowie die Methoden getCircumference(), welche den Umfang liefert, und getArea(), welche die Fläche berechnet. Die Methoden sollen sich der Konstante PI und der Methode getSquare() von SomeMaths bedienen.
Eine dritte Klasse CircleExercise soll im main() drei Kreise vom Typ Circle erzeugen und jeweils den Radius, den Umfang und die Fläche ausgeben.
Das UML-Diagramm sieht etwa so aus:


Arrays

Erzeugen Sie eine Klasse DreieckArray, welche ein privates zweidimensionales Array feld für Integers enthält. Dieses Feld soll "dreieckig" sein und mit folgenden Werten initialisiert werden:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
Die Methoden sind public und sollen folgendes tun:
void create(int zeilen, int spalten) soll ein neues Feld mit den gegebenen Zeilen und Spalten erzeugen (rechteckig).
int[][] getArray() soll das Feld zurückliefern.
int[] summen() soll ein eindimensionales Feld mit den Zeilensummen liefern.
boolean set(int zeile, int spalte, int wert) soll den Wert an der gegebenen Stelle im Feld feld setzen und bei erfolg true zurückliefern. Bei ungültigen Indizes soll false zurückgeliefert werden.

Schreiben Sie eine Klasse TestDreieckArray, in der zwei Objekte von DreieckArray in der Methode main() angelegt werden. Machen Sie zum Testen passende Aufrufe der Methoden (z.B. gültige und ungültige set() usw.).

Streams, Strings

Schreiben Sie eine Klasse ReadFile, welche eine eine auf der Kommandozeile angegebene Datei öffnet und in einen String einliest. Verwenden Sie dazu drei Methoden String liesString(InputStream fileInputStream), String  liesStringBuffer(InputStream fileInputStream) und String liesStringBuilder(InputStream fileInputStream) und messen Sie die Zeiten, die mit den einzelnen Methoden benötigt werden um die Datei zu lesen.
Von der Datei soll jeweils mit read() ein Zeichen gelesen werden. Die Methode liesString() hängt das gelesene Zeichen jeweils an einen String an, die Methode liesStringBuffer() verwendet einen StringBuffer und liesStringBuilder() verwendet einen StringBuilder. Das gelesene Zeichen wird dann immer mit append() an den StringBuffer bzw. StringBuilder angehängt.
Testen Sie mit unterschiedlich großen Dateien (kleine, mittlere, große, ganz große). Sie werden unterschiedliche Zeiten messen bei den verschiedenen Methoden. Erklären Sie die Unterschiede!
Verwenden Sie System.currentTimeMillis() (liefert long), um die aktuelle Zeit in ms zu erhalten.

Random, Calendar, Kommandozeile, Streams

Es sollen Testdaten der Form
Datum Von-Zeit Bis-Zeit
Datum Von-Zeit Bis-Zeit
...
erzeugt werden. Wobei ein Datum die Form JJJJ-MM-TT und Von-Zeit und Bis-Zeit die Form HH:MM haben soll. Weiters sollen folgende Bedingungen erfüllt sein:

Beispiel:
2011-03-04 10:24 10:45
2011-03-04 11:33 12:56
2011-03-05 12:33 17:23

Das Programm soll folgendermaßen aufgerufen werden:
java CalenderTest [-h] [startdatum] [startzeit] [datei] anzahl
   -h         ... Ausgabe dieser Hilfe und Ende
   startdatum ... JJJJ-MM-TT (optional)
   startzeit  ... HH:MM (optional)
   datei      ... Name der Ausgabedatei (optional)
   anzahl     ... Anzahl der zu erzeugenden Zeilen
Wird kein startdatum angegeben, so ist der heutige Tag als Startwert zu verwenden.
Wird kein startzeit angegeben, so ist die aktuelle Uhrzeit als Startwert zu verwenden. Diese Startwerte sind in der ersten Ausgabezeile zu sehen.
Wenn kein Dateiname datei angegeben, so ist die Standardausgabe zu verwenden.
Die Anzahl der zu erzeugenden Zeilen muss mindestens 1 sein!

Fehlermeldungen sind auf System.err auszugeben.

Nennen Sie das Projekt calendertest (klasse-name-calendertest).

Labels: , ,


Mittwoch, 2. März 2011

 

Prüfen Sie Ihr Wissen (POS1: 2A, 2C)

Sie finden an dieser Stelle eine Reihe von Fragen und Aufgaben, die Sie etwa Mitte des zweiten Jahrgangs Informatik beantworten bzw. lösen können sollten.

Theorie
  1. Was ist ein Dictionary in Python?
  2. Was bedeutet der Begriff "Sequenz" in Python? Geben Sie einige Beispiele.
  3. Wie wird eine Funktion in Python definiert?
  4. Welche Arten der Parameterübergabe gibt es in Python?
  5. Wie kann eine Python-Funktion mehr als einen Wert (Parameter) ändern bzw. zurückliefern?
  6. Geben Sie ein Beispiel für eine kürzest mögliche Funktion in Python an!
  7. Was ist eine Variable?
  8. Was versteht man unter "Duck Typing"?
  9. Wie werden in Python for-Schleifen gebildet?
  10. Was ist ein Python-Modul? Wofür werden Module verwendet?
  11. Wie kann eine (Text-)Datei gelesen werden?
  12. Wie kann man in Python Kommandozeilenparameter auswerten?
  13. Welche Grunddatentypen kennen Sie? Geben Sie auch den Speicherbedarf und den Wertebereich an.
  14. Was versteht man unter den Eigenschaften eines Objekts? Wie spiegelt sich das in einer Klasse wieder?
  15. Welche Kontrollstrukturen kennen Sie?
  16. Was ist ein Array?
  17. Geben Sie ein Beispiel eines eindimensionalen Arrays in Java an.
  18. Wie kann man ein eindimensionales Array in Java initialisieren?
  19. Geben Sie ein Beispiel eines zweidimensionalen Arrays in Java an.
  20. Wie kann man ein zweidimensionales Array in Java initialisieren?
  21. Kann man in Java ein zweidimensionales Array definieren, welches "nicht rechteckig" (rechteckig bedeutet hier: alle Zeilen gleich lang) ist?
  22. Geben Sie einen Algorithmus (ohne Programmdetails) an, um alle Elemente eines eindimensionalen Arrays zu ermitteln, die ein bestimmtes Kriterium (z.B. ist größer als ein bestimmter Wert) erfüllen.
  23. Geben Sie einen Algorithmus (ohne Programmdetails) an, um alle Elemente eines zweidimensionalen Arrays zu ermitteln, die ein bestimmtes Kriterium erfüllen.
  24. Geben Sie einen Algorithmus (ohne Programmdetails) an, um das Maximum aller Werte eines eindimensionalen Arrays zu ermitteln.
  25. Geben Sie einen Algorithmus (ohne Programmdetails) an, um das Minimum aller Werte eines eindimensionalen Arrays zu ermitteln.
  26. Geben Sie einen Algorithmus (ohne Programmdetails) an, um das Maximum aller Werte eines zweidimensionalen Arrays zu ermitteln.
  27. Geben Sie einen Algorithmus (ohne Programmdetails) an, um das Minimum aller Werte eines zweidimensionalen Arrays zu ermitteln.
  28. Was ist eine Funktion? Geben Sie ein Beispiel in Java inklusive der Verwendung der Funktion.
  29. Was ist eine Methode? Geben Sie ein Beispiel in Java inklusive der Verwendung der Funktion.
  30. Was ist der Unterschied zwischen einer Variable eines Grunddatentyps und einer Variable vom Typ einer Klasse?
  31. Was ist eine Klasse?
  32. Was ist ein Objekt? Erklären Sie den Zusammenhang zwischen Objekt und Klasse.
  33. Was sind Strings?
  34. Wie kann man Strings vergleichen?
  35. Wozu benötigt man Kommentare?
  36. Wozu benötigt man Programmierrichtlinien?
  37. Vergleichen Sie Java mit Python.
  38. Woran soll Sie die Geschichte "Shlemiel the Painter" erinnern?
  39. Was muss grundsätzlich (ohne Programmdetails) gemacht werden, um von einer Datei zu lesen?
  40. Was muss grundsätzlich (ohne Programmdetails) gemacht werden, um in eine Datei zu schreiben?
  41. Geben Sie einen Algorithmus (ohne Programmdetails) an, um alle Zeilen einer Textdatei zu ermitteln, die ein bestimmtes Kriterium (z.B. enthält einen bestimmten String) erfüllen.
  42. Welche Arten von Programm(ier)fehlern kennen Sie? Wie findet man diese Fehler?
  43. Wieviele main()-Methoden muss eine Java-Klasse besitzen?
  44. Wieviele main()-Methoden kann eine Java-Klasse maximal haben?
  45. Wieviele Klassen können in einer einzelnen Java-Datei implementiert werden?
  46. Welchen Wert hat die Variable einInteger? Begründen Sie Ihre Antwort!
    int einInteger = Integer.MAX_VALUE + 1;
    
  47. Was versteht man unter impliziter bzw. expliziter Typumwandlung? Nennen Sie jeweils mindestens ein Beispiel.
Praxis:
In diesem Blog und auch im alten Blog gibt es bereits eine Reihe von Aufgaben. Vergleichen Sie Ihre Lösungen mit jenen Ihrer MitschülerInnen.
Hier noch ein paar "Grundübungen":
  1. Erstellen Sie ein Pythonprogramm, welches das Maximum einer Sequenz ermittelt. Definieren Sie dazu eine fixe Testsequenz mit folgenden Werten [-1, -23, -2, -32,-89, -100, -1, -99]. Finden Sie weitere Beispieltestdaten.
  2. Erstellen Sie ein Pythonprogramm, welches das Minimum einer Sequenz ermittelt. Definieren Sie dazu eine fixe Testsequenz mit folgenden Werten [1, 23, 2, 32, 89,100, 1, 99]. Finden Sie weitere Beispieltestdaten.
  3. Erstellen Sie ein Javaprogramm, welches das Maximum einer Sequenz ermittelt. Definieren Sie dazu eine fixe Testsequenz mit folgenden Werten [-1, -23, -2, -32,-89, -100, -1, -99]. Finden Sie weitere Beispieltestdaten.
  4. Erstellen Sie ein Javaprogramm, welches das Minimum einer Sequenz ermittelt. Definieren Sie dazu eine fixe Testsequenz mit folgenden Werten [1, 23, 2, 32, 89,100, 1, 99]. Finden Sie weitere Beispieltestdaten.
  5. Erstellen Sie ein Pythonprogramm, welches die Häufigkeit von Elementen einer Sequenz (Liste) ermittelt. Definieren Sie dazu eine Testsequenz mit folgenden Werten [1, 2,-3, 4, 2, 1, 10, -3, 4, 2, 2, 10] bzw. ["hallo", "du", "test", "du","Maus", "test", "Test"]. Das Ergebnis soll eine Liste von unterschiedlichen Werten sein und wie oft sie vorkommen.
  6. Betrachten Sie die Methode dividiere im folgenden Programm. lässt sich das Programm übersetzen? Falls nicht, dann korrigieren Sie die Fehler:
    public class DivisionApp {
      public static void main(String[] args) {
        int ergebnis = dividiere(40, 5);
        System.out.println("Ergebnis: " + ergebnis);
      }
    
      public static int dividiere(int divident, int divisor) {
        if (divisor > 0) {
          return divident / divisor;
        }
      }
    }
    
  7. Folgende Ausgabe soll erzeugt werden: 1 3 5 7 9. Lösen Sie die Aufgabe
    • mithilfe einer while-Schleife
    • mithilfe einer do-while-Schleife
    • mithilfe einer for-Schleife und des Modulo-Operators
    • mithilfe einer for-Schleife ohne den Modulo-Operator
  8. Folgende Ausgabe soll erzeugt werden: 1 2 4 7 11 16 22 29 37. Lösen Sie die Aufgabe
    • mithilfe einer do-while-Schleife
    • mithilfe einer for-Schleife
  9. Schreiben Sie ein Java-Programm, welches mithilfe eines Stacks folgende Werte in umgekehrter Reihenfolge ausgibt: 23 21 20 18 17 13 11 5 3
  10. Schreiben Sie ein Java-Programm, welches mithilfe einer Queue folgende Werte in derselben Reihenfolge ausgibt: 23 21 20 18 17 13 11 5 3
  11. Schreiben Sie auf Basis der letzten beiden Beispiele Programme, die Strings statt der Integer-Werte verwenden können.

Labels: , , , ,


 

Klammerprüfung mittels Stack (POS1: 2A, 2C)

Schreiben Sie ein Java-Programm, welches zeichenweise von der Standardeingabe liest und prüft, ob die eingegebenen Klammern eine gültige Klammerung mit ([{}]) ergeben.
Beispiele für korrekte Klammerung:

()([])[[([])]]{}
x{abc(sdf(c))asd}asx
()
[]
((()))
{{{(())}}}

Beispiele für falsche Klammerung:

(()
[])
(([)])

Das Programm soll nur ausgeben, ob die Klammerung korrekt ist. Das Programm soll alle Klammern bis EOF prüfen (bei den Beispielen oben ist jeweils eine Zeile eine Eingabe!).
Verwenden Sie für die Implementierung einen (selbstprogrammierten) Stack (eigene Klasse):
void push(char elem);   // Element oben auf Stack
char pop();             // oberstes Element
boolean isEmpty();      // false wenn nicht leer

Erweiterung um Programmargumente und Dateien


Erweitern Sie das Programm so, dass eine Hilfe ausgegeben werden kann, mehrere Eingabedateien und optional eine Ausgabedatei angegeben werden kann. Eine Eingabedatei entspricht einem Klammerausdruck (der natürlich aus mehreren Zeilen bestehen kann). Wird keine Eingabedatei angegeben, so ist von stdin zu lesen.

Aufruf:

java Klammer [-h|-o out] [eingabedateien...]
  -h             ... Hilfe
  -o out         ... Ausgabe in die Datei out
  eingabedateien ... optional mehrere Eingabedateien

Detaillierte Fehlermeldungen


Erweitern Sie das Programm um die Ausgabe einer detaillierten Fehlermeldung. Welcher Fehler wurde gefunden? Wo wurde der Fehler gefunden (Zeile/Spalte)?
mögliche Fehler

falsche Klammer-Zu
zu viele Klammern-Zu
zu wenig Klammern-Zu

Labels: , ,


Dienstag, 1. März 2011

 

Binärdateien bearbeiten mit Java (PR: 5A, 5B)

Entwickeln Sie sie eine Klasse Data, die das Interface DB implementiert. Die Klasse Data soll die Methoden zum Bearbeiten (Lesen, Schreiben) binärer Dateien nach dem unten beschriebenen Datenformat bereitstellen.

package data;
/**
 * RuD-Übung 2011
 *
 * data.DB.java
 * @author (c) 2011, Harald R. Haberstroh
 * 1.3.2011
 */


import java.rmi.Remote;
import java.rmi.RemoteException;

/**
 * @author Harald R. Haberstroh (hh)
 * 
 */
public interface DB extends Remote {
  /**
   * Sperre den gegebenen Datensatz. Ist der Datensatz bereits gesperrt, dann
   * wait().
   * 
   * @param recNo
   *          Nummer des zu sperrenden Datensatzes.
   * @return Sperr-Identifikation.
   * @throws RemoteException
   * @throws RecordNotFoundException
   *           ungültige Datensatznummer
   */
  public long lock(int recNo) throws RemoteException, RecordNotFoundException;

  /**
   * Hebe Sperre wieder auf. notify().
   * 
   * @param recNo
   *          Nummer des gesperrten Datensatzes
   * @param lockCookie
   *          Sperr-Identfikation.
   * @throws RemoteException
   * @throws RecordNotFoundException
   *           ungültige Datensatznummer
   * @throws SecurityException
   *           ungültiges lockCookie oder nicht gelockt
   */
  public void unlock(int recNo, long lockCookie) throws RemoteException,
      RecordNotFoundException, SecurityException;

  /**
   * lege neuen Datensatz an.
   * 
   * @param record
   *          Neuer Datensatz
   * @return Nummer des neuen Datensatzes
   * @throws RemoteException
   */
  public int create(Record record) throws RemoteException;

  /**
   * lösche Datensatz. Sperre wird aufgehoben, notify();
   * 
   * @param recNo
   *          Nummer des Datensatzes.
   * @param lockCookie
   *          Sperr-Identfikation.
   * @throws RemoteException
   * @throws RecordNotFoundException
   *           ungültige Datensatznummer
   * @throws SecurityException
   *           ungültiges lockCookie oder nicht gelockt
   */
  public void delete(int recNo, long lockCookie) throws RemoteException,
      RecordNotFoundException, SecurityException;

  /**
   * ändere Datensatz (überschreibe mit dem gegebenen Datensatz).
   * 
   * @param record
   *          neuer Datensatz
   * @param recNo
   *          Nummer des gesperrten Datensatzes
   * @param lockCookie
   *          Sperr-Identfikation.
   * @throws RemoteException
   * @throws RecordNotFoundException
   *           ungültige Datensatznummer
   * @throws SecurityException
   *           ungültiges lockCookie oder nicht gelockt
   */
  public void update(Record record, int recNo, long lockCookie)
      throws RemoteException, RecordNotFoundException, SecurityException;

  /**
   * suche Datensätze.
   * 
   * @param criteria
   *          String-Array mit Kriterien, null matcht alles, sonst
   *          Stringrepräsentation des Suchkriteriums (entspricht den Daten in
   *          Record).
   * @return Array von Recordnummern
   * @throws RemoteException
   */
  public int[] find(String[] criteria) throws RemoteException;

  /**
   * lese Datensatz.
   * 
   * @param recNo
   *          Nummer des zu lesenden Datensatzes.
   * @return Record
   * @throws RemoteException
   * @throws RecordNotFoundException
   *           Datensatz existiert nicht.
   */
  public Record read(int recNo) throws RemoteException, RecordNotFoundException;
}
Die Klasse Data muss zusätzlich einen Konstruktor Data(String dateiname) bereitstellen, der die Datei dateiname (z.B. "machines.db") öffnet und zur Bearbeitung mit den im Interface DB definierten Methoden bereitstellt. Der Konstruktor muss die nötigen Exceptions werfen, wenn die Datei nicht geöffnet werden kann.
Der Parameter String[] criteria der Methode find() enthält das Suchkriterium, wobei jedem Feld ein String zugeordnet ist. Soll also nur nach einem Namen (siehe unten) gesucht werden, so muss criteria[1] den gesuchten Namen (bzw. einen Teil davon) enthalten. Alle anderen Elemente des Arrays müssen dann null sein. Soll nach mehreren Kriterien selektiert werden, so sind die entsprechenden Felder mit Werten zu belegen.
find() liefert dann ein Array mit den Indizes der passenden Datensätze.
Die Datensatznummer recNo ist immer der Index innerhalb der Datei!
Gelöschte Datensätze werden einfach als gelöscht markiert (s.u.) und müssen beim Anlegen neuer Datensätze zuerst verwendet werden (erst wenn kein gelöschter Datensatz existiert, wird die Datei vergrößert).
RecordNotFoundException wird immer dann geworfen, wenn recNo auf einen gelöschten Datensatz weist oder, wenn der Datensatz gar nicht existiert. Lösen Sie das Problem, dass nur mit der einfachen RecordNotFoundException das Dateiende nicht ermittelt werden kann, in geeigneter Weise.

Record muss in geeigneter Weise implementiert werden.

Datensätze sollen gesperrt werden können (die Klasse Data kann von mehreren parallelen Threads verwendet werden). Dazu soll die Methode lock() eine (beliebige) dem Datensatz zuordenbare Zahl (Lockcookie bzw. Sperr-Identifikation) liefern und den Satz sperren. Die Methoden, die eine Sperre benötigen müssen dann anhand dieses Lockcookies überprüfen, ob der Datensatz gesperrt ist und die entsprechende Operation ausführen. Ist der Datensatz nicht gesperrt oder das Lockcookie ungültig, so ist eine SecurityException zu werfen. Die Methode unlock() hebt die Sperre wieder auf. Nach einem delete() braucht man (kann man) kein unlock() machen.

Datenformat


Die Strings sind kodiert mit dem Encoding ISO-8859-1, d.h. jedes Zeichen ist einem Byte kodiert und es sind nur ASCII-Zeichen sowie die westeuropäischen Erweiterungen erlaubt. Es gibt Strings fixer Länge (werden mit Leerzeichen aufgefüllt) und Strings variabler Länge (werden wie C-Strings mit '\0' abgeschlossen) mit einer Obergrenze.
Die Datei mit den (Test-)Daten hat folgendes Format:
Die Datei beginnt mit einem Header:
4 Bytes Magic Cookie, Wert 4242
4 Offset in Bytes zu den eigentlichen Daten
2 Bytes Anzahl Felder pro Datensatz
Pro Feld gibt es folgenden Eintrag:
2 Bytes Länge Feldname
n Bytes Feldname
2 Bytes Länge Feldbeschreibung
n Bytes Feldbeschreibung für die Tabellenansicht
1 Byte  Datentyp: 'F' ... fix, 'V' ... variabel, 'C' ... ein Zeichen
2 Bytes Feldlänge
Nach den Feldbeschreibungen, d.h. ab dem Offset, finden sich die eigentlichen Datensätze gemäß der Beschreibungen im Header:
2 Bytes Flag Gelöscht (== 0 ... gelöscht, != 0 ... aktiv)
n Bytes Daten gemäß der Datensatzbeschreibung

Testdatei machines.db


Diese Datei enthält nur die Geräte (und deren grafische Koordinaten):
Feldname Feldbeschreibung (maximale) Länge Typ Bemerkung
id Geräte-Id 10 String '\0' terminiert z.B. "z23"
name Gerätename 100 String '\0' terminiert Gerätebeschreibung, z.B. "Laderoboter Halle B"
x x 5 String fixe Länge z.B. " 50" (vorne mit Leerzeichen aufgefüllt)
y y 5 String fixe Länge z.B. " 200"

Testen der Klasse Data


Schreiben Sie passende Tests um die Klasse Data zu testen. Der erste Test soll einfach die Datei machines.db in lesbarer Form auf der Konsole ausgeben.
Die weiteren Tests sollen natürlich die gesamte Funktionalität und das Locking testen.

Downloads



Abgabe


Nennen Sie das Projekt klasse-name-data also z.B. 5ad-meier-data und checken Sie es am CVS ein. Termin wird noch bekanntgegeben.

Wie geht's weiter


Die Datenklasse soll dann später in einem Client-/Server-System zum Einsatz kommen, wo mehrere grafische Clients eine Binärdatei bearbeiten sollen. Die Angabe dazu folgt...

Labels: , ,


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

Abonnieren Posts [Atom]