/ / Gibt es einen besseren Weg, dies zu implementieren? - Python, Implementierung, Rechner

Gibt es einen besseren Weg, dies zu implementieren? - Python, Implementierung, Rechner

Ich schreibe einen Taschenrechner in Python (als Übung), und da ist ein Stück, über das ich mich wundere.

Das Programm teilt die Eingabe in eine Liste von Zahlen und Operatoren auf. Das Ergebnis wird dann folgendermaßen berechnet:

import operator
ops = {"+" : operator.add,                      # operators and corresponding functions
"-" : operator.sub,
"*" : operator.mul,
"/" : operator.truediv,
"%" : operator.mod}
precedence = [["*", "/", "%"], ["+", "-"]]      # order of precedence for operators

def evaluate(exp):
for oplist in precedence:                     # search for operators first in order of precedence
for op in exp:                              # then from left to right
if op in oplist:
index = exp.index(op)
result = ops[op](exp[index - 1], exp[index + 1])
# compute the result of the operation
exp[index - 1:index + 2] = [result]
# replace operation and operands with result
return exp[0]

# for example,
evaluate([2, "+", 3, "+", 4, "+", 5])
# should return 14

Diese Funktion durchsucht die Liste nacharithmetische Operatoren in der Reihenfolge abnehmender Priorität und dann von links nach rechts, und wenn sie einen solchen Operator findet, ruft sie die entsprechende Funktion auf den benachbarten Listenelementen (den Operanden) auf und ersetzt den Operator und die Operanden in der Liste durch das Ergebnis der Operation . Sobald alle Operationen ausgeführt wurden, enthält die Liste ein einzelnes Element - das Ergebnis der Berechnung.

Diese Funktion verhält sich jedoch nicht soist gedacht, um. Das Problem (glaube ich) ist, dass diese Funktion die Liste ändert (indem sie Slices zuweist), während sie darüber iteriert. Ich habe bereits eine Lösung für dieses Problem gefunden Hier (durch Neustart des inneren for Schleife, jedes Mal, wenn die Liste geändert wird), aber die Leute, die die Lösung gaben, schienen zu denken, dass es normalerweise einen besseren Weg geben sollte, um das zu erreichen, was auch immer benötigt wird.

Ich habe mich gefragt, ob es einen besseren Weg gibt, diesen Algorithmus zu implementieren, der das seltsame "Neustart der Schleife" -Ding vermeidet.

Danke für irgendwelche Ideen!

Antworten:

3 für die Antwort № 1

Ich denke, ich würde es anders machen und eine rekursive Funktion verwenden. Pop-off-Operationen und ersetzen sie durch das Ergebnis ihrer Auswertung.

import operator

ops = {
"+" : operator.add,
"-" : operator.sub,
"*" : operator.mul,
"/" : operator.truediv,
"%" : operator.mod,
}

precedence = [
set(["*", "/", "%"]),
set(["+", "-"]),
]

def evaluate(expr):
# if len == 3 then just return result of expression
if len(expr) == 3:
l, op, r = expr
return ops[op](l, r)
else:
for op_list in precedence:
for op in expr:
if op in op_list:
# find index of first operation
idx = expr.index(op)-1
# pop off and evaluate first matching operation in expr
result = evaluate([expr.pop(idx) for i in range(3)])
# insert result back into expr
expr.insert(idx, result)
return evaluate(expr)

3 für die Antwort № 2

Sie können den Index manipulieren, wenn Sie a verwenden while Schleife stattdessen

def evaluate(exp):
for oplist in precedence:  # search for operators first in order of precedence
idx = 0
while idx < len(exp):
op = exp[idx]
if op in oplist:
result = ops[op](exp[idx - 1], exp[idx + 1])
exp[idx - 1:idx + 2] = [result]
idx -= 1       # move index back since list is shortened by 2
else:
idx += 1
return exp[0]

2 für die Antwort № 3

Ich bin mir nicht sicher, was du damit meinst, indem du denloop "thingy. In diesem speziellen Fall scheint es mir, dass Sie einfach eine Funktion wiederholt auf den Ausdruck anwenden, bis es auf einen Wert reduziert wurde. Dies ist weniger effizient als es sein könnte, aber es funktioniert und ist klar. So ...

def find_op(tokens, oplist):
for i, token in enumerate(tokens):
if token in oplist:
return i
else:
return -1

def reduce_binary_infix(tokens, i, ops):
op = ops[tokens[i]]
tokens[i - 1: i + 2] = [op(tokens[i - 1], tokens[i + 1])]
return tokens

def evaluate(tokens, ops, precedence):
for prec in precedence:
index = find_op(tokens, prec)
while index >= 0:
tokens = reduce_binary_infix(tokens, index, ops)
index = find_op(tokens, prec)
return tokens

print evaluate([2, "+", 3, "+", 4, "+", 5], ops, precedence)

Geprüft:

>>> print evaluate([2, "+", 3, "+", 4, "+", 5], ops, precedence)
[14]

Dies könnte effizienter gemacht werden, indem nicht wiederholt durch die ganze Saite gesucht wird. Das könnte getan werden, indem man a start_index Parameter in find_opund durch haben reduce_binary_infix gibt einen neuen aktuellen Index zusammen mit der reduzierten Liste zurück.

Dies ist auch ein wenig ausführlicher als das, was Sie haben, aber ich denke, es hilft der Lesbarkeit des Codes - ganz zu schweigen von seiner Wiederverwendbarkeit.