【问题标题】:How to evaluate a user input of a string as a math expression without using eval()?如何在不使用 eval() 的情况下将字符串的用户输入评估为数学表达式?
【发布时间】:2020-07-14 18:53:25
【问题描述】:

我需要制作一个程序,接受用户输入的数学表达式 (expr) 并处理找到答案。但是,我们不允许使用 eval() 函数。赋值说明我们应该假设用户将输入的唯一运算符是:+、-、*、/、%。操作数也被假定为一位整数。

我的想法是将操作数转换为整数并列出所有可以使用的运算符。然后,使用 if 语句查看运算符如何匹配我的列表。

到目前为止,我能够想出这些代码行:

operand1 = expr[1]
operator = expr[2]
operand2 = expr[3]

operators = ['+','-','*','/','%']

我这样做是为了索引输入表达式中每个操作数和运算符的位置。我被困在这里,希望有人能给我一些关于如何前进的帮助。代码的结果需要输出用户输入的表达式,以及表达式的结果。如果表达式的第二个操作数是 0 并且运算符是除法,则代码输出“无”。

【问题讨论】:

  • ast.literal_eval 可以评估文字表达式,它们是什么样的?
  • 解决此问题的典型方法是转换为后缀表示法(或仅转换为后缀堆栈),然后解决它。
  • 查看数学表达式的语法树。您可能还对operator 模块感兴趣。
  • Parse Tree here 这样的东西会起作用吗?

标签: python python-3.x string type-conversion


【解决方案1】:

您可以使用ast.parse 解析表达式。

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

然后您可以分析生成的解析树。在这个特定的例子中,你关心的是表达式的主体。

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

此对象具有感兴趣的属性:leftopright。你会看运算符

>>> 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 来处理其他操作、括号等。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 表达式。如果没有,您将需要编写自己的解析器,但是一旦有了解析树,该树的评估就会以类似的方式进行(尽管树的节点是您在解析中创建的任何内容,不一定是 @987654338 @ et al. 由ast.parse 创建的节点)。

【讨论】:

    【解决方案2】:

    如果您在此输入字符串中只有一个运算符(例如5 + 9),那么您首先列出您要计算的所有运算符 operators = ["+", "-", "*", "/", "%"]

    然后你必须遍历这个列表中的每个运算符,如果它在字符串中,继续

    # 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:
            ....
    

    如果操作符在那个表达式字符串中,首先你必须把它分成两个操作数,然后把它们都转换成浮点数

    operands = expression_string.split(operators[i])
    
    # Converting the two numbers into floats
    operands[0] = float(operands[0])
    operands[1] = float(operands[1])
    

    现在最后一步是使用 if 循环遍历您要计算的每个运算符并计算它。

    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”中

    希望对你有帮助

    编辑:你可以简单地在最后一个 if 循环下写 print(result) 来打印你的结果。如果操作数为 0,您也可以检查循环 elif operators[i] == "/",然后将 result 保存为 None

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-06-14
      • 2015-05-28
      • 1970-01-01
      • 2023-04-11
      • 2012-05-29
      • 1970-01-01
      • 2012-09-04
      • 1970-01-01
      相关资源
      最近更新 更多