Programm zum lösen von Funktionsgleichungen



  • Ich schreibe gerade an einem Programm, welches mit Funktionsgleichungen rechnen soll (Nullstellen, Wertetabelle, Rechenweg und Zeichnung), als Beilage für meine Bewerbung für ein Praktikum 🙂

    Mit Linearen Funktionen klaptt das ganze schon (nur, dass die Konstante und die Steigung noch nicht als Brüche angegeben werden können -kommt noch-).

    Ich wollte mal nett nach Feedback fragen, da ich mir besonders bei den Klassen unsicher bin.

    cTerms.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    
    namespace Funktionen {
        class cTerms {       
            private string rightTerm = "0";
            private string leftTerm = "";
    
            private cTerms obj;
    
            protected List<string> explanation = new List<string>();    //Rechenweg
    
            public List<string> _explanation { get { return this.explanation; }}
    
            public cTerms() {
    
            }
    
            public cTerms(string leftTerm) {
                this.leftTerm = leftTerm;
            }
    
            //Art des Terms herausfinden
            private void CheckTerm() {
                if (Regex.IsMatch(this.leftTerm, cLineareFuktion.functPattern))
                    obj = new cLineareFuktion(this.leftTerm);
            }
    
            //Nullstellen
            virtual public string GetToZero() {
                string toZero = obj.GetToZero();
    
                this.explanation = obj.explanation;
                return toZero;
            }
    
            //Wertetabelle
            virtual public List<string> GetLookUpTable(int start, int end, int step) {
                return obj.GetLookUpTable(start, end, step);
            }
    
            protected static double ToSum(double zahl1, double zahl2) {
                return (zahl1 + zahl2);
            }
    
            protected static double ToSubtract(double zahl1, double zahl2) {
                return (zahl1 - zahl2);
            }
    
            protected static double ToMultiply(double zahl1, double zahl2) {
                return (zahl1 * zahl2);
            }
    
            protected static double ToDivide(double zahl1, double zahl2) {
                return (zahl1 / zahl2);
            }
        }
    }
    

    cLineareFunktion.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    
    namespace Funktionen {
        class cLineareFuktion: cTerms {
            public static string functPattern = "^(?<m>[+-]{0,1}[0-9]{0,2})(?<v>[a-z])(?<b>[+-][0-9]{0,3}){0,1}$";
    
            private string leftTerm = "";
            private string rightTerm = "0";
    
            private double gradient;    //Steigung
            private string variable;    //Variable (z.B x)
            private double absolutTerm; //Konstante
    
            public cLineareFuktion(string leftTerm) {
                this.leftTerm = leftTerm;
    
                InitValues();
            }
    
            //Werte aus dem String auslesen und merken
            private void InitValues() {
                Match match = Regex.Match(leftTerm, functPattern);
    
                if (match.Groups["m"].Value != "")
                    gradient = Double.Parse(match.Groups["m"].Value);
                else
                    gradient = 1f;
    
                variable = match.Groups["v"].ToString();
    
                if (match.Groups["b"].Value != "") {
                    absolutTerm = Double.Parse(match.Groups["b"].Value);
                }
                else {
                    absolutTerm = 0f;
                }
            }
    
            //Nullstelle errechnen
            override public string GetToZero() {
                base.explanation.Add("f(x)=" + leftTerm);
                base.explanation.Add("n.B. f(x)=0");
    
                //Konstante auf die rechte Seite der Gleichung holen
                if(absolutTerm > 0f) {
                    base.explanation.Add("<=> " + leftTerm + "=" + rightTerm + string.Format(" |+{0}", absolutTerm));
                        rightTerm = cTerms.ToSum(Double.Parse(rightTerm), absolutTerm).ToString();
                        absolutTerm = cTerms.ToSum(absolutTerm, absolutTerm);
                }
                if(absolutTerm < 0f){
                    base.explanation.Add("<=> " + leftTerm + "=" + rightTerm + string.Format(" |-{0}", absolutTerm));
                        rightTerm = cTerms.ToSubtract(Double.Parse(rightTerm), absolutTerm).ToString();
                        absolutTerm = cTerms.ToSubtract(absolutTerm, absolutTerm);
                }
    
                //Gesamte Gleichung durch die Steigung teilen
                base.explanation.Add("<=> " + gradient + variable + "=" + rightTerm + string.Format(" |/({0})", gradient));
                rightTerm = ToDivide(Double.Parse(rightTerm), gradient).ToString();
                gradient = 1f;
                base.explanation.Add("<=> " + gradient + variable + "=" + rightTerm);
    
                return string.Format("N({0}/0)", rightTerm);
            }
    
            //Vorgegebenen Wertebereich für x einsetzen und Wertetabelle erstellen
            override public List<string> GetLookUpTable(int start, int end, int step) {
                List<string> funktion = new List<string>();
    
                for (int xWert = start; xWert <= end; xWert += step)
                    funktion.Add(string.Format("f({0})=", xWert) + ((gradient * xWert) + absolutTerm).ToString());
                return funktion;
            }
        }
    }
    

    Gruß RisH



  • Also zum einen, wem willst du das zeigen? An der Uni/FH interessiert man sich für sowas überhaupt nicht. Da hab ich schon von zig Leuten gehört, die auch sehr große/komplexe Programme ihrer Bewerbung oder an FH beigelegt haben und das hat sich keiner angeschaut und wollte auch gleich von Anfang an auch keiner haben. Kann ich auch gut verstehen. Du interessierst da keinen. Die Profs kennen die Studenten ganz selten persönlich, außer die Leute, die sie bei der Diplomarbeit oder so betreuen, und die interessiert estmal überhaupt nicht, was irgendjemand vor dem Studium geschrieben hat. Und sowas schon gar nicht, ich kann dir gleich sagen, das kann überhaupt niemanden interessieren. Und die Sekretärin, die deinen Antrag bearbeitet, interessiert es schon mal gar nicht.
    Und wenn du dich bei einer Firma bewirbst, wird es sie wahrscheinlich auch nicht interessieren. Du schickst denen 1-200 Zeilen Code hin, und? Die werden beim Vorstellungsgespräch höchstens eigene Aufgaben stellen, so ein Codeschnippsel interessiert keine Sau.
    Ich will dich jetzt nicht entmutigen, aber ich wollts mal gesagt haben.

    So, jetzt zu deinem Code. Weiß jetzt gar nicht so genau, was das überhaupt machen soll. Das scheint jetzt nicht viel Sinn zu ergeben 😉 Ich hätte jetzt aus der Beschreibung ein Programm erwartet, das Funktionen symbolisch/nummerisch auswerten kann. Wo ich z.b. "tan(x^3) + 5x^2" eingeben kann, und das mir dann die Ableitung als "String" und die ganzen Extremwerte usw. als Zahlen ausgibt. Das kann dein Programm schon mal nicht. Ich seh überhaupt keinen Parser, außer Double.Parse und Regex, das kanns ja nicht sein 😉 Was dann? Versteh ich ehrlich gesagt nicht ganz.
    So, den Code an sich (was auch immer er macht) find ich jetzt nicht sooo übel. Es ist zumindest halbwegs sauber. ABER. protected static ist natürlich ganz übel. Überhaupt solltest du kein static brauchen/benutzen.
    Dann hantierst du irgendwie zu viel mit Strings... Das sollte jetzt nicht dein grundlegender Datentyp sein, dafür sollte man eher einen AST definieren. Und deine Bezeichnungen finde ich verbesserungswürdig. "cTerms" ist kein guter Name für eine Klasse, nenn sie einfach Terms. Klassennamen sollten mit einem Großbuchstaben beginnen, und auf ein c kann man echt gut verzichten. Oder hast du sowas schon mal irgendwie im .NET Framework gesehen? Warum TMultiply usw? Warum GetToZero? Versteh die Bezeichnungen nicht...

    So, aber wenn du erklärst, was das ganze eigentlich soll, kann ich meine Meinung vielleicht noch revidieren 😉



  • Das Programm wollte ich einer Bewerbung, für ein Jahrespraktikum beilegen, da ich schon einige Betriebe angerufen habe und manche sogar um Referenzen, Beispiele etc gebeten haben.

    Also beschreibe ich kurz den Ablauf:
    Man gibt einen Funktionsterm(im Moment nur eine einfache lineare Funktion) in eine Textbox ein und klickt einen Button. Ein neues Objekt Terms wird erstellt und der FunktionsTerm übergeben. Vom Konstruktor wird CheckTerm() aufgerufen. CheckTerm() soll prüfen, um was für eine Funktion es sich handelt, indem es den Aufbau des Terms mit der statischen String-Variable (in LineareFunktion -später kommen noch mehr Klassen dazu, für jede verschieden Funktion eine) functPattern vergleicht.
    Ist das der Fall wird ein Objekt von LineareFunktion erstellt, welchem auch der Funktionsterm übergeben wird. Jetzt wird (in der Funktion für das Button_Click Event) GetToZero() aufgerufen, welche die Nullstelle zurück gibt. Danach GetLookUpTable(), welche eine Wertetabelle erstellt (anhand eines, vom User, festgelegten Bereiches - z.B start -20 ende 50 schritte 1-) und diese als Liste zurück gibt. explaination ist einfach nur eine Liste mit Strings, in der ich den Rechenweg dokumentiert habe, welcher auch ausgegeben werden soll.

    Warum ich ein To vor Add, Multiplay usw. gesetzt habe, frage ich mich jetzt gerade auch und auch, warum ich zum Addieren, Multiplizieren usw. überhaupt extra Methoden erstellt habe, kann ich nicht beantworten. Fällt mir auch gerade erst auf, dass das total Sinnlos ist. Dass ich static nicht nutzen sollte, wusste ich garnicht ich dachte eher, dass es eine feine Sache sei, wenn eine Variable eh immer den gleichen Wert hat, in jedem Objekt. Aber gut, das static streich ich dann erstmal.

    So, ich hoffe ich konnte das verständlich rüber bringen 🙂
    Und danke für die Antwort



  • Was ist ein Jahrespraktikum? Bist du noch Schüler und musst ein Praktikum machen, oder machst du eine Ausbildung? Ich kann den Begriff grad nicht zuordnen...

    Zuerst wegen static. Das sollte man nur verwenden, wenn man das unbedingt braucht. Und wenn man meint das zu brauchen, gibt es meist immer noch eine bessere Lösung, auf die man einfach nicht kommt 😉 Man versucht normalerweise Abhängigkeiten durch lose Kopplung und "Programmieren gegen Schnittstellen" zu vermeiden. Direkte Abhängigkeiten sollte man nach Möglichkeit immer vermeiden, die wirken sich irgendwann immer negativ aus, wenn man sein Programm warten/erweitern will. Also, nach Möglichkeit static vermeiden.
    Wobei ich hier jetzt nichts gesagt hätte, wenn das einfach nur static wäre. Du brauchst keine Klasseninstanzen und hast ein paar (sinnlose) Hilfsfunktionen. Ok, damit kann man leben, vor allem bei der Programmgröße. Aber protected static deutet ganz klar auf einen Designfehler hin. Das macht schon mal gar keinen Sinn.

    Ok, also eine einfache lineare Funktion wäre z.B. 2x+3? Ich muss dir leider sagen, dass dein Ansatz nicht wirklich funktionieren wird. Da kannst du jetzt aber nichts dafür, ich vermute, du bist einfach noch nicht so weit. Du kannst mit regulären Ausdrücken nur reguläre Sprachen parsen und ein Funktionsterm ist keine reguläre Sprache. Es mag jetzt so ein bisschen funktionieren, aber es wird nie mit beliebigen Funktionen funktionieren. Nicht mal mit beliebigen linearen Funktionen. Und deine Regex für Zahlen ist sehr einfach, aber das ist jetzt nebensächlich...
    Du müsstest also eine Grammatik definieren und einen richtigen Parser schreiben. Ich weiß nicht, inwiefern dich das jetzt schon interessiert, das ist durchaus auch ohne Studium schaffbar, aber nicht ganz trivial und da gibts auch einiges an Theorie dazu, die man zwar vielleicht nicht unbedingt 100% verstehen muss, aber schaden tuts sicher auch nicht.
    Der Ansatz zum Berechnen der Funktionswerte/Nullstellen ist natürlich auch etwas zu "direkt". Das funktioniert so wie es funktioniert, ist aber nicht flexibel. Hier bietet sich ein entsprechender Abstrakter Syntaxbaum an. Das könnte so ähnlich ausschauen, wie in deiner Klassenhierarchie, z.B. eine Basisklasse Term mit einer double evaluate Methode (oder besser vielleicht einen Visitor akzeptieren), und verschiedenen Ableitungen, z.B. BinaryOperationTerm, der entsprechend drei Parameter für zwei Operanden und den Operator hat, oder ein FunctionTerm. Dein Parser überführt dann den String in den abstrakten Syntaxbaum und damit kannst du dann beliebige Funktionen verarbeiten, die deine Grammatik zulässt, beliebig verschachtelt, mit Klammern, Funktionen (z.B. trigonometrischen Funktionen) usw. Auf der Basis könnte man dann auch andere Funktionen implementieren, z.B. die Ableitung einer Funktion bestimmen.
    Das wäre der "richtige" Ansatz hier. "Naive" Ansätze führen hier auf Dauer zu nichts. Aber es wäre halt einiges an Aufwand, das richtig zu machen, keine Ahnung, obs dich soweit interessiert und obs dir das wert ist.

    Lies dir z.B. diesen Artikel durch, der machts richtig 😉

    http://www.codeproject.com/Articles/18880/State-of-the-Art-Expression-Evaluation



  • Übrigens, 2x+3, was ich als Beispiel genannt habe, ist keine lineare Funktion. Ich hab mich da an deiner Regex orientiert (ich hoff, ich hab die richtig verstanden und unterstell dir da nichts falsches). Das wäre eine affine Abbildung, oder bezeichnet man das in der Schule als lineare Funktion? Ich weiß es nicht mehr, aber es kann ja keine lineare Funktion sein...



  • Ich mache das Abitur an der Abendschule. Ich habe also den ganzen Morgen und Nachmittag frei. Da ich erstens nicht den ganzen Tag abgammeln will und zweitens etwas machen möchte, was mir auch Spaß macht suche ich ein Praktikumsplatz für ein Jahr. Kommt meinem Lebenslauf ja auch zu gute.

    Ok, dann lösch ich das Projekt mal lieber, versuche das Programm zu verstehen, von dem du mir den Link gepostet hast, und fange dann nochmal von vorn an. So lange bewerbe ich mich dann halt so weiter.

    Ja, ich habe extra noch mal nachgeschlagen und in meinen Notizen ist genau solch ein Term eine lineare Funktionsgleichung (f(x)=2x+3 - im Prog wird das f(x) nur nicht gebraucht, für die Eingabe).

    Ist nicht das erste Mal, dass ich mich übernehme 😉 Es ist irgendwie schwer was Anspruchsvolleres zu programmieren, wenn man nicht mal ein Buch komplett durch hat. Hatte auch an Delegates und Lambda gedacht, aber habe da noch nicht so ganz durchgeblickt.

    Ich danke dir für deine Zeit
    Wenn du sonst noch eine Idee hast, für ein kleines Projekt, welches für mich machbar sein sollte, würde mich das sehr interessieren



  • RisH: Du kannst dir auch mal diesen Artikel hier durchlesen: http://www.c-plusplus.net/forum/268247
    Der befasst sich u.a. mit dem Parserbau, welches auch für dein Projekt eine essentielle Angelegenheit ist.

    Mechanics schrieb:

    Überhaupt solltest du kein static brauchen/benutzen.

    Einspruch 😉 static hat durchaus seine berechtigten Anwendungszwecke. Ich würde es als Methodenmodifizierer (ja, man modifiziert eig. die Speicherklasse der Methode) sogar viel öfters verwenden - nämlich immer dann, wenn auf kein normales Member einer Klasse zugegriffen wird. Jedoch ist es dann optisch etwas scheiße, dass man static-Methoden nur über den Klassennamen aufrufen kann und nicht auch über einen Instanznamen wie in C++. Das ist zwar semantisch klarer, da immer offensichtlich ist, ob man gerade eine Instanzmethode oder eine Klassenmethode aufruft, aber ich bin trotzdem nicht ganz zufrieden damit.



  • GPC schrieb:

    static hat durchaus seine berechtigten Anwendungszwecke.

    Ja, hat es.

    GPC schrieb:

    Ich würde es als Methodenmodifizierer (ja, man modifiziert eig. die Speicherklasse der Methode) sogar viel öfters verwenden - nämlich immer dann, wenn auf kein normales Member einer Klasse zugegriffen wird.

    Das find ich aber sehr, sehr suboptimal. Das führt einzig und allein zu unwartbarem Code. Hab ich schon zig mal erlebt. Man benutzt oft irgendwie static, weil man meint, das ist nur eine Hilfsfunktion, braucht keine Membervariablen. Und dann nach paar Jahren ist das Programm viel größer geworden, sind etliche Anforderungen dazu gekommen, und man würde gern die "Hilfsfunktion" abhängig vom Kontext austauschen (oder man will einen Proxy haben, einen Cache, oder was weiß ich was), und siehe da, es geht nicht, ohne den halben Code umzubauen. Seh ich jeden Tag in der Arbeit.


Log in to reply