Mathpack als 2D-Disp-Demo

Auch Computer kommen ohne Zahlen klar


Das System Mathpack gestattet den Aufbau eigener Elemente, die den Gesetzen der Mathematik unterliegen. So können ohne weitere Probleme komplexe Zahlen, Vektoren oder auch Matritzen in einfachen Ausdrücken bearbeitet werden. Um den Anwender nicht bei Null beginnen zu lassen, ist eine Arithmetik beigefügt, die jederzeit erweitert, geändert oder entfernt werden kann. Die folgenden Seiten beschäftigen sich mit dem Aufbau dieser Arithmetik und bilden so die Grundlage für eigene Erweiterungen.
Mathpack stellt prizipiell nur einen Rahmen zur Verfügung, innerhalb dessen sich die vom Benutzer definierten Objekte bearbeiten lassen. Objekt ist auch das erste Stichwort, denn zunächst muß geklärt werden um was sich der Benutzer denn alles kümmern soll. Am einfachsten scheint die Bereitstellung einer Zahl zu sein, denn Computer gehen ja ausschlieďż˝ich mit diesen "Dingern" um.
 

Zahlen

Ob es sich bei einer Zeichenfolge um eine Zahl handelt kann leider nicht ohne Weiteres gesagt werden. Die Zahlenbasis ist zu beachten, Vorzeichen und vielleicht auch ein Komma oder ein Dezimalpunkt. All diese Kleinigkeiten müssen beachtet werden, um von einer Zeichenfolge sagen zu können: Das ist eine Zahl. Glücklicherweise stellt JavaTM hier bereits viele Möglichkeiten bereit, die Zeichenfolgen in Zahlen umwandeln. Die Klassifizierung von Zahlen wird eben diese bereitgestellten Möglichkeiten nutzen, womit bereits die erste Klasse aufgebaut werden kann. Die elementaren Methoden dieser Klasse sind vorgeschrieben und müssen vorhanden sein. Es handelt sich um zwei Methoden; die eine eine erzeugt ein (Zahlen)Objekt aus einer Zeichenfolge, die andere überprüft, ob die Zeichenfolge in eine Zahl gewandelt werden kann. Das Aussehen dieser beiden Methoden ist festgelegt in

mathpack/values/anyValue.java

Ob weitere Methoden nötig oder sinnvoll sind, obliegt dem Anwender. Wenn eigene Objekte für Werte erzeugt werden sollen, muss zunächst eine Klasse dieser Objekte entworfen werden. Die hier besprochene Arithmetik benutzt eine in Java TM bereits vorhandene Klasse namens Double. Es wird also eine Klasse definiert, die unbedingt die beiden erwähnten Methoden in der vorgeschriebenen Form (interface) benutzt. Begonnen wird mit der Testmethode t.

 public boolean t( String str) {
  try
  {
   new Double( str);
   return true;
  }
  catch( NumberFormatException nfe)
  {
   return false;
  }
 }
Im try-Part wird einfach ein neues Double-Object instanziert. Weil dieses jedoch aus den bereits besprochenen Gründen schiefgehen könnte, wird der Protest, der von Java TM in Form einer NumberFormatException vorgebracht wird, im catch-Part abgefangen. In Kurzfassung:
Versuche aus der Zeichenfolge ein Double-Object zu erzeugen.
Wenn es gelungen ist, verlasse die Methode mit true.
Wenn eine NumberFormatException aufgefangen wurde, verlasse die Methode mit false.
Für alle anderen Java TM-internen Wertobjekte (Integer, short, ...) funktioniert diese Vorgehensweise ebenfalls. Natürlich muß dann statt Double eben die entsprechende Klassenbezeichnung eingesetzt werden. Obwohl hier bereits ein entsprechendes Objekt erzeugt wurde, ist eine explizite Instanzierung vorgeschrieben. Die Instanzierung eines Objekts das Zahlen repäsentiert, ist durch die Methode c vorgeschrieben. Genau wie bei der vorangegangenen Methode muß auch c vom Benutzer ausdrücklich spezifiziert werden.
 public Object c( String str) {
  return new Double( str);
 }
