Seite 1 von 1
Python Taschenrechner mit Binärbäumen
Verfasst: Dienstag 8. September 2020, 07:10
von emi1023
Hi,
ich hatte versucht mit tkinter einen Tacshenrechner mit Binärbäumen zu schreiben,
Er sollte blos + und - rechnen können.
ich dachte ich hätte es nach langer Zeit endlich geschafft, jedoch meinte jetzt mein Auftraggeber, es seien keien Binärbäume.
Wie soll ich hier (Siehe unten) denn binärbäume mit rein bringen. Ich bin mit meinen ideen am ende, zumal ich dache ich hätte binärbäume.
Bitte helft mir
from tkinter import *
class Taschenrechner(Tk):
def __init__(self):
Tk.__init__(self)
self.title('Taschenrechner')
self.ende = 0
self.display = Display(self)
self.display.grid(column=0, row=0, sticky=E + W,
columnspan=6, pady=5)
tasten = [(0, 1, '7'), (1, 1, '8'), (2, 1, '9'), (3, 1, '+'),
(0, 2, '4'), (1, 2, '5'), (2, 2, '6'), (3, 2, '-'),
(0, 3, '1'), (1, 3, '2'), (2, 3, '3'),
(0, 4, '0')]
for (i, j, zeichen) in tasten:
Taste(self, zeichen).grid(column=i, row=j)
Clear(self).grid(column=5, row=1)
Gleich(self).grid(column=5, row=2)
self.mainloop()
class Taste(Button):
def __init__(self, fenster, aufschrift):
Button.__init__(self, fenster, text=aufschrift,
command=self.druecken, width=3)
self.aufschrift = aufschrift
self.fenster = fenster
def druecken(self):
d = self.fenster.display
if self.fenster.ende:
d.delete(0, len(d.get()))
self.fenster.ende = 0
d.anhaengen(self.aufschrift)
class Clear(Button):
def __init__(self, fenster):
Button.__init__(self, fenster, text='C',
command=self.loeschen, width=3)
self.display = fenster.display
def loeschen(self):
self.display.loeschen()
class Gleich(Button):
def __init__(self, fenster):
Button.__init__(self, fenster, text='=',
command=self.rechnen, width=3)
self.fenster = fenster
def rechnen(self):
ergebnis = eval(self.fenster.display.get())
ende = len(self.fenster.display.get())
self.fenster.display.insert(ende, '=' + str(ergebnis))
self.fenster.ende = 1
class Display(Entry):
def __init__(self, fenster):
Entry.__init__(self, fenster, width=20)
def anhaengen(self, zeichen):
self.insert(len(self.get()), zeichen)
def loeschen(self):
self.delete(len(self.get()) - 1)
t = Taschenrechner()
Re: Python Taschenrechner mit Binärbäumen
Verfasst: Dienstag 8. September 2020, 07:25
von __deets__
Code von hier
viewtopic.php?t=24390
Aufgabenstellung von hier
viewtopic.php?f=1&t=49571#p372567
Irgendwo hier trapst eine Nachtigall.
Re: Python Taschenrechner mit Binärbäumen
Verfasst: Dienstag 8. September 2020, 07:34
von emi1023
sorry aber ichb habe nichts mit dem oben genannten code zu tun:
Ich sehe zwar es ist anscheinend der gleiche.
Aleerdings habe ich meinen code nicht von dort.
Ich brauche hier einfach nur jemand der mir bei meinem Problem hilf, es soeinafch wie möglich zu erklären.
Wenn du also eine idee hast wie ich mein problem am besten löse gerne.
Aber bitte unterstelle mir nicht ich hätte den code von einem anderen user geklaut
Re: Python Taschenrechner mit Binärbäumen
Verfasst: Dienstag 8. September 2020, 07:43
von __deets__
Du magst den Code von woanders haben. Der entscheidende Teil, ist: es ist nicht dein Code. Dein Verständnis ist offensichtlich bei weitem nicht ausreichend, um sowas selbst zu schreiben. Und solche Ähnlichkeiten bis ins Detail nicht anders erklärbar. Versuch nicht, mich für dumm zu verkaufen.
Du kannst die Antworten, die dem anderem User gegeben wurden, durchlesen. Denn da wurden die notwendigen Lösungsstrategien besprochen. Der hat übrigens auch die gleichen ziffern am Ende wie du. Vielleicht macht ihr einen Club auf, den Club der unglaublichen Zufälle.....
Re: Python Taschenrechner mit Binärbäumen
Verfasst: Dienstag 8. September 2020, 08:20
von snafu
Der Zufall kann auch sein, dass einfach die gleiche Quelle benutzt wurde (zB ein Buch oder ein Tutorial). Der TE bestreitet ja nicht, dass der Code von woanders übernommen wurde, sondern er schreibt nur, dass "sein" Code nicht aus dem verlinkten Thread kam...

