Python:如何使用逻辑算法评估代数表达式?

时间:2018-07-24 19:59:18

标签: python algorithm math calculator evaluation

我是编程新手,因此开始了一个小项目以求更好。 我想做一个不太基本的shell计算器,基本方法很快就完成了,我学到了很多东西。 但是我的新目标是评估完整的代数表达式,例如:(2+3)^(80/(3*4)) 我认为一种算法将像人类一样逐步“一步一步”地派上用场-解决整个问题的一小部分并重新开始。 所以我想先检查所有有趣的代数符号: (术语是用户输入)

    open_brackets     =[i.start() for i in re.finditer('\(',str(term))]
    close_brackets    =[i.start() for i in re.finditer('\)',str(term))]
    powers            =[i.start() for i in re.finditer('\^',str(term))]
    mult              =[i.start() for i in re.finditer('\*',str(term))]
    div               =[i.start() for i in re.finditer('\/',str(term))]
    plus              =[i.start() for i in re.finditer('\+',str(term))]
    minus             =[i.start() for i in re.finditer('\-',str(term))]

现在我掌握了这些信息,我认为寻找最右边的开口括号并找到相应的闭合括号可能会很有用:

last_open_bracket = open_brackets[len(open_brackets)-1]
    next_closed_bracket = 0
    for i in close_brackets : 
        if last_open_bracket < i :
            next_closed_bracket = i
            break

从那以后,我只遇到问题,因为我不知道如何构造整个事情,而且我已经搞砸了好几次了。 在这些括号内查看并检查它们之间的代数符号显然是有用的。自从大国以来,大国的例子是:

for i in powers : 
        if (last_open_bracket < i) and (next_closed_bracket > i) :

现在,一旦找到“ ^”的位置,就可以逐步移至“ ^”的左侧,直到遇到非int或“”类型的东西。由于这是在浮点数中使用的,更具体地说,我搜索下一个代数符号或方括号。 依此类推,依次类推,直到获得2个数字,然后再取幂。 然后在原始输入中切出用于此计算的所有符号和数字,并将计算结果添加到正确的位置,然后重新开始。

我的问题是处理括号内可能发生的所有不同情况。

  • 多个操作,即(2 + 3 + 4)
  • 识别(-9)或-(9)中的“-”,而不是减去某些东西的调用

还有在某些情况下如何处理不同的括号,例如,我早些时候曾建议将所有使用的符号和数字切掉,但是在不同情况下,我们需要使用不同的方法:

  1. (9)-> 9
  2. (2 + 3)-> 5
  3. (-2)^ 2-/-> -2 ^ 2

如您所见,我需要一点思考帮助,我不知道如何概念化此算法。 希望您能帮助我,并祝您愉快!

3 个答案:

答案 0 :(得分:2)

尽管您的代数语言非常有限,但这并不是一件容易的事。

您当然可以按照其他答案的建议去编写自己的解析器。或者,您可以使用解析器生成器来从语法规则生成解析器。

这是一种可能的解决方案草图:

您首先需要为您的代数语言定义一个语法。在Wikipedia上,a similar language有一个正式的语法。

您尝试评估的语言似乎与上下文无关。

用于指定形式语法的规则有多种。一种叫做EBNF(扩展Backus Naur形式)。

上下文无关语言的语法通常被定义为包含终止符和非终止符的生产规则,而规则的左手仅包含一个非终止符。

您所用的端子符号是数字(0到9),括号和运算符+-/*^

对于您的语言,我猜想一个单一的非终结符号S就足够了。

您的规则如下所示:

S        -> ( <S> + <S> );
S        -> ( <S> - <S> );
S        -> ( <S> / <S> );
... 
S        -> 0|1|2|3|4|5|6|7|8|9;

This is a nice tool尝试使用上下文无关的规则:

指定语法后,您可以使用解析器生成器(例如ANTLR)生成解析器,如果输入字符串符合查询条件,该解析器会将输入字符串转换为parse tree,并抛出一个否则例外。

维基百科为各种不同的语言提供了list of parser generators。 您应该查看有关上下文无关语言的部分。

然后,您将需要使用结构递归来评估抽象语法树。

答案 1 :(得分:1)

我看过一些解析器并编写了一些解析器,但是我从未见过像您正在尝试的算法那样的算法。您的困难可能意味着您应该使用其他整体算法。

我建议您一次从左至右一次扫描整个表达式字符串,然后对每个字符进行适当处理。如果您想简化操作,可以使用PEMDAS和例程组来创建递归算法。顶层计算一个表达式,下一个括号组,下一个指数,下一个乘法和除法,最后一个加法和减法。您还需要一个例程来评估数字文字,其中一个用于取反,一个用于一元加法。

还有其他方法,例如为每个运算符使用令牌,将内容放到堆栈上以及使用为每个运算符给出操作顺序的字典。在这里要小心取幂,因为它的顺序是从右到左而不是从左到右。但是您的需求似乎很简单,并且递归方法对您来说会更简单。

答案 2 :(得分:1)

您可能要考虑的一件事是自下而上的解析器。您可以通过在递归解析方法内部使用正则表达式基于操作顺序进行过滤来使用它。首先,将括号内的内容传递给解析。然后,您将自己的权力传递给解析。然后,将乘法和除法从左到右传递给解析。最后,您将加法和减法再一次从左至右传递给解析。在最底层(即*,/,-,+,^),您需要先评估表达式,然后再将它们传回。可以在here上找到有关该主题的维基百科文章。