Es wird ein Object übergeben, das einfach aus der intern erzeugten Instanz einer Klasse namens Double hergestellt wurde. Wenn der Anwender eine andere Klasse als Zahl vereinbart, wird eben diese instanziert. Die Methoden um aus einer Zeichenfolge das Objekt einer Zahl herzustellen stehen jetzt fest. Es fehlt noch die Klassifizierung und damit die universelle Bereitstellung eines Zahlenobjektes. Hier ist der Benutzer alleiniger Herrscher über die Namens- und Ortsvergabe.

Klasse der Werte

Bei dem Entwurf dieser Klasse ist zu berücksichtigen, dass zwei Methoden zwingend vorhanden sein müssen. Beide Methoden wurden im letzten Abschnitt hergeleitet und müssen in eine Klasse für allgemeine Wertangaben integriert werden. Die entsprechende Klasse im Arithmetik-Paket wird eingeleitet durch
public class value implements mathpack.values.anyValue {
Das Schlüsselwort public erklärt diese Klasse als allgemein verfügbar. Gefolgt von class, das die Klassifizierung einleitet. Anschließend folgt der Name der Klasse (nicht etwa die Bezeichnung eines Objekts) value. Dieser Name kann vom Benutzer frei vergeben werden. Zwei Methoden sollen zwingend sein, was durch implements und die Ortsangabe des Pakets erfolgt. Zusammengefügt ergibt sich nun die elementare Klasse für Zahlenwerte des Objekttyps Double zu folgendem Bild:
//**************************************/
//*                                    */
/** Klasse aller Werte einer Grammatik */
//*                                    */
//**************************************/

public class value implements mathpack.values.anyValue {

 //*************************************************************/
 /** Test auf Validierbarkeit eines Zeichenfolge (hier Double) */
 //*************************************************************/

 public boolean t( String str) {
  try
  {
  Double dbl;
   dbl = new Double( str);
   return true;
  }
  catch( NumberFormatException nfe)
  {
   return false;
  }
 }

 //**************************************************/
 /** Instanzierung eines Wertelements (hier Double) */
 //**************************************************/

 public Object c( String str) {
  return new Double( str);
 }
}

Es ist nicht sofort erkenntlich, warum ein Objekt auf diese umständliche Art klassifiziert wird, jedoch mit etwas Geduld kann dieser Weg nachvollzogen werden. Das System Mathpack arbeitet mit Objekten und es ist dem System völlig egal, welche Objekte es benutzt. Dem Anwender ist diese Gleichgültigkeit aber nicht zuzumuten, weshalb eine Schnittstelle zwischen dem was der Benutzer meint und dem was das System bearbeitet, bereitgestellt werden muss. Genau diese Schnittstelle wurde soeben in ihrer minimalsten Form fertiggestellt.

Die Benutzung dieser Klasse ist relativ einfach und verdeutlicht etwas den Schnittstellengedanken. Angenommen die Zeichenfolge 123.45 soll als Wertobjekt über die eben hergeleitete Klasse als Zahl erstellt werden. Zunächst wird eine Ortsangabe benötigt, unter der die Klasse zu finden ist.
import mathpack.arithmetic.*;
leistet hier den entsprechenden Dienst. Es folgen die Formalien, die jeder Programmiersprache innewohnen um ein Programm zu starten. Hier soll etwas getestet werden, weshalb die Programmsequenz den treffenden Namen test erhält. Das Hauptprogramm trägt die vorgeschriebene Bezeichnung main.
import mathpack.arithmetic.*;
public class test {
 public static void main( String args[]) {
 }
}
Dieses Schema (abgesehen von der ersten Zeile) kann für alls möglichen Tests zugrunde gelegt werden. Jedenfalls soll jetzt zwischen den geschweiften Klammern bei main eine Zeichenfolge verfügbar gemacht werden. Mit
String folge = "123.45";
ist diese Hürde genommen. Jetzt folgt die Instanzierung des entworfenen Objekts mit der Klassenbezeichnung value. Der Name des Objekts soll zahl sein. Eine Zeile mit
value zahl = new value();
erledigt diese Aufgabe. Es fehlt nur noch ein Objekt, das den eigentlichen Wert 123.45 enthält. Dieses Objekt muss aber universelle Gültigkeit besitzen. Somit bleibt nur
Object objekt = null;
Über die Instanz der Werteklasse value, die hier die Bezeichnung zahl trägt, wird zunächst die Zeichenfolge auf Wandlungsfähigkeit überprüft. Je nachdem, ob aus die Folge eine Zahl gewandelt werden kann, soll eine entsprechende Ausgabe auf der Systemebene erfolgen. Sehr skizzenhaft etwa
if (zahl.t( folge))
// then (für Verwirthe)
        System.out.println( "Klappt");
   else System.out.println( "Klappt nicht");
Angenommen es klappt, dann kann das Objekt des Wertes mit der Instanzierungsmethode c erzeugt werden, was ungefähr folgendes Aussehen hat:
objekt = zahl.c( folge);
Jetzt wurde endlich ein Wertobjekt aufgebaut, das den intern die Zahl 123.45 besitzt. Intern deshalb, weil ein Objekt aber keine nackte Zahl vorhanden ist. Zwar liefert deine print( objekt)-Anweisung die ursprüngliche Zeichenfolge 123.45, aber der Veruch den Wert (objekt + 5) auszurechenen, wird an der Syntax (Typenverletzung) scheitern. Die Zahl wird erst durch eine Typenwandlung und eine entsprechende Methode zugänglich. Glücklicherweise ist der Typ von objekt bekannt es handelt sich um ein Double-Object. Um aus objekt ein Double-Object zu machen, bedarf es einer Typenwandlung der Form
(Double)objekt
Eine andere Wandlung ist nicht möglich, weil die Methode c zwar den Typ Object liefert, dieser aber auf alle Instanzen von Java TM zurifft. die umgekehrte Wandlung erfolgt nur, wenn die Typen instanzmäßig ubereinstimmen. Ein Double-Object hat eine eigene Methode ihren Wert mitzuteilen, sie lautet doubleValue(). Die Kombination aus Typenwandlung und Methodenaufruf erschließt den Wert.
double wert = ((Double)objekt).doubleValue();
Eine Zusammenfassung des bisher Erreichten liefert zwar kaum Einblicke in das System Mathpack, schafft aber die Grundlagen für den nächsten Schritt, der sich mit der Benutzung von klassenexternen Methoden auseinandersetzt. Doch zunächst die Zusammenfassung als Testprogramm.
import mathpack.arithmetic.*;

public class test {

