Laden...

Design: abstrakte Operatoren

Erstellt von ANSI_code vor 15 Jahren Letzter Beitrag vor 15 Jahren 2.856 Views
ANSI_code Themenstarter:in
467 Beiträge seit 2007
vor 15 Jahren
Design: abstrakte Operatoren

angenommen, man hat vor(habe ich vor, mal sehen ob´s klappt) eine Biliothek zu schreiben, Die Mathematische Ausdrücke speichern, und mit ihnen rechnen soll.
Ich habe erstmal eine Abstrakte Basisklasse Term - daran ist nichts auszusetzten, oder? Davon leite ich später Klassen, wie Summ, Fraction, Root etc... die wiederum Instanzen der Klasse Term handhaben.
Meine Frage: Ich schreibe gerade die Klassendefinition, und führe abstrakte Operatoren ein. Beispiel :

public abstract Term operator+ (Term lhs, Term rhs);

so sieht meine Definition aus. Ist sie sinnvoll/richtig? Eigentlich soll ja auch jede Klasse, die von Term ableitet ihre eigene Logik definieren, wie sie sich mit anderen Termen Addieren lässt, aber es soll, je nach Summanden der Operator der endsprechenden Klasse aufgerufen werden, was ja mit den Parametern vom Typ erm schwierig wird, oder? Wenn eine Summe mit einer Summe addiert wird, muss man, um der Ausdruck möglichst weit zu vereinfachtn(ich gedenke Variablen etc zu erlauben) andere Logik anwenden, als wenn der Summand eine Wurzel darstellt.
Außerdem möchte ich nicht in der Implementation alles mit "if(typeof(rhs)==whatever)"
vollschreiben müssen. Wäre auch nicht das richtige, oder?
Vielleicht sollte nur die Klasse Sum wissen, wie man andere Objekte miteinander Addiert - dann müsste diese nach allen Typen differenzieren, bzw. den Operator für alle Typen überladen.
Kann man das vermeiden? Meine Lösung wird wahrscheinlich nicht gehen, oder?
Wäre sehr dankbar für ein paar Vorschläge, wie man das implementieren könnte.

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo ANSI_code,

eigentlich brauchst du nur die Klassen Term, Operrator und Literal und vielleicht noch Variable. Jedenfalls brauchst du keine Unterklassen für Summ, Fraction, Root etc. und einen Term zu repräsentieren. Was die Berechnung angeht, könnte es dagegen praktisch oder nötig sein, solche Unterklassen zu haben. Die müssen dann aber nicht viel mehr tun, als eine abstrakte Methode Berechne zu überschreiben.

Ist sie sinnvoll/richtig?

Ja, ja, die Frage nach dem Sinn. 😃
Nein, ich halte das nicht für sinnvoll. Zum einen bist du dann auf die Operatorsymbole festgelegt, die von C# unterstützt werden und zum anderen musst du dann alles hartcodieren. Mach eher eine Klasse Operator mit einer String-Property Symbol oder Name.

BTW: In C# sind Operatoren statische Methoden und können daher nicht abstrakt sen.

herbivore

0
767 Beiträge seit 2005
vor 15 Jahren

um einen abstrakten + operator zu machen könntest du allerdings eine abstrakte methode Add() machen, und diese im public static operator+ aufrufen.

loop:
btst #6,$bfe001
bne.s loop
rts

C
980 Beiträge seit 2003
vor 15 Jahren

Die Addition mit den Operatoren würde ich jedenfalls in der Term-Klasse definieren, das hat sich bewährt. Aber sie muss gar nicht abstrakt sein, denn das einzige was sie machen wird ist eine neue Instanz deiner Add-Klasse erstellen und zurückgeben, also

Term1 + Term2 => Sum(Term1,Term2)

Außerdem möchte ich nicht in der Implementation alles mit "if(typeof(rhs)==whatever)"
vollschreiben müssen. Wäre auch nicht das richtige, oder?

Wenn du die Term Klasse mit abstrakten oder virtuellen Methoden wie Calculate, Expand, Simplify, Derive, etc. erweiterst und die entsprechenden Klassen wie Sum das selber machen lässt brauchst du erfahrungsgemäss nur sehr wenige solche Abfragen.

Vielleicht sollte nur die Klasse Sum wissen, wie man andere Objekte miteinander Addiert - dann müsste diese nach allen Typen differenzieren, bzw. den Operator für alle Typen überladen.

Ja, nur Sum sollte wissen wie addieren funktioniert, aber das heisst nicht dass Term nicht wissen darf dass es eine Addition gibt.

Tip: beschränke dich auf nur wenige skalare Datentypen (z.b. double), macht alles sehr viel einfacher.

Habe in C# übrigens schon diverse kleine computer algebra systeme geschrieben, der neuste Ansatz arbeitet direkt mit rohen Linq Expressions...

ANSI_code Themenstarter:in
467 Beiträge seit 2007
vor 15 Jahren

danke für die Hilfe. Den Link von cdr werde ich mir mal anschauen.

C
980 Beiträge seit 2003
vor 15 Jahren

(PS: Der Palladium Prototyp (der Link von oben) ist speziell aufgebaut weil er direkt mit Linq Expressions arbeitet und die ganze Baumstruktur entsprechend nicht beeinflussen kann/will. Ist also wahrscheinlich eine komplett andere Architektur als bei dir und kaum übertragbar...)

ANSI_code Themenstarter:in
467 Beiträge seit 2007
vor 15 Jahren

war mir durchaus klar, trotzdem danke für den Hinweis.
Dann hoffe ich einfach mal, ich werde auch fertig. Bisher habe ich erst alle Klassen angelegt. Mit der Logik werde ich bestimmt noch eine Weile garnichts tu tun haben.

kann mir mal jemand erklären, warum mein Beitrag viel mehr Platz wegnimmt?

F
10.010 Beiträge seit 2004
vor 15 Jahren

Bild!?

1.002 Beiträge seit 2007
vor 15 Jahren

Hallo herbivore,

Jedenfalls brauchst du keine Unterklassen für Summ, Fraction, Root etc. und einen Term zu repräsentieren.

Wie würdest du denn z.B. den Term WURZEL(x - 1) repräsentieren? Wäre es nicht praktisch, wenn es eine Klasse für die Wurzelfunktion gibt, die ihrerseits wieder einen Term als Radikanten enthalten kann?
Wie stellst du Brüche dar, sodass mit ihnen bequem gerechnet werden kann, wie Potenzen? Was ist mit komplexeren Gebilden wie Vektoren und Formen / Körper wie Dreiecken / Quadern, ...?

m0rius

Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo m0rius,

ich komme vom symbolischen Rechnen her. Daher habe ich da vielleicht eine zu spezifische Sichtweise (eingenommen). Wenn man tatsächlich arithmetisch rechnen will, muss man die die Operationen natürlich irgendwo implementieren.

herbivore

1.002 Beiträge seit 2007
vor 15 Jahren

Hallo herbivore,

aber jetzt nochmal rein programmier- bzw. designtechnisch gesehen: Prinzipiell ist doch nichts gegen eine Klasse Fraction mit Eigenschaften wie Numerator & Denominator und Methoden wie Add(Fraction fraction), Multiply(Fraction fraction) und Reduce() einzuwenden, oder?

m0rius

Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo m0rius,

es sind auf jeden Fall zwei ganz unterschiedliche Paar Schuh, ob es eine Klasse für Brüche oder eine Klasse für die Wurzelfunktion gibt. Bruch ist auch im Sinne der Objektorientierung eine super Klasse. Bei Wurzelfunktion muss man schon eine sehr abstrakte Sicht einnehmen, um das als annehmbare Klasse zu sehen.

herbivore

1.002 Beiträge seit 2007
vor 15 Jahren

Hallo herbivore,

wo liegt denn hinsichtlich der "Klassenqualität" der Unterschied? Beide Klassen haben verschiedene Eigenschaften - ob diese nun "Nenner" oder "Radikant" heißen ist relativ egal - und Methoden. Okay, vll mag ein Bruch in er OOP mehr Methoden als eine Wurzel haben, aber ist denn ein Bruch nicht (zumindest fast) ebenso abstrakt wie eine Wurzel?

m0rius

Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo m0rius,

Beide Klassen haben verschiedene Eigenschaften

ich sagte ja, dass wenn man eine sehr abstrakte Sichtweise einnimmt, auch die Wuzelfunktionsklasse annehmbar wird. Wenn man die die Semantik ins Spiel bringt, sieht es doch etwas differenzierter aus.

Da repräsentiert ein Bruch Daten. Samt den zugehörigen Operationen natürlich. Eine Klasse hat immer Zustand (Daten) und Verhalten (Operationen). Aber wenn man überlegt, welche Klassen es gibt, dann schaut man nur nach den Daten. Objektorientierung bedeutet auch Datenzentrierung, im Gegensatz zur prozeduralen Programmierung, der eine Operations-/Funktionszentrierung zu Grunde liegt.

Bruch repräsentiert also Daten, konkreter: rationalen Zahlen. Ok, ganz abstrakt gesehen steckt da eine Divisionsoperation hinter, aber im Grunde sind wir uns doch einig, dass Brüche einfach eine bestimmt Menge von Zahlen repräsentieren, eben die rationalen Zahlen.

Im Gegensatz dazu steht nun eine nach einer Funktion benannte und eine Funktion repräsentierende Klasse Wurzelfunktion. Und das ist vom Grundsatz her nicht erwünscht. Wenn man überlegt, welche Klassen es gibt, sollen man nicht nach den Funktionen schauen.

Natürlich ist die Problemdomäne hier die Repräsentation von Termen, da kann es dann schon sein, dass man die abstrakte Sichtweise einnehmen muss. Ich wollte ja auch nur das Problembewusstsein schärfen.

herbivore

1.002 Beiträge seit 2007
vor 15 Jahren

Hallo herbivore,

[...], aber im Grunde sind wir uns doch einig, dass Brüche einfach eine bestimmt Menge von Zahlen repräsentieren, eben die rationalen Zahlen.

Okay, dieses Argument überzeugt.

Im Gegensatz dazu steht nun eine nach einer Funktion benannte und eine Funktion repräsentierende Klasse Wurzelfunktion.

Da leuchtet es ein, dass eben das nicht erwünscht ist.

Ich wollte ja auch nur das Problembewusstsein schärfen.

... was dir gelungen ist.

m0rius

Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg