Seite 1 von 1

Taschenrechner (mit strings)

Verfasst: Donnerstag 15. Oktober 2009, 22:17
von microkernel
Hallo,
ich habe folgendes Problem ich möchte gerne aus einem string (z.B.: "45 * 9 - 9 + 2.3") eine mathematische rechnung machen. Hatte es schonmal mit folgenden Code versucht, hat aber nicht wirklich funktioniertund ich glaube es geht auch wesentlich einfacher.

Code: Alles auswählen

import re


" Math functions--------------------"
def plus(int1, int2):
    return float(int1 + int2)

def minus(int1, int2):
    return float(int1 - int2)

def multiply(int1, int2):
    return float(int1 * int2)

def square(int1, int2):
    return float(int1 ** int2)

def root(int1, int2):
    return float(int1 // int2)


" Main functions--------------------"
def get_operators(string):
    global oprt_plattern
    return re.findall(oprt_plattern, string)

def get_numbers(string):
    global numbers_plattern
    return re.findall(numbers_plattern, string)

def calculate(string):
    global operators
    global tmp_result
    string_numbers = get_numbers(string)
    string_operators = get_operators(string)

    result = int(string_numbers[0])
    string_numbers.pop(string_numbers.index(string_numbers[0]))

    for None_ in range(len(string_numbers)):
        result = operators[string_operators[0]] (result, int(
            string_numbers[0]))
        string_operators.pop(string_operators.index(string_operators[0]))
        string_numbers.pop(string_numbers.index(string_numbers[0]))
    return tmp_result
            

" Vars---------------------------------"
oprt_plattern = r"[+-/*(//)(**)]{1,}"
numbers_plattern = r"\d{1,}"

operators = {
    "+" : plus,
    "-" : minus,
    "*" : multiply,
    "**" : square,
    "//" : root,
    }

Hätte da jemand von ecuh ne Idee wie man es einfacher macht?

lg
microkernel

Verfasst: Donnerstag 15. Oktober 2009, 22:31
von Dauerbaustelle
Nur kurz zum Code: Die `global`s sind unnötig. `range(len(x))` macht man lieber mit `enumerate`. Mathematische Operationen gibt es in Funktionsform in `operator`.

Gruß

Verfasst: Donnerstag 15. Oktober 2009, 23:07
von problembär
Hätte da jemand von ecuh ne Idee wie man es einfacher macht?
Nun ja:

Code: Alles auswählen

print eval("45 * 9 - 9 + 2.3")
:wink:

Verfasst: Donnerstag 15. Oktober 2009, 23:15
von ms4py
problembär hat geschrieben: Nun ja:

Code: Alles auswählen

print eval("45 * 9 - 9 + 2.3")
:wink:
Die Verwendung von eval in diesem Zusammenhang ist nicht empfohlen.
Nähere Informationen und Lösungsansätze in folgendem Thread:
http://www.python-forum.de/viewtopic.ph ... 07d5c51dfc

Verfasst: Donnerstag 15. Oktober 2009, 23:26
von EyDu
"root" und "square" sind als Namen hier nicht unbedingt passend.

Kommentare beginnt man übrigens mit einem # und benutzt keine Strings dazu.

Reguläre Ausdrücke solltest du dir besser auch noch einmal anschauen.

Verfasst: Freitag 16. Oktober 2009, 07:51
von numerix
ice2k3 hat geschrieben:Die Verwendung von eval in diesem Zusammenhang ist nicht empfohlen.
Dieser Hinweis kommt an solchen Stellen so sicher wie das Amen in der Kirche. Aber mal nüchtern betrachtet: Ist das Programm nur für den Eigenbedarf oder eine bestimmte (näher zu spezifizierende) Gruppe von Anwendern gedacht, dann geht von der Verwendung von eval() wahrhaftig keine Gefahr aus.

@microkernel: Dein Code ist ziemlich wüst. Wenn ich nichts übersehen habe, hast du z.B. die Ausführungspriorität der Operatoren gar nicht berücksichtigt.

Der saubere Weg wäre nach meinem (laienhaften) Kenntnisstand die Umwandlung der Infix-Notation in Postfix-Notation (RPN) und danach eine stackbasierte Auswertung. Zum Einlesen: http://en.wikipedia.org/wiki/Shunting-yard_algorithm

Verfasst: Freitag 16. Oktober 2009, 08:06
von ms4py
numerix hat geschrieben: Der saubere Weg wäre nach meinem (laienhaften) Kenntnisstand die Umwandlung der Infix-Notation in Postfix-Notation (RPN) und danach eine stackbasierte Auswertung. Zum Einlesen: http://en.wikipedia.org/wiki/Shunting-yard_algorithm
Und warum nicht parsen mit ast? Damit ist die Reihenfolge schon richtig...
Funktionierendes Beispiel ist in dem angegebenen Thread ja schon vorhanden.
numerix hat geschrieben: Dieser Hinweis kommt an solchen Stellen so sicher wie das Amen in der Kirche.
Ja, aber er ist auch notwendig ;) Wenn man das ``aval`` einsetzt, sollte man mit dieser Problematik auf jeden Fall vertraut sein. Wobei ich deinen Argumenten natürlich auch zustimme...

