OlFi wrote:
> @Karl Heinz:
>
> Danke für den Tipp! Das wäre wirklich eine elegante Lösung, aber ich
> will im Endeffekt darauf hinaus das der Anwender eine mathematische
> Funktion mit einer Variablen (z.B. 3x+2) eingibt und das Programm diese
> zeichnet.
>
> Natürlich könnte da auch eine Funktion wie 2/3x+(-3)*2 stehen.
>
> Ich muß ausprobieren ob ich es schaffe so eine Gleichung in berechenbare
> Teilblöcke umzuwandeln, dann könnte ich deine Parse Funktion verwenden.
Den kannst du sowieso verwenden. Du musst lediglich das x noch
zur Verfügung stellen:
1 | long Faktor()
|
2 | {
|
3 | // Faktor := [ '-' ] ( Number | ( '(' Expression ')' ) | 'x' ).
|
4 |
|
5 | long Result;
|
6 | unsigned char Sign = FALSE;
|
7 |
|
8 | //
|
9 | // Schau an, schau an: Ein '-' als Vorzeichen
|
10 | //
|
11 | if( NextChar == '-' ) {
|
12 | Sign = TRUE;
|
13 | GetNextChar();
|
14 | }
|
15 |
|
16 | //
|
17 | // Fall: Eine hundsordinaere Zahl
|
18 | //
|
19 | if( NextChar >= '0' && NextChar <= '9' ) {
|
20 | Result = NextChar - '0';
|
21 | GetNextChar();
|
22 | while( NextChar >= '0' && NextChar <= '9' ) {
|
23 | Result = 10 * Result + NextChar - '0';
|
24 | GetNextChar();
|
25 | }
|
26 | }
|
27 |
|
28 | //
|
29 | // Fall: Ein Ausdruck in Klammern
|
30 | //
|
31 | else if( NextChar == '(' ) {
|
32 | GetNextChar();
|
33 | Result = Expr();
|
34 | if( NextChar != ')' ) {
|
35 | HaveError = PAREN_EXPECTED_ERROR;
|
36 | Result = 0;
|
37 | }
|
38 | else
|
39 | GetNextChar();
|
40 | }
|
41 |
|
42 | //
|
43 | // Fall: die einzige Variable, die in Funktionen erlaubt ist
|
44 | // nämlich 'x'
|
45 | // wird von der Umgebung global zur Verfügung gestellt
|
46 | //
|
47 | // Wenn mehrere Variablen möglich sein sollen, muesste
|
48 | // man hier eine Variablenverwaltung einbauen
|
49 | //
|
50 | else if( NextChar == 'x' ) {
|
51 | GetNextChar();
|
52 | Result = ValueOfX;
|
53 | }
|
54 |
|
55 | //
|
56 | // Fall: Nichst von alledem. Ein Fehler
|
57 | //
|
58 | else {
|
59 | HaveError = NUMBER_EXPECTED_ERROR;
|
60 | Result = 0;
|
61 | }
|
62 |
|
63 | if( Sign )
|
64 | return -Result;
|
65 |
|
66 | return Result;
|
67 | }
|
Du willst also einen Funktionsplotter bauen.
OK.
D.h. du bestimmst, oder lässt dir vom Benutzer die Grenzen für X geben,
sowie eine Schrittweite.
Dann gehst du in einer Schleife die X von der unteren Grenze bis
zur oberen Grenze durch, und rufst jedesmal den Formelparser auf
(der laut oben schon für die Behandlung einer Variablen 'x' vorbereitet
wurde)
1 | long ValueOfX; // Variable 'x' für den Parser
|
2 |
|
3 | int main()
|
4 | {
|
5 | char Expr[] = "( 2 + 3 ) * ( 4 / 2 )"
|
6 | ...
|
7 |
|
8 | for( x = Minimum; x < Maximum; x += Schrittweite ) {
|
9 | ValueOfX = x;
|
10 | Result = Parse( Expr );
|
11 |
|
12 | PlotPoint( x, Result );
|
13 | }
|
14 | ...
|
15 | }
|
Allerdings wäre es für einen Funktionsplotter besser, die ganzen
Berechnungen von long auf double umzustellen.
Die andere Sache ist: Klarerweise ist der Parser in der jetzigen
Form nicht optimal für einen Funktionsplotter. Es ist natürlich
eine Verschwendung von Rechenzeit, wenn die Formel jedesmal erneut
geparst werden muss, nur um sie auszuwerten.
Für einen Funktionsplotter würde man den Parser soweit umbauen,
dass er Maschinencode für eine gedachte, selbst implementierte,
fiktive CPU generiert und ein zusätzliches Programmmodul machen,
welches diese CPU implementiert und den generierten Code dann
immer wieder abarbeitet.
Aber: Soweit bist du noch nicht. Das wäre der übernächste Schritt.
Der logische nächste Schritt wäre, den Parser so wie er jetzt ist
in einen lexikalischen Scanner und den eigentlichen Parser zu trennen.
Denn: math. Standard-Funktionen stehen bereits in den Startlöchern
und ohne lexikalischen Scanner ist es eher mühsam, die Funktionsnamen
aus der Formel zu parsen. Es geht, ist aber mühsam.
1 | //
|
2 | // Fall: die einzige Variable, die in Funktionen erlaubt ist
|
3 | // nämlich 'x'
|
4 | // wird von der Umgebung global zur Verfügung gestellt
|
5 | //
|
6 | // Wenn mehrere Variablen möglich sein sollen, muesste
|
7 | // man hier eine Variablenverwaltung einbauen
|
8 | //
|
9 | else if( NextChar == 'x' ) {
|
10 | GetNextChar();
|
11 | Result = ValueOfX;
|
12 | }
|
13 |
|
14 | //
|
15 | // Fall: die Funktionen sqrt, sin
|
16 | // Also Funtkionsnamen, die mit 's' anfangen
|
17 | //
|
18 | else if( NextChar == 's' ) {
|
19 | GetNextChar();
|
20 | if( NextChar == 'q' ) {
|
21 | GetNextChar();
|
22 | if( NextChar == 'r' ) {
|
23 | GetNextChar();
|
24 | if( NextChar == 't' ) {
|
25 | GetNextChar();
|
26 |
|
27 | if( NextChar == '(' ) {
|
28 | GetNextChar();
|
29 | Result = sqrt( Expr() );
|
30 | if( NextChar != ')' ) {
|
31 | HaveError = PAREN_EXPECTED_ERROR;
|
32 | Result = 0;
|
33 | }
|
34 | else
|
35 | GetNextChar();
|
36 | }
|
37 | }
|
38 | }
|
39 | }
|
40 | }
|
41 |
|
42 | else if( NextChar == 'i' ) {
|
43 | GetNextChar();
|
44 | if( NextChar == 'n' ) {
|
45 | GetNextChar();
|
46 |
|
47 | if( NextChar == '(' ) {
|
48 | GetNextChar();
|
49 | Result = sin( Expr() );
|
50 | if( NextChar != ')' ) {
|
51 | HaveError = PAREN_EXPECTED_ERROR;
|
52 | Result = 0;
|
53 | }
|
54 | else
|
55 | GetNextChar();
|
56 | }
|
57 | }
|
58 | }
|
59 | }
|
Sinnvollerweise wird man natürlich den Abschnitt, der einen
geklammerten Ausdruck parst und berechnet, in eine eigene
Funktion herausziehen.