DIGITAL
Letzte Änderung: 06. July 2020, 10:50 Uhr
Übrigens: Dieses Übungsblatt enthält gelbe Felder, die Zusatzinformationen anzeigen, wenn Sie mit der Maus darüber Fahren.
Ziel dieses Übungsblattes ist es das angegebene Problem möglichst eigenständig zu lösen.
Im Rahmen dieses Aufgabenblattes werden wir ein einfaches Computeralgebra-System in vier Schritten zu programmieren:
3*(b + a*2) / (1 + sin(1-b)^2)^2
) in eine Hierarchie von Konstanten, Variablen und Funktionen. a
und b
Zahlen einsetzen und das Ergebnis berechnen). Dies ist eine Aufgabe, die ein Interpreter wie Python ständig bewältigen muss. Warum der ganze Aufwand?
Die Aufgabe soll dazu dienen noch einmal das praktisch gelernte nochmals Anwenden zu können: GUIS, statische Typisierung & objektorientiertes Programmieren, dynamic Dispatch und funktionale Ansätze werden hier in einer weiteren Softwaredesignübung vereint.
Was tun, wenn alles schief geht?
Sollten Sie Probleme beim Implementieren des Parsers haben, können Sie trotzdem den Rest des Projektes bearbeiten. Dazu können Sie die Objektbäume für die Ausdrücke direkt im Code konstruieren (man programmiert nur die Klassen zur Repräsentation von Ausdrücken, braucht aber keinen funktionierenden Parser).
Letzte Änderung: 06. July 2020, 10:50 Uhr
keine Punkte — im Detail
Im verlinkten Exkurs gibt es einen kurzen Überblick über Grammatiken für formale Sprachen und Compilerbau.
Für die Aufgabe ist es nicht unbedingt nötig, alles durchzuarbeiten — es hilft aber dabei, die Prinzipien hinter den Aufgaben besser zu verstehen.
Änderungshistorie:
^
ist potenzieren gemeint.Worum geht's?
Das ganze Aufgabenblatt beschäftigt sich damit, mathematische Ausdrücke zu manipulieren. Dazu müssen wir diese erstmal in den Rechner bringen.
Wie in der Vorlesung (zu Lehreinheit 8) erklärt, sind Ausdrücke eigentlich Bäume von Operatoren. Dies ist aber relativ schwierig direkt einzugeben (man bräuchte ein komplexes GUI, und die Interaktion wäre immer noch mühsam, wenn man sich nicht eine sehr raffinierte Schnittstelle überlegt). Daher ist es sinnvoll, mathematische Ausdrücke zunächst in linearisierter Form, als einfache Zeichenketten zu repräsentieren. Die Aufgaben, aus einer solchen, linearisierten Kodierung wieder einen Baum zu rekonstruieren, ist in der Informatik als „Parsing“ bekannt. Der Formalismus dahinter ist als kontextfreie Grammatiken/Sprachen bekannt: In jeden Operator im Baum kann man beliebig komplexe, zusammengesetzte Unteroperatoren einsetzen (Teilbäume).
Das schöne ist, dass die Definition der Sprache von der restlichen Aufgabe komplett losgelöst ist. Aus dem Grund können sie sich eine von zwei Grammatiken aussuchen:
Wählen Sie dazu eine der folgenden beiden Syntaxen aus, bevor Sie mit der Bearbeitung beginnen:
(2 + sin(42) ) * cos(x)
[1] Daher werden für die erfolgreiche Bearbeitung bis zu 40 Punkte als Bonus vergeben
Implementierung des Parsers:
Die praktische Aufgabe besteht nun darin den zuvor selbst hergeleiteten Algorithmus zu einer der oben aufgelisteten Syntaxen zu implementieren.
Anforderungen:
README.md
. 1
, 42
oder 1337
)+
, -
(unär und binär), *
, /
und ^
(potenzieren)x
, y
, a
, b
, ...)sin
, cos
, exp
und log
123.456789
sin
oder mod
erlauben, die auch aus mehr als nur einem Zeichen bestehen. +
,*
,-
), Funktionsnamen (sin
, cos
), Klammern und Variablennamen. Whitespaces (Leerzeichen, Tabs, returns) trennen die Tokens — in der Liste tauchen diese nicht mehr explizit auf. Tokens können aber auch anders getrennt sein (z.B. möchte man, dass in allgemeinen mathematischen Ausdrücken auch 3+4
, also ohne Whitespaces, drei Tokens ergibt).sin a
) als func Dummyoperator Eingabe
auffassen. Indem wir den Dummyoperator schon mit in die Operatorliste nehmen können wir sogar festlegen, wie z.B. ein Ausdruck der Form sin a\textasciicircum 3
übersetzt wird.// string containing the number
string Text = "456";
//number which will contain the result
int Result;
// stringstream used for the conversion constructed with the contents of 'Text'
// ie: the stream will start containing the characters of 'Text'
istringstream convert(Text);
//give the value to 'Result' using the characters in the stream
//if that fails set 'Result' to 0
if ( !(convert >> Result) )
Result = 0;
//'Result' now equal to 456
Anderungshistorie:
to_string
, die eine Repräsentative Zeichenkette für die (Teil-)Bäume und Objekte zurückgibt. Es muss dabei nicht exakt die gleiche Zeichenkette enstehen, wie die, die eingegeben wurde. Beispielsweise kann sich die neue bis auf Leerzeichen und Klammerungen unterscheiden. Was jedoch der Fall sein sollte ist, dass diese String-Repräsentation selbst wieder einlesbar ist. Insbesondere gilt dann für jede valide Zeichenkette Expression(Expression(str).to_string()) == Expression(str).to_string()
Expression(Expression(str).to_string()) == Expression(str).to_string()
to_string
-Funktionalität gestalten. Noch wissen die Operatorobjekte jedoch nicht, was das Operatortoken eigentlich tut. Übergeben Sie zum Beispiel der Operatordefinition zusätzlich einen Lamba-Ausdruck, der zwei Eingaben erhält und den Operator auf diese beiden anwendet. Implementieren eine Funktion deriv
, die die Ableitung einer einfachen Funktion in gegebenen Variable (z.B. \(x\)) berechnet. Als Operatoren sollen Addition, Subtraktion, Multiplikation und die Funktionen \(\sin(x),\cos(x),\exp(x),\log(x)\) unterstützt werden
Hinweise:
Zum Schluss brauchen wir unbedingt noch etwas hübsche Graphik (Eye Candy muß sein!).
Wählen Sie dazu eine der folgenden beiden GUIs aus, bevor Sie mit der Bearbeitung beginnen: