首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python PEMDAS计算器

Python PEMDAS计算器
EN

Code Review用户
提问于 2017-09-26 19:42:18
回答 2查看 3.3K关注 0票数 2

这个计算器的目标是用PEMDAS来计算表达式(还没有括号,很快就会添加)。虽然这个程序有效,但我想用我用来攻击数学表达式的方法使它尽可能高效。

因此,我决定解决这个问题的方法是最初通过我的cleanUp()来组织表达式,这样以后操作/迭代就更容易了,例如:

"2+2 -4 * 1 / 3.344 - -2“变成了"2 + 2 - 4 * 1 / 3.344 + 2”,它现在可以很容易地被一个空间分割,计算过程现在要简单得多,但是我觉得我的cleanUp()函数可以更高效。(我还在学习Python 3内置函数的时间复杂性)

这是我的全部代码,可以随意评论程序的任何其他部分,但我主要要求对cleanUp()函数进行优化改进。

代码语言:javascript
复制
import time

def isNumber(s):
    #s must be a non-empty string
    #returns True if it's convertible to float, else False
    if (len(s)==0 and not s =='') or not isinstance(s, str):
        print("type mismatch error: isNumber")
        return "type mismatch error: isNumber"
    try:
        s = float(s)
        return True
    except ValueError:
        #print("type mismatch error: isNumber")
        return False


def cleanUp(s):
    priorSpace = True
    inDecimal = False
    ops = ["+","-","*","/","^"]
    s = s.replace("+", " + ").replace("-", " - ").replace("*", " * ").replace("/", " / ").replace("^", " ^ ")
    st = list(s)
    for i,c in enumerate(s):
        if (not isNumber(c) and c not in ops) and priorSpace:
            if c == "." and not inDecimal:
                inDecimal = True
            elif c == "." and inDecimal:
                st[i] = ""
            else:
                st[i] = ""
                inDecimal = False
        elif (not isNumber(c) and c not in ops) and not priorSpace:
            if c == "." and not inDecimal:
                inDecimal = True
            elif c == "." and inDecimal:
                st[i] = ""
            else:
                st[i] = " "
                priorSpace = True
                inDecimal = False
        else:
            priorSpace = False
    final = ''.join(st).strip().split(' ')
    if '-' in final[0]:
        del final[0]
        final[0] = '-' + final[0]
    newNumber, newOpr, oprPos = getNextNumber(final, 0)
    if oprPos == None:
        return newNumber
    cacheNum=newNumber
    pos=oprPos+1                #the new current position
    opr=newOpr
    while pos<(len(final)-1):
        newNumber, newOpr, oprPos = getNextNumber(final, pos)
        if (newOpr == None):
            break
        if (not isNumber(newNumber) and newNumber == '-'):
            del final[oprPos-1] #BOOKMARK
            final[oprPos-1] = '-' + final[oprPos-1]
        if (not isNumber(newNumber) and newNumber != '-'):
            print("error at line AB", newNumber)
            return "error at line AB"
        pos=oprPos+1
    return (final)


def getNextNumber(expr, pos):
    #expr is a given arithmetic formula in string
    temp = expr
    #pos = start position in expr
    #1st returned value = the next number (None if N/A)
    #2nd returned value = the next operator (None if N/A)
    #3rd retruned value = the next operator position (None if N/A)
    ops = ["+","-","*","/"]
    if len(expr)==0 or pos<0 or pos>=len(expr) or not isinstance(pos, int):
        print("type mismatch error: getNextNumber")
        return None, None, "type mismatch error: getNextNumber"
    #--- function code starts ---#
    res3 = 0
    res2 = 0
    if (pos+1) < len(temp):
        res3 = pos+1
        res2 = temp[pos+1]
    else:
        res3 = None
        res2 = None
    return ((temp[pos], res2, res3))



    #--- function code ends ---#


