【问题标题】:Parsing program input string for math expression为数学表达式解析程序输入字符串
【发布时间】:2016-12-05 02:36:42
【问题描述】:

我正在尝试编写 worlfram alpha 之类的数学表达式求解器。 我目前的障碍是找出对应的左括号和右括号。

例如,我如何确定((1 * 3) / (4 / 2)) 中的哪些括号是匹配的。该程序将单独解决每个部分,并在这样做时将原始部分中的部分替换为答案。

因此,例如,程序将尝试在 ((1 * 3) / (4 / 2)) 中求解的第一个部分将是 (1 * 3),因此它将用乘积 3 替换该部分,而 ((1 * 3) / (4 / 2)) 现在将是 (3 / (4 / 2))

我当前的代码,如果有帮助的话在这里 - http://pastebin.com/Xpayzbff,处理配对的函数是 parse()

谢谢!

编辑(2019 年 7 月 3 日): 对于任何尝试类似项目的人,我在询问后很快就弄清楚了。为了提供帮助,这是我的源代码 - https://github.com/j-osephlong/python-arithmetic

【问题讨论】:

  • 正如我所说的in my profile,计算器不是初学者编码项目的最佳选择。您可以考虑编写类似纸牌游戏或棋盘游戏的程序。
  • 要实现一个中缀表达式求值器(不是求解器,因为求解器是别的东西),你需要理解和使用递归。
  • @TigerhawkT3 或至少一个具有三个独立参数的操作
  • 请不要使用 PasteBin。让您的问题自成一体,并minimal reproducible example 说明您希望完成的工作
  • @DYZ 没有意识到 pastebin 是一个糟糕的选择,但是在我目前的代码中我确实使用了递归,但是它选择一个部分的方式并不适用于所有表达式

标签: python string python-3.x parsing math


【解决方案1】:

我不会为你编写代码,因为那样会破坏重点,但你可能想要的是Shunting-yard algorithm。它将中缀(人类通常如何表示一系列操作,操作数 in)转换为后缀(计算机易于评估,操作符 after操作数)。

这是在 python 中为布尔登录做的:https://msoulier.wordpress.com/2009/08/01/dijkstras-shunting-yard-algorithm-in-python/

您也可以尝试将语句直接解析为 AST,然后您可以对其进行操作。

还请务必查看tokenizer module for python。

【讨论】:

    【解决方案2】:

    将括号之间的表达式视为符号列表,该列表也可以包含另一个列表。所以"((1 * 3) / (4 / 2))"[['1', '*', '3'], '/', ['4', '/' '2']] 表示。将符号列表称为“节点”。

    当您迭代字符串时,维护一个堆栈,该堆栈跟踪您所在的“括号对”(或节点)。将符号附加到堆栈中的最后一个节点(current_node)。在每个'(',添加一个新节点到你所在的节点,到堆栈。在每个')',弹出堆栈,这样你就在父节点中,更多的符号将被附加在那里。

    然后你可以递归地评估每个节点,首先在内部,直到你有一个最终值。

    对这段代码进行逆向工程。

    def parse(expr):
        stack = [[]]
        for i in expr:
            current_node = stack[-1]
            if i == '(':
                new_node = []
                current_node.append(new_node)
                stack.append(new_node)
            elif i == ')':
                stack.pop()
            elif not i.isspace():
                current_node.append(i)
    
        return stack[0]
    
    print(parse('(1 * 3) / (4 / 2)')) # [['1', '*', '3'], '/', ['4', '/', '2']]
    

    看看这个问题: Python parsing bracketed blocks

    【讨论】:

      【解决方案3】:

      您可以使用Shunting-Yard 算法。但是,该算法的完整实现非常复杂。这是一个更简单、有点幼稚的版本,可以让你有一个基本的了解https://gist.github.com/tiabas/339f5c06f541c176a02c02cc3113d6f7

      # Simple Shunting-Yard solution
      #
      # Given a math expression, parse and evaluate
      # the expression
      #
      # E.g '2+1' => 3, 8/2*4+1 => 17, 2+(1*2) = 4, ((2+4)/2*7) => 21
      # 
      def parse_math_expression(exp):
          PRECENDENCE = {
              ')': 3,
              '(': 3,
              '*': 1,
              '/': 1,
              '+': 0,
              '-': 0,
          }
          output = []
          operators = []
          for ch in exp:
              # Handle nested expressions
              if ch == ')':
                  opr = operators.pop(0)
                  while opr != '(':
                      output.append(opr)
                      opr = operators.pop(0)
              elif ch.isdigit():
                  output.append(ch)
              else:
                  # Handle operator prescendence
                  top_op = None
                  if len(operators) and operators[0]:
                      top_op = operators[0]
                  # Check if top operator has greater prcendencethan current char
                  if top_op in ['*', '/'] and PRECENDENCE[top_op] > PRECENDENCE[ch]:
                      output.append(top_op)
                      operators.pop(0)
                  # Push operator onto queues
                  operators.insert(0, ch)
          # Handle any leftover operators
          while len(operators):
              output.append(operators.pop(0))
          return output
      
      test1 = "(2+1)"
      assert parse_math_expression(test1) == ['2', '1', '+']
      test2 = "((2+4)/(2*7))"
      assert parse_math_expression(test2) == ['2', '4', '+', '2', '7', '*', '/']
      test3 = "(3*2)+(4/2)"
      assert parse_math_expression(test3) == ['3', '2', '*','4', '2', '/','+']
      
      def eval_parsed_expression(exp):
          OPRS = {
              '+': lambda a, b: a + b,
              '-': lambda a, b: a - b,
              '*': lambda a, b: a * b,
              '/': lambda a, b: a / b
          }
          tmp = []
          while len(exp) > 1:
              k = exp.pop(0)
              while not k in ['*', '-', '+', '/']:
                  tmp.insert(0, k)
                  k = exp.pop(0)
              o = k
              b = tmp.pop(0)
              a = tmp.pop(0)
              r = OPRS[o](int(a), int(b))
              exp.insert(0, r)
          return exp[0]
      
      test4 = ['2', '1', '+'] # (2+1*2)
      assert eval_parsed_expression(test4) == 3
      test5 = ['2', '1', '2', '*', '+'] # (2+1*2)
      assert eval_parsed_expression(test5) == 4
      test6 = ['3', '2', '*','4', '2', '/','+'] # (3*2)+(4/2)
      assert eval_parsed_expression(test6) == 8
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-06-20
        • 2011-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多