Как оценить ввод строки как математическое выражение без использования eval ()? - PullRequest
0 голосов
/ 14 июля 2020

Мне нужно создать программу, которая принимает пользовательский ввод (expr) математического выражения и обрабатывает найденный ответ. однако нам не разрешено использовать функцию eval (). в присваивании указано, что единственные операторы, которые, как мы должны предполагать, будет вводить пользователь: +, -, *, /,%. Предполагается, что операнды также являются целыми числами one-di git.

я думал преобразовать операнды в целые числа и составить список всех операторов, которые можно было бы использовать. затем используйте оператор if, чтобы увидеть, как оператор соответствует моему списку.

пока мне удалось придумать следующие строки кода: расположение каждого операнда и оператора во введенном выражении. Я застрял здесь и надеялся, что кто-то может мне помочь, как двигаться дальше. результат кода должен выводить выражение, введенное пользователем, а также результат выражения. если второй операнд выражения - 0, а оператор - деление, код выводит «Нет».

Ответы [ 2 ]

1 голос
/ 14 июля 2020

Вы можете проанализировать выражение с помощью ast.parse.

>>> import ast
>>> expr = ast.parse("3 + 5", mode="eval")

Затем вы можете проанализировать получившееся дерево синтаксического анализа. В данном конкретном случае вы заботитесь о теле выражения.

>>> expr
<_ast.Expression object at 0x10992c438>
>>> expr.body
<_ast.BinOp object at 0x10a926320>

Этот объект имеет интересующие атрибуты: left, op и right. Вы посмотрите на оператор

>>> expr.body.op
<_ast.Add object at 0x10a91a208>

, чтобы решить, как обрабатывать операнды.

>>> expr.body.left.n + expr.body.right.n
8

Таким образом, простая рекурсивная функция, которая может обрабатывать умножение и сложение, может выглядеть как

def evaluate_expr(expr):
    if isinstance(expr, ast.Expression):
        return evaluate_expr(expr.body)
    elif isinstance(expr, ast.Num):
        return expr.n
    elif isinstance(expr, ast.BinOp):
        op = expr.op
        left = evaluate_expr(expr.left)
        right = evaluate_expr(expr.right)
        if isinstance(op, ast.Add):
            return left + right
        elif isinstance(op, ast.Mult):
            return left * right
    raise ValueError(f"Can't evaluate {expr}")

e = ast.parse("3 + 5 * 2", mode="eval")
print(evaluate_expr(e.body))  # Outputs 13

См. Документацию к модулю ast, чтобы узнать, какие еще узлы могут отображаться в дереве, чтобы вы могли адаптировать evaluate_expr для обработки других операций, скобок и т. Д. c. ast.dump также полезен для изучения того, как анализируется выражение.

>>> ast.dump(e, annotate_fields=False)
'Expression(BinOp(Num(3), Add(), BinOp(Num(5), Mult(), Num(2))))'

Это ясно показывает, что синтаксический анализатор обрабатывает приоритет: 3 + 5 * 2 не 3 + 5 с результатом, умноженным на 2, а скорее 3 плюс результат 5 * 2 (более низкие узлы имеют более высокий приоритет, поскольку дерево оценивается снизу вверх).

Это предполагает, что ваш ввод фактически является действительным выражением Python. Если нет, вам нужно будет написать свой собственный синтаксический анализатор, но как только у вас есть дерево синтаксического анализа, оценка этого дерева происходит аналогичным образом (хотя узлы дерева - это все, что вы создаете в своем синтаксическом анализе, не обязательно ast.Bin и др. узлы, созданные ast.parse).

0 голосов
/ 14 июля 2020

Если у вас есть только один оператор в этой строке ввода (пример 5 + 9), тогда вы сначала составляете список со всеми операторами, которые хотите вычислить operators = ["+", "-", "*", "/", "%"]

Затем вам нужно go через каждый оператор в этом списке, и если он в строке, go вперед

# I'm defining the string here manually but you can do it with input()
expression_string = "5 + 9"

for i in range(0, len(operators)):
    if operators[i] in expression_string:
        ....

Если оператор находится в этой строке_выражения, сначала вам нужно разделить его на два операнда и преобразовать их оба in float

operands = expression_string.split(operators[i])

# Converting the two numbers into floats
operands[0] = float(operands[0])
operands[1] = float(operands[1])

Теперь последний шаг - это go через каждый оператор, который вы хотите вычислить с if-l oop, и вычислить его.

if operators[i] == "+":
    result = operands[0] + operands[1]

elif operators[i] == "-":
    result = operands[0] - operands[1]

elif operators[i] == "*":
    result = operands[0] * operands[1]

elif operators[i] == "/":
    result = operands[0] / operands[1]

elif operators[i] == "%":
    result = operands[0] % operands[1]

Теперь вы сохранить результат этой строки в переменной "result"

Надеюсь, это может вам помочь

EDIT: вы можете просто написать print(result) под последним if-l oop в распечатайте свой результат. Также вы можете проверить l oop elif operators[i] == "/", если операнд равен 0, а затем сохранить result как None

...