def exeOpr(num1, opr, num2):
    #This is a simple utility function skipping type check
    if opr=="+":
        return num1+num2
    elif opr=="-":
        return num1-num2
    elif opr=="*":
        return num1*num2
    elif opr=="/":
        return num1/num2
    elif opr=="^":
        return num1**num2
    else:
        return None


def calc(expr):
    #expr: nonempty string that is an arithmetic expression
    #the fuction returns the calculated result
    expr = cleanUp(expr)
    if len(expr)<=0:
        print("argument error: line A in eval_expr")        #Line A
        return "argument error: line A in eval_expr"
    #Hold two modes: "addition" and "multiplication"
    #Initializtion: get the first number
    newNumber, newOpr, oprPos = getNextNumber(expr, 0)
    ops = ["^","*","/","+","-"]
    if newNumber is None or not isNumber(newNumber):
        print("input formula error: line B in eval_expr")   #Line B
        return "input formula error: line B in eval_expr"
    elif newOpr is None or not newOpr in ops:
        return newNumber
    cacheNum=newNumber
    pos=oprPos+1                #the new current position
    opr=newOpr                  #the new current operator
    #start the calculation. Use the above functions effectively.
    new_expr = expr
    while True:
        newNumber, newOpr, oprPos = getNextNumber(new_expr, pos)
        #print(cacheNum, opr, newNumber, "The next opr however is", newOpr)
        if (not isNumber(newNumber)):
            print("input formula error: line C in eval_expr")   #Line C
            return "input formula error: line C in eval_expr"
        if (newOpr == None):
            break
        if(newOpr == "^"):
            temp = getNextNumber(new_expr, oprPos+1)[0]
            if (not isNumber(temp)):
                print("input formula error: line D in eval_expr")   #Line D
                return "input formula error: line D in eval_expr"
            #print("Weee", newOpr, newNumber, "with", temp)
            subs = exeOpr(float(newNumber), newOpr, float(temp))
            del new_expr[oprPos-1]
            del new_expr[oprPos-1]
            new_expr[oprPos-1] = str(subs)
            #print("The new expression to start calculating is: " + new_expr)
        elif((newOpr == "*" or newOpr == "/") and (opr not in ["^","*","/"])):
            temp, temp_Opr, temp_Pos = getNextNumber(new_expr, oprPos+1)
            #print("uhhh", temp_Opr)
            if temp_Opr == "^":
                temp2 = getNextNumber(new_expr, oprPos+3)[0]
                if (not isNumber(temp)):
                    print("input formula error: line D in eval_expr")   #Line D
                    return "input formula error: line D in eval_expr"
                #print("We", temp_Opr, temp, "with", temp2)
                subs = exeOpr(float(temp), temp_Opr, float(temp2))
                del new_expr[temp_Pos-1]
                del new_expr[temp_Pos-1]
                new_expr[temp_Pos-1] = str(subs)
                #print("The new expression to start calculating is: " + new_expr)
            else:
                if (not isNumber(temp)):
                    print("input formula error: line D in eval_expr")   #Line D
                    return "input formula error: line D in eval_expr"
                #print("Weee", newOpr, newNumber, "with", temp)
                subs = exeOpr(float(newNumber), newOpr, float(temp))
                del new_expr[oprPos-1]
                del new_expr[oprPos-1]
                new_expr[oprPos-1] = str(subs)
                #print("The new expression to start calculating is: " + new_expr)
        elif((newOpr == "+" or newOpr == "-") and opr not in ["+","-"]):
            subs = exeOpr(float(cacheNum), opr, float(newNumber))
            del new_expr[pos-2]
            del new_expr[pos-2]
            new_expr[pos-2] = str(subs)
            cacheNum=str(subs)
            opr=newOpr
            #print("The new expressionn to start calculating is: " + new_expr)
        else:
            subs = exeOpr(float(cacheNum), opr, float(newNumber))
            del new_expr[pos-2]
            del new_expr[pos-2]
            new_expr[pos-2] = str(subs)
            #print("The new expression to start calculating is: " + new_expr)
            cacheNum=str(subs)
            opr=newOpr
    subs = exeOpr(float(cacheNum), opr, float(newNumber))
    #print(new_expr)
    del new_expr[new_expr.index(cacheNum)]
    del new_expr[new_expr.index(opr)]
    new_expr[new_expr.index(newNumber)] = str(subs)
    return float(new_expr[0])

print(calc("2+2 -4 * 1 / 3.344 - -2")
EN

回答 2

Code Review用户

回答已采纳

发布于 2017-09-28 08:18:00

可以用几乎一个正则表达式更改整个cleanUp方法

举个例子:

代码语言:javascript
复制
import re
def clean(s):
    return re.split("([+\-\/*])", s.replace(" ", ""))

>>> print(clean("2+2 -4 * 1 / 3.344 - -2"))
>>> ['2', '+', '2', '-', '4', '*', '1', '/', '3.344', '-', '', '-', '2']

这几乎与您的输出只有不同之处相同,当有两个操作器时,它会添加一个空的''

这可以使用一些列表切片来修复,使干净的看起来如下所示:

代码语言:javascript
复制
import re
def clean(s):
    cleaned_expr = re.split("([+\-\/*])", s.replace(" ", ""))
    while '' in cleaned_expr:
        # Remove empty and concat operator to next number
        i = cleaned_expr.index('')
        cleaned_expr[i] = cleaned_expr[i+1] + cleaned_expr[i+2]
        del cleaned_expr[i+1:i+3]
    return cleaned_expr

>>> print(clean("2+2 -4 * 1 / 3.344 - -2"))
>>> ['2', '+', '2', '-', '4', '*', '1', '/', '3.344', '-', '-2']
>>> print(clean("2+2- -4 * 1 / 3.344 -2"))
>>> ['2', '+', '2', '-', '-4', '*', '1', '/', '3.344', '-', '2']

其他一些轻微的PEP8改进包括:

  1. 函数的命名应该是clean_up()而不是cleanUp
  2. 缺少像=这样的运算符周围的空白
  3. 缺少,周围的空白

Note

回想起来,感谢@Mathias,您的calc()感到有问题,作为一个诚实的审阅者,我建议它处理像这个['2', '+', '2', '-', '4', '*', '1', '/', '3.344', '-', '-', '2']那样的输入,当它被修复时,我想再看看它,因为现在它感觉太错误了。

代码语言:javascript
复制
# The cleanest clean (but incorrect output for your calc)
import re
def clean(s):
    s = re.split("([+\-\/*])", s.replace(" ", ""))
    return [token for token in s if token]

>>> print(clean("2+2 -4 * 1 / 3.344 - -2"))
>>> ['2', '+', '2', '-', '4', '*', '1', '/', '3.344', '-', '-', '2']
票数 1
EN

Code Review用户

发布于 2017-09-27 11:22:20

不管事实如何,您的代码在返回时没有正常运行。

代码语言:javascript
复制
input formula error: line C in eval_expr
input formula error: line C in eval_expr

使用示例输入,我建议将模块简化为

代码语言:javascript
复制
from re import compile

ARITHMETIC_EXPRESSIONS = compile('[0-9]*( ){0,}([+-/*^]( ){0,}[0-9]*( ){0,})*')
LEADING_ZEROS = compile('^0+(?=.)')


def coerce_python_syntax(string):
    """Converts arithmetic expression to python syntax."""

    string = string.replace('^', '**')
    return LEADING_ZEROS.sub('', string)


def calc(string):
    """Calculates the arithmetic expression given by string."""
    if ARITHMETIC_EXPRESSIONS.fullmatch(string):
        return eval(coerce_python_syntax(string))

    raise ValueError('String is not a safe arithmetic expression.')


if __name__ == '__main__':
    print(calc("2+2 -4 * 1 / 3.344 - -2"))

因为您正在实现基本的算术计算,python已经很冷了。

票数 0
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/176587

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档