Verfasst: Freitag 16. Oktober 2009, 09:47
von Ronnie
numerix hat geschrieben:Der saubere Weg wäre nach meinem (laienhaften) Kenntnisstand die Umwandlung der Infix-Notation in Postfix-Notation (RPN) und danach eine stackbasierte Auswertung. Zum Einlesen: http://en.wikipedia.org/wiki/Shunting-yard_algorithm
Vielen Dank, den kannte ich noch gar nicht! Dort findet man (u.a.) eine Python-Implementierung: http://en.literateprograms.org/Shunting ... 8Python%29 :D
EDIT:
ice2k3 hat geschrieben:Und warum nicht parsen mit ast? Damit ist die Reihenfolge schon richtig...
Funktionierendes Beispiel ist in dem angegebenen Thread ja schon vorhanden.
Auch sehr schick! Eine wahre Goldgrube der Thread!

Verfasst: Freitag 16. Oktober 2009, 10:45
von microkernel
Vielen dank!
Hab es jetzt hinbekommen.

Verfasst: Freitag 16. Oktober 2009, 10:49
von numerix
microkernel hat geschrieben:Vielen dank! Hab es jetzt hinbekommen.
Ich schätze mit eval() ... :D

Wer seine Infix->Postfix Implementation mal testen will: http://www.spoj.pl/problems/ONP/

Wem das nicht genügt: http://www.spoj.pl/problems/MMASS/

Verfasst: Freitag 16. Oktober 2009, 14:23
von ms4py
Postfix-Generator und Taschenrechner in einem (mit ast):
http://paste.pocoo.org/show/145261/

Verfasst: Freitag 16. Oktober 2009, 15:04
von numerix
ice2k3 hat geschrieben:Postfix-Generator und Taschenrechner in einem (mit ast):
http://paste.pocoo.org/show/145261/
Sieht prima aus!

Verfasst: Samstag 17. Oktober 2009, 12:03
von microkernel
numerix hat geschrieben:
microkernel hat geschrieben:Vielen dank! Hab es jetzt hinbekommen.
Ich schätze mit eval() ... :D

Wer seine Infix->Postfix Implementation mal testen will: http://www.spoj.pl/problems/ONP/

Wem das nicht genügt: http://www.spoj.pl/problems/MMASS/
:D ja zuerst schon aber dank ice2k3's code jetzt mit dem Shunting-yard-algorythmus. Ich habe allerdings immer noch nicht so recht verstanden warum "eval()" nicht so empfehlenswert ist.

Verfasst: Samstag 17. Oktober 2009, 12:30
von sma
Ist niemand aufgefallen, dass `oprt_plattern` falsch ist? Das match nie und nimmer, was gemeint ist? Wie auch immer, hier ein klassischer rekursiv absteigender Parser:

Code: Alles auswählen

import re

operators = {
    "+": lambda a, b: a + b,
    "-": lambda a, b: a - b,
    "*": lambda a, b: a * b,
    "/": lambda a, b: a / b,
    "**": lambda a, b: a ** b,
}

class Parser(object):
    def __init__(self, s):
        def scan(s):
            for m in re.finditer(r"(\d+)|(\*\*|[-+*/()])", s):
                yield m.group()
            yield ""
        self.scanner = scan(s)
        self.token = self.scanner.next()
    
    def advance(self):
        token, self.token = self.token, self.scanner.next()
        return token
    
    def eval(self):
        r = self.eval_factor()
        while self.token in ("+", "-"):
            r = operators[self.advance()](r, self.eval_factor())
        return r
    
    def eval_factor(self):
        r = self.eval_power()
        while self.token in ("*", "/"):
            r = operators[self.advance()](r, self.eval_power())
        return r
    
    def eval_power(self):
        r = self.eval_value()
        if self.token == "**":
            r = operators[self.advance()](r, self.eval_power())
        return r
    
    def eval_value(self):
        if self.token.isdigit():
            return int(self.advance())
        if self.token == "(":
            self.advance()
            r = self.eval()
            if self.advance() != ")": raise SyntaxError
            return r
        raise SyntaxError

print Parser("(3+4*5)**2").eval()
Stefan

Verfasst: Samstag 17. Oktober 2009, 13:37
von numerix
microkernel hat geschrieben:mit dem Shunting-yard-algorythmus
Also wenn schon nicht "Algorithmus", dann wenigstens "Algo-Rhythmus" ...

Verfasst: Samstag 17. Oktober 2009, 14:33
von derdon
sma: Gefällt dir das operator-Modul etwa nicht? (add, sub, mul, (true)div, pow)

Verfasst: Sonntag 18. Oktober 2009, 11:05
von sma
Ich meide diese Extra-Imports. Sie hätten nur wenige Zeichen gespart. Mehr sparen kann man, wenn man meinen viel zu komplizierten Scanner durch einen Generatorausdruck ersetzt:

Code: Alles auswählen

        self.scanner = (m.group() for m in re.finditer(r"\d+|\*\*|[-+*/()]|", s))
Stefan

Verfasst: Sonntag 18. Oktober 2009, 17:16
von derdon
sma hat geschrieben:Ich meide diese Extra-Imports. Sie hätten nur wenige Zeichen gespart.
Darum geht es mir nicht. Ich finde, dass damit die Lesbarkeit erhöht wird. Die Länge des Quellcodes spielt eine sekundäre Rolle.