 public static void main( String args[]) {
 String folge  = "123.45";    // könnte eine Zahl sein
 value  zahl   = new value(); // erzeugt Wertobjekte
 Object objekt = null;        // irgendein Objekt

  System.out.print( "Die Zeichenfolge: " + folge + " kann ");
  if  (zahl.t( folge))
       System.out.print( "ohne Weiteres "); // geht ja wohl
  else System.out.print( "nicht ");         // geht nicht
  System.out.println( "gewandelt werden.");

  if (!zahl.t( folge)) System.exit(0);      // hat sowieso keinen Sinn mehr

  objekt = zahl.c( folge);                  // Instanzieren
  System.out.println( "       Das Objekt " + objekt + " wurde instanziert.");
  System.out.println( "       Objektwert " + ((Double)objekt).doubleValue());
 }
}

Damit nun nicht für jede Berechnung die umständliche Typenwandlung mit anschließendem Methodenaufruf durgeführt werden muss, ist in der Klasse value des Pakets mathpack.arithmetic noch eine Hilfsmethode enthalten. Sie übernimmt die Umwandlung in den spezifizierten Typ vor und liefert den Wert selbst.
 public static double getValue( Object obj) {
  return ((Double)obj).doubleValue();
 }
Die letzte Zeile des Beispielprogramms könnte nun vereinfacht werden zu
System.out.println( "       Objektwert " + zahl.getValue( objekt));
Der eigentliche Sinn dieser Objekthascherei liegt jedoch noch etwas tiefer. Wenn ein System mit Objekten gleich welcher Art hantieren, gleichzeitig aber Rücksicht auf spezielle Eigenschaften nehmen soll, müssen dem System die individuellen Eigenschaften extern mitgeteilt werden.
 

Die Pakete

Eine sammlung von Java-Programmen zur Bearbeitung mathematischer Gleichungen. Es umfasst die Eingabe von Funktionstermen, deren Berechnung und grafischen Darstellung. Auf konsequente Auslegung auf Lauffähigkeit in Applets gängiger Browser (sog. Vierer-Versionen) wurde geachtet. Das umfangreiche Paket ist jedoch keinesfalls nur für die simple Darstellung elementarer Funktionsverläufe ausgelegt, sondern als Einstieg in die symbolische Algebra.

Die Eingabe eines Strings der Form "4+5", kann zur Folge eine sofortige Berechnung, eine Umwandlung in die umgekehrt-polnische-Notation oder eine Repräsentation als Objekt nach sich ziehen. Hinter dieser, auf den ersten Blick wenig verheißungsvollen Aussage, steckt jedoch mehr als vermutet. Wie reagiert ein herkömmliches Programm (kein Compiler), das mit einem String wie

f(x) = 2*sin(a*x)/(cos(x)-b)

konfrontiert wird? Wahrscheinlich mit einer Fehlermeldung. Mag x noch als Variable und 2 noch als Literal erkannt werden, so treten doch bei a und b erste Probleme auf. Eine interaktive Definition dieser beiden Größen nach Übersetzung des Programms ist oft nur auf simple Wertzuweisungen beschränkt. Dafür ist jedoch die Existenz von a und b als Variablen im übersetzten Programm Voraussetzung. Mathpack geht hier einen anderen Weg.

Die Behandlung von einfachen Gleichungen ist zunächst ein interpretierender Vorgang, wie bei allen gängigen Programmen dieser Art auch. Der Unterschied liegt tiefer. Praktisch jedes Element eines Ausdrucks kann vom Benutzer selbst entworfen werden. Die Erstellung eigener Algebren, eigener Werte (wer schreibt denn vor, daß 2 eine Dezimalzahl ist?), eigener Funktionen usw. ist möglich. Jeder arithmetisch gültige (Sub)Term kann als eigenständig ausführbares Objekt benutzt werden. Folgen derartiger Objekte können so eigenständige Programme bilden und in andere Projekte integriert werden.

Das gesamte, hier vorgestellte, System umfasst drei Bereiche: mathpack, graphpack und gui. Für die einheitliche Interagtion der zwei Beispielapplets wurde noch die Klasse GridApplet beigefügt. Eine javadoc aller Klassen ist natürlich dabei. Trotzdem sollen in den folgenden Abschnitten wesentliche Teile des gesamten Pakets detailliert besprochen werden.

mathpack

Diese Paket gestattet den bereits erwähnten Entwurf eigener Arithmetiken. Detailliert auf die einzelnen Packages einzugehen würde zu weit führen, weshalb hier nur die essentiellen Punkte kurz angesprochen werden.

arithmetic

Ist praktisch die Menge aller benutzerdefinierten zulässigen Operatoren, Funktionen. In der hier beigefügten Arithmetik sind boolsche Werte den "normalen" gleichwertig. So können Ausdrücke der Form
 
(a<b)*tan(x)

berechnet werden, ohne jedesmal die H-Funktion bemühen zu müssen. Entsprechende Änderungen können vom Benutzer natürlich gemacht werden.

constants, variables

Diese beiden Klassen sind, ähnlich wie bei Java, um fast identisch. Beide benutzen eine Klasse namens namedValue. Konstanten und Variablen sind prinzipiell nur Werte die einen Namen haben. Welcher Art die Werte sind (integer, double, complex, vector) ist Sache des Benutzers. Beide verkörpern Mengen; entweder die Menge aller Konstanten, oder die aller Variablen eines Ausdrucks.

operators, functions

Auch hier handelt es sich wieder um Mengen. Allerdings sind sie der Arithmetilk untergeordnet und nicht dem Ausdruck.

grammar

Die Grammatik - es gibt bestimmt eine zutreffendere Bezeichnung - integriert die genannten Mengen. Außerdem stellt sie Methoden bereit, die feststellen, ob es sich bei dem Argument um Elemente einer bestimmten Menge handelt. So würde z.B. der Aufruf
 
isFunction( "sqrt")
den Wert "true" liefern.

expression

Ein Ausdruck umschließt und benutzt praktisch die genannten Objekte. Außerdem sind rudimentäre Syntaxprüfungen integriert. Mehrere Ausdrücke lassen sich zu Objekten verbinden, die dann als Programme lauffähig sind. Erwähnenswert ist, daß jeder Ausdruck seine eigene, unabhängige Grammatik und auch Laufzeitumgebung besitzen kann.
 

graphpack

So interessant Mathematik auf der arithmetischen Ebene auch sein mag, richtig rund wird die Sache erst wenn die Ergebnisse in grafischer Form vorliegen.