Re: Python Taschenrechner mit Binärbäumen
Verfasst: Dienstag 8. September 2020, 08:28
von __deets__
Es wird eine rührselige Geschichte langer, harter Arbeit erzählt. Statt zu sagen “ich habe diesen Code gefunden”.
Und bei der exakt gleich dämlichen Aufgabenstellung (binärbaum bei nur +/-) ist dann das Bullshit-O-Meter im Vollausschlag. Natürlich *kann* das alles Zufall sein. Und vielleicht ist die Erde eine Scheibe.
Re: Python Taschenrechner mit Binärbäumen
Verfasst: Dienstag 8. September 2020, 08:33
von snafu
__deets__ hat geschrieben: Dienstag 8. September 2020, 08:28
Es wird eine rührselige Geschichte langer, harter Arbeit erzählt. Statt zu sagen “ich habe diesen Code gefunden”.
Den Punkt sehe ich genau so. Wenn der Einstieg schon mit Halbwahrheiten beginnt (um es mal nett zu formulieren), macht man keine allzu gute Figur. Sicherlich aber auch teilweise dem Fakt geschuldet, dass sich nicht wenige Leute in ihrem Berufsleben gerne so durchmogeln und damit leider nur zu oft erfolgreich sind.

Re: Python Taschenrechner mit Binärbäumen
Verfasst: Dienstag 8. September 2020, 08:49
von __deets__
Ich bin sicher, man könnte auch mal eine Analyse zu den Rechtschreibfehlern der beiden “total nicht der gleiche User Account” emi1023 und wasgeht1023 machen, und würde erstaunliche statistische Übereinstimmung finden....
Re: Python Taschenrechner mit Binärbäumen
Verfasst: Dienstag 8. September 2020, 08:59
von Sirius3
emi1023 hat geschrieben: Dienstag 8. September 2020, 07:10
zumal ich dache ich hätte binärbäume.
An welcher Stelle genau dachtest Du, dass Du einen Binärbaum gefunden hättest?
Programmierenlernen ist frustrierend, vor allem wenn man von seinem "Auftraggeber" nicht die richtige Hilfe bekommt. Aber auch dort hilft meist freundliches Fragen.
Auch wir helfen hier gerne, dazu müssen wir aber Deinen wirklichen Stand kennen. Aus kopiertem Code kann man den Wissenstand aber nicht ableiten.
Re: Python Taschenrechner mit Binärbäumen
Verfasst: Dienstag 8. September 2020, 11:44
von __blackjack__
@emi1023: Die Aufgabe wie wir sie bisher kannten hat ja nichts mit einer GUI zu tun. Und selbst wenn tatsächlich eine GUI gefordert ist, sollte man das hier nicht vermischen, sondern erst einmal den Code schreiben, der eine Zeichenkette mit dem Ausdruck aus Zahlen und Operatoren in einen Binärbaum umwandelt und dann ausrechnet. Wenn das funktioniert, *dann* kann man da eine GUI drauf setzen falls das tatsächlich sein muss. Ansonsten würde ich es bei `input()` und `print()` für die Schnittstelle zum Benutzer belassen, und nicht mit GUI-Programmierung noch eine weitere Baustelle aufmachen.
In dem von __deets__ verlinkten Thema mit der Aufgabenstellung steht in dem verlinkten Beitrag von mir und dem davor von __deets__ sowohl etwas zu Binärbäumen als auch generell was man machen muss um das Problem anzugehen. Gibt es daran irgendwelche *konkreten* Unklarheiten? Verstehst Du da alle Begriffe? Falls nicht, hast Du recherchiert was die bedeuten? Das Internet ist ja an sich schon voll von Informationen, aber gerade bei Informatikthemen gibt es wirklich gutes, ausführliches Material. Aus allgemeinen Quellen wie Wikipedia, aber auch Seiten von vielen Unis und Schulen, wo so etwas wie Bäume grundlegender Stoff in den entsprechenden Veranstaltungen ist. Das ist kein Geheimwissen, das kann sich jeder drüber informieren.
Grundlegend muss man für diese Aufgabe Python können, und zwar inklusive Klassen, denn die sollen hier für den Binärbaum ja verwendet werden. Kannst Du das? Falls nicht, dann erst einmal die Grundlagen durcharbeiten, denn ohne die kann man nichts programmieren.
Dann musst Du Dir draufschaffen was ein Binärbaum ist. Woraus der besteht, wie man ihn grafisch darstellen kann um sich ein Bild davon machen zu können, und wie man so etwas als eigenen Datentyp implementieren kann.
Danach dann Syntaxbäume für Ausdrücke, die in diesem Fall auch Binärbäume für diesen speziellen Anwendungsfall sind. Da wirst Du dann den Zusammenhang zwischen dem Rechenausdruck als Zeichenkette und dem Binärbaum/Syntaxbaum dazu sehen. Bei der Implementierung könnte man dann beispielsweise überlegen ob es Sinn machen kann mehr als eine Klasse für die Knoten des Baums zu schreiben, je nach dem wofür so ein Knoten im Baum steht. Denn man braucht beim auswerten des Baums eventuell mindestens zwei unterschiedliche Verhalten der Knoten. Ansonsten kann man bei einer Implementierung für diese Aufgabe natürlich passendere Attributnamen wählen als für eine ganz allgemeine Binärbaumimplementierung.
Das Thema Rekursion wird man zum auswerten des Baums verstanden haben müssen. Das sollte man sich also auch vorher mal unabhängig von der konkreten Aufgabe an den üblichen Beispielen klar machen.
Wenn Dir der Zusammenhang zwischen dem Rechenausdruck und dem Syntaxbaum klar ist, kannst Du mit der Programmierung der Lösung für die Aufgabe anfangen. Grundsätzlich ist das wie bei Mathe. Man fragt sich was man an Daten hat, was man als Ergebnis haben will, und welche Werkzeuge/Formeln man kennt, mit denen man dann einen Lösungsweg beschreibt.
Die Aufgabenstellung hat einen Rechenausdruck in Form einer Zeichenkette gegeben und soll das Ergebnis dieses Ausdrucks als Zahl liefern. Randbedingung ist, dass es einen Binärbaum als Zwischenergebnis geben muss. An der Stelle kann man das Gesamtproblem dann auch schon mal in zwei Teilprobleme zerlegen. 1. Zeichenkette mit Rechenausdruck in einen Binärbaum überführen. 2. den Binärbaum auswerten. Diese beiden Teilaufgaben lassen sich unabhängig voneinander lösen und testen. Wobei sie als Schnittmenge natürlich den selbst zu schreibenden Datentyp des Binärbaums haben. Mit dem würde ich bei der Implementierung dann auch anfangen.
Den 1. Teil kann und sollte man weiter aufteilen. Das wurde ja im anderen Thema schon beschrieben. Erst in Schritt 1.1. die Zeichenkette in Token zerlegen, und dann im nächsten Schritt (1.2.) aus dieser Folge von Token den Baum aufbauen. In beiden Schritten kann es passieren das Dein Code feststellt, das die Eingabe nicht den Vorgaben entspricht. Das muss man dem Aufrufer der Funktion, welche die Zeichenkette in einen Binärbaum überführt mitteilen können. An der Stelle wirst Du Dein wissen über Ausnahmen und Ausnahmebehandlung brauchen.
Wir kennen nur *einen* Beispielausdruck und da kann man sich mindestens zwei Fragen stellen wie das denn allgemein aussehen soll um die Verarbeitung, insbesondere die Zerlegung in Token einfach zu halten. Die erste Frage ist ob in der Zeichenkette ”whitespace”-Zeichen vorkommen dürfen, also Leerzeichen, Tabs, Zeilenumbrüche, und die zweite Frage ist ob alle Zahlen nur aus einer Ziffer bestehen dürfen. Die erste Annahme würde ich persönlich wohl erst einmal treffen und eventuell am Ende noch mal schauen ob es aufwändig zu programmieren wäre diese Einschränkung aufzuheben. Bei nur einer Ziffer pro Operand würde das den Code zwar deutlich vereinfachen, aber da würde ich ziemlich sicher davon ausgehen, dass der Auftraggeber/Aufgabensteller damit unzufrieden wäre. Es wäre aber vielleicht etwas, was man als Not-/Zwischenlösung ins Auge fassen könnte. Also das erst einmal mit diesen beiden Einschränkungen umsetzen bis das Programm als ganzes läuft, und es dann durch eine allgemeinere Lösung ersetzen. Das betrifft letztlich nur das Zerlegen in Tokens (Schritt 1.1.) wo man erst eine sehr einfache Funktion schreiben kann, die man dann später durch eine etwas aufwändigere ersetzen müsste.
Den 2. Teil braucht man dann eigentlich nicht stark aufteilen, denn das sind ein paar Zeilen Code, mit dem ”Haken”, dass man dazu Rekursion verstanden haben muss. Und Polymorphie in der objektorientierten Programmierung, denn wenn man mehrere Klassen für den Baum hat entscheidet der Typ des Knotens darüber, wie das Ergebnis für diesen Knoten ausgerechnet wird.
Code von woanders kopieren ist übrigens keine gute Idee. Nicht nur das Du dabei nichts lernst, also jedenfalls nicht programmieren, kann Dein Auftraggeber auch Suchmaschinen bedienen und wird das heraus finden. Da Du den Code im ersten Beitrag anscheinend schon vom Auftraggeber begutachten lassen hast, weiss der ziemlich sicher bereits, dass der nicht von Dir war und ist vorgewarnt sich eine Lösung von Dir gut erklären zu lassen. Das heisst selbst wenn Du etwas übernimmst was man nicht so leicht finden kann, solltest Du das so gut verstanden haben, dass Du es so erklären kannst, dass man Dir abnimmt, dass Du das selbst programmiert hast. Und zwar jemandem der skeptisch ist! Und in dem Fall kannst Du das auch gleich wirklich selbst programmieren.
Wir wissen Deinen Kenntnisstand nicht, das heisst folgendes kannst Du nur selbst beurteilen: Die Aufgabe steht seit April und ich sehe irgendwie so gar keinen Fortschritt in der Bearbeitung. Die Aufgabe ist nicht ganz ohne, was aber auch zu der anscheinend recht langen Bearbeitungszeit passen würde. Solltest Du in der ganzen Zeit tatsächlich nichts substantielles gemacht haben, wäre ein ehrliches Gespräch mit dem Aufgabensteller vielleicht nicht die schlechteste Idee. Bei dem kopierten Code ohne Binärbaum aber Deiner Annahme da wäre einer, wird der sicher sowieso schon einen Eindruck von der Situation haben. Du könntest nach Hilfestellung fragen, sowohl was das herangehen/strukturieren der Arbeitsweise und Lösung angeht, als auch nach Hinweisen und Quellen zu den beteiligten Themen. Vielleicht auch Punkte gegen vorgegebene Teillösungen eintauschen oder fragen was man an der Aufgabe abspecken könnte und wie viele Punkte Dich das kosten würde, die dann nicht mehr erreichbar sind. Eventuell kannst Du dann auch (zusammen mit dem Aufgabensteller) abschätzen ob das ganze überhaupt noch Sinn macht. Das wäre eine harte Entscheidung, aber besser als sich jetzt noch sinnlos abzustrampeln ohne reale Aussicht auf einen Erfolg. Falls es so düster aussieht, könntest Du auch nach einer Verlängerung fragen, oder ob die Möglichkeit besteht jetzt oder später eine Zusatzaufgabe zu bekommen, um Punkte auszugleichen.
Re: Python Taschenrechner mit Binärbäumen
Verfasst: Freitag 11. September 2020, 15:32
von __blackjack__
Hier mal eine Umsetzung in der Programmiersprache Vala, die in der Standardbibliothek bereits eine `Scanner`-Klasse mitbringt die man für das zerlegen in Token verwenden kann:
Code: Alles auswählen
public errordomain ParseError {
UNEXPECTED
}
private static void throw_parse_error (
Scanner scanner, TokenType expected_token
)
throws ParseError
{
scanner.unexp_token (expected_token, null, null, null, null, true);
throw new ParseError.UNEXPECTED ("unexpected token");
}
public interface Evaluable : Object {
public abstract int evaluate ();
}
public class Value : Object, Evaluable {
private int value;
public Value (int value) { this.value = value; }
public Value.from_scanner (Scanner scanner) throws ParseError {
if (scanner.get_next_token () != TokenType.INT) {
throw_parse_error (scanner, TokenType.INT);
}
this ((int) scanner.value.int);
}
public int evaluate () { return this.value; }
}
public class BinaryOperation : Object, Evaluable {
delegate int BinaryFunction(int a, int b);
private BinaryFunction operation;
private Evaluable operand_a;
private Evaluable operand_b;
public BinaryOperation (
char symbol, Evaluable operand_a, Evaluable operand_b)
{
switch (symbol) {
case '+':
this.operation = (a, b) => a + b;
break;
case '-':
this.operation = (a, b) => a - b;
break;
default:
assert_not_reached ();
}
this.operand_a = operand_a;
this.operand_b = operand_b;
}
public int evaluate () {
return this.operation (
this.operand_a.evaluate (), this.operand_b.evaluate ()
);
}
}
private static char parse_operator (Scanner scanner) throws ParseError
ensures (result in '+' || result == '-')
{
var symbol = scanner.get_next_token ();
if (symbol != '+' && symbol != '-') {
throw_parse_error (scanner, TokenType.SYMBOL);
}
return symbol;
}
private static Evaluable parse_expression (string source) throws ParseError {
var scanner = new Scanner(null);
scanner.input_name = "<expression>";
scanner.input_text (source, source.length);
/*
* TODO The default config of `Scanner` skips whitespace and allows numbers
* in hex and octal notation. If that's not wanted the config needs to be
* changed here.
*/
Evaluable result = new Value.from_scanner (scanner);
while (scanner.peek_next_token () != TokenType.EOF) {
result = new BinaryOperation (
parse_operator (scanner), result, new Value.from_scanner (scanner)
);
}
return result;
}
public static int main (string[] args) {
var source = (args.length == 1) ? "5+6-4+3+9" : args[1];
try {
print ("%s = %d\n", source, parse_expression (source).evaluate ());
} catch (ParseError error) {
/*
* The error information was already reported at the source of the
* exception.
*/
return 1;
}
return 0;
}
Der `Scanner` überspringt „whitespace“ und ``# …``-Kommentare und parst ganze Zahlen auch in Hex- und Oktalnotation (mit 0x…- bzw. 0…-Präfix). Das kann man dem wegkonfigurieren falls man das nicht haben will.
In Python könnte man das `tokenize`-Modul zur Hilfe nehmen.
Oder sogar das `ast`-Modul. Auch die können beide mehr erkennen/parsen als für die Aufgabe gefordert ist. Das müsste man dann rausfiltern.
Falls man besonders ”dreist” ist, könnte man sich das schreiben eigener Klassen sparen, denn die `ast`-Klassen, die bei beim parsen der Ausdrücke entstehen, repräsentieren ja bereits einen Binärbaum.
Re: Python Taschenrechner mit Binärbäumen
Verfasst: Freitag 11. September 2020, 18:33
von darktrym
Oder man liest sich in das bekannte Tutorial
Let’s Build A Simple Interpreter ein.
Re: Python Taschenrechner mit Binärbäumen
Verfasst: Samstag 12. September 2020, 11:05
von __blackjack__
Eine etwas dreiste Lösung in Python, die `ast.parse()` aus der Standardbibliothek verwendet. Die `parse_expression()`-Funktion verwendet `ast.parse()` um einen Python-Ausdruck zu einem AST zu parsen und prüft dann ob in dem AST nur Knoten enthalten sind, die der Spezifikation der Aufgabe entsprechen. Also ganze Zahlen und + und - als binäre Operationen. Der in der Aufgabe geforderte Binärbaum besteht aus Exemplaren von Klassen aus dem `ast`-Modul.
Falls der Aufgabensteller sich daran stossen sollte, dass man keine eigenen Klassen geschrieben hat, aber nicht an `ast.parse()`, könnte man leicht zwei eigene Klassen schreiben und `validate_ast()` verwenden um die Daten aus den `ast`-Klassen in die eigenen umzuverpacken.
Die Lösung könnte zwei Funktionen weniger haben (siehe Kommentare) wenn man nur Python ≥3.8 unterstützen muss/will. Vor 3.8 gibt es `ast.Constant` das aber erst ab 3.8 so verwendet wird, ab 3.8 gibt es `ast.Num` zwar noch, aber das wird nicht mehr verwendet.
Das Programm unterstützt etwas mehr als die Aufgabe spezifiziert, weil es grundsätzlich ja Python-Ausdrücke verarbeitet. Das heisst man bekommt beispielsweise die verschiedenen Varianten wie man literale ganze Zahlen ausdrücken kann (Hexadezimal, Binär, Oktal, mit Unterstrichen als Trenner), Klammerung, und Kommentare:
Code: Alles auswählen
$ ./forum18.py 5+6-4+3+9
5+6-4+3+9 = 19
$ ./forum18.py "1_000 - (0b11 + 0x0b) - 0o15 # comment"
1_000 - (0b11 + 0x0b) - 0o15 # comment = 973
Und hier der Quelltext:
Code: Alles auswählen
#!/usr/bin/env python3
import ast
import sys
from functools import singledispatch
from operator import add, sub
AST_TYPE_TO_OPERATION = {ast.Add: add, ast.Sub: sub}
def get_type_name(value):
return type(value).__name__
class ParseError(RuntimeError):
pass
@singledispatch
def validate_ast(node):
raise ParseError(f"unsupported node type {get_type_name(node)}")
@validate_ast.register(ast.BinOp)
def validate_binary_operation(node):
validate_ast(node.left)
if type(node.op) not in AST_TYPE_TO_OPERATION:
raise ParseError(f"unsupported operator {get_type_name(node.op)}")
validate_ast(node.right)
return node
#
# TODO Remove when Python 3.7 reaches end of life.
#
@validate_ast.register(ast.Num)
def validate_number(node):
if not isinstance(node.n, int):
raise ParseError(f"unsupported number type {get_type_name(node.n)}")
return node
@validate_ast.register(ast.Constant)
def validate_constant(node):
if not isinstance(node.value, int):
raise ParseError(
f"unsupported number type {get_type_name(node.value)}"
)
return node
def parse_expression(source):
try:
return validate_ast(ast.parse(source, "<expression>", "eval").body)
except SyntaxError as error:
raise ParseError(error.msg)
@singledispatch
def evaluate_ast(node):
raise NotImplementedError(
f"evaluation of {get_type_name(node)} not implemented"
)
@evaluate_ast.register(ast.BinOp)
def evaluate_binary_operation(node):
return AST_TYPE_TO_OPERATION[type(node.op)](
evaluate_ast(node.left), evaluate_ast(node.right)
)
#
# TODO Remove when Python 3.7 reaches end of life.
#
@evaluate_ast.register(ast.Num)
def evaluate_number(node):
return node.n
@evaluate_ast.register(ast.Constant)
def evaluate_constant(node):
return node.value
def main(arguments):
source = "5+6-4+3+9" if len(arguments) <= 1 else arguments[1]
try:
tree = parse_expression(source)
except ParseError as error:
print(f"Error: {error}\n{source}")
return 1
# print(ast.dump(tree, False))
print(f"{source} = {evaluate_ast(tree)}")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))
Re: Python Taschenrechner mit Binärbäumen
Verfasst: Montag 14. September 2020, 19:12
von __blackjack__
Und noch eine Umsetzung. Diesmal in C++ mit der Qt-Bibliothek. Die Syntax für die mathematischen Ausdrücke ist so minimalistisch gefasst wie das Beispiel aus der Aufgabenstellung es zulässt: Zahlen dürfen nur aus einer einzigen Ziffer bestehen und es sind keine Leerzeichen erlaubt. Die Fehlermeldung bei Syntaxfehlern ist auch sehr minimalistisch. Da ginge mehr. Man könnte Informationen wie den Quelltext und den Index an dem das Problem aufgetreten ist mit in die Ausnahmeklasse packen.
Bei diesem Ansatz gibt es zwei Klassen mehr: die beiden Rechenoperationen haben ihre eigene Klasse bekommen die von einer `BinaryOperation`-Klasse abgeleitet sind.
main.h:
Code: Alles auswählen
#ifndef MAIN_H
#define MAIN_H
#include <QtCore>
class Evaluable : public QObject
{
Q_OBJECT
public:
virtual int evaluate() const = 0;
};
class Number : public Evaluable
{
const int value;
Q_OBJECT
public:
Number(int value) : value(value) {}
virtual int evaluate() const { return value; }
};
class BinaryOperation : public Evaluable
{
const QScopedPointer<const Evaluable> left;
const QScopedPointer<const Evaluable> right;
Q_OBJECT
public:
BinaryOperation(const Evaluable* left, const Evaluable* right)
: left(left), right(right) {}
virtual int evaluate() const {
return op(left->evaluate(), right->evaluate());
}
protected:
virtual int op(int a, int b) const = 0;
};
class Add : public BinaryOperation
{
Q_OBJECT
public:
Add(const Evaluable* left, const Evaluable* right)
: BinaryOperation(left, right) {}
protected:
virtual int op(int a, int b) const { return a + b; }
};
class Sub : public BinaryOperation
{
Q_OBJECT
public:
Sub(const Evaluable* left, const Evaluable* right)
: BinaryOperation(left, right) {}
protected:
virtual int op(int a, int b) const { return a - b; }
};
class ParseError {};
class Lexer : public QObject
{
QString source;
int index = 0;
Q_OBJECT
public:
Lexer(QString source) : source(source) {}
virtual ~Lexer() {}
bool eol() const { return index >= source.length(); }
uint parseNumber();
QChar parseOperator();
private:
QChar getNextChar();
};
#endif // MAIN_H
main.cpp:
Code: Alles auswählen
#include "main.h"
QChar Lexer::getNextChar()
{
if (eol()) throw ParseError();
return source.at(index++);
}
uint Lexer::parseNumber()
{
QChar c = getNextChar();
if (!c.isDigit()) throw ParseError();
return c.digitValue();
}
QChar Lexer::parseOperator()
{
QChar c = getNextChar();
if (!QString("+-").contains(c)) throw ParseError();
return c;
}
const Evaluable* parseExpression(QString source)
{
Lexer lexer(source);
QScopedPointer<Evaluable> result(new Number(lexer.parseNumber()));
while (!lexer.eol()) {
QChar symbol = lexer.parseOperator();
QScopedPointer<Evaluable> left(new Number(lexer.parseNumber()));
if (symbol == '+') {
result.reset(new Add(result.take(), left.take()));
} else if (symbol == '-') {
result.reset(new Sub(result.take(), left.take()));
}
}
return result.take();
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QTextStream stream(stdout);
QStringList arguments = app.arguments();
QString source = (arguments.length() <= 1) ? "5+6-4+3+9" : arguments.at(1);
try {
QScopedPointer<const Evaluable> tree(parseExpression(source));
stream << source << " = " << tree->evaluate() << endl;
} catch (ParseError) {
stream << "Syntax error in '" << source << "'." << endl;
}
return 0;
}