1
import string

# Strength of operations:
#   -> [] (brackets)
# 6 -> ~ (negative)
# 5 -> @, $, & (average, maximum, minimum)
# 4 -> %, ! (modulo, factorial)
# 3 -> ^ (power)
# 2 -> *, / (multiplication, division)
# 1 -> +, - (addition, subtraction)

def BinaryOperation(exp, idx):
    """ Gets an expression and an index of an operator and returns a tuple with (first_value, operator, second_value). """
    first_value = 0
    second_value = 0

    #Get first value
    idx2 = idx -1
    if idx2 == 0:
        first_value = exp[idx2:idx]

    else:
        while (idx2 > 0) and (exp[idx2] in string.digits):
            idx2 -=1

        if (exp[idx2] in ("-")) or (exp[idx2] in string.digits):#-5*3
            first_value = exp[idx2:idx]
        else:#%5*3
            first_value = exp[idx2+1:idx]

    #Get second value
    idx2 = idx +1
    if exp[idx+1] not in string.digits: #If there is something like 1*+5, second_sign will be +.
        idx2 += 1 #idx2 will begin from the char after the sign.

    while (idx2 < len(exp)) and (exp[idx2] in string.digits):
        idx2 += 1

    second_value = exp[idx+1:idx2]

    return (first_value, exp[idx], second_value)

def UnaryOperation(exp, idx):
    """ Gets an expression and an index of an operator and returns a tuple with (operator, value). """
    #Get value
    idx2 = idx+1
    if exp[idx+1] not in string.digits: #If there is something like ~-5, second_sign will be -.
        idx2 += 1 #idx2 will begin from the char after the sign.
    while (idx2 < len(exp)) and (exp[idx2] in string.digits):
        idx2 +=1

    return (exp[idx], exp[idx+1:idx2])

def Brackets(exp):
    idx = 0
    while idx < len(exp):
        if exp[idx] == "[":
            #Brackets
            close_bracket = exp.find("]")
            if close_bracket == -1:
                raise Exception("Missing closing bracket.")

            exp_brackets = exp[idx+1:close_bracket]

            value = str(solve(exp_brackets))

            exp = exp.replace("[" + exp_brackets + "]", value)
            idx = 0 #The len has been changed, scan again.

        idx += 1

    return Level6(exp)

def Level6(exp):
    idx = 0
    while idx < len(exp):
        if exp[idx] in ("~"):
            #Negative
            sub_exp = UnaryOperation(exp, idx)
            value = ~int(sub_exp[1])

            value = str(value)

            exp = exp.replace(''.join(sub_exp), value)
            idx = 0 #The len has been changed, scan again.

        idx += 1
    return Level5(exp)

def Level5(exp):
    idx = 0
    while idx < len(exp):
        if exp[idx] in ("@", "$", "&"):
            #Average, Maximum and Minimum
            sub_exp = BinaryOperation(exp, idx)
            first_value = int(sub_exp[0])
            second_value = int(sub_exp[2])
            if sub_exp[1] == "@":
                value = (first_value + second_value)/2
            if sub_exp[1] == "$":
                value = first_value if first_value > second_value else second_value
            if sub_exp[1] == "&":
                value = first_value if first_value < second_value else second_value

            value = str(value)

            exp = exp.replace(''.join(sub_exp), value)
            idx = 0 #The len has been changed, scan again.

        idx += 1

    return Level4(exp)

def Level4(exp):
    idx = 0
    while idx < len(exp):
        if exp[idx] in ("%","!"):
            #Modulo and Factorial
            if exp[idx] == "%":
                sub_exp = BinaryOperation(exp, idx)
                value = int(sub_exp[0]) % int(sub_exp[2])
            if exp[idx] == "!":
                sub_exp = UnaryOperation(exp, idx)
                value = reduce(lambda x,y:x*y, range(1, int(sub_exp[1])+1))


            value = str(value)

            exp = exp.replace(''.join(sub_exp), value)
            idx = 0 #The len has been changed, scan again.

        idx += 1

    return Level3(exp)

def Level3(exp):
    idx = 0
    while idx < len(exp):
        if exp[idx] in ("^"):
            #Power
            sub_exp = BinaryOperation(exp, idx)
            value = int(sub_exp[0]) ** int(sub_exp[2])

            value = str(value)

            exp = exp.replace(''.join(sub_exp), value)
            idx = 0 #The len has been changed, scan again.

        idx += 1

    return Level2(exp)


def Level2(exp):
    idx = 0
    while idx < len(exp):
        if exp[idx] in ("*", "/"):
            #Multiplication and Division
            sub_exp = BinaryOperation(exp, idx)
            if sub_exp[1] == "*":
                value = int(sub_exp[0]) * int(sub_exp[2])
            if sub_exp[1] == "/":
                value = int(sub_exp[0]) / int(sub_exp[2])

            value = str(value)

            exp = exp.replace(''.join(sub_exp), value)
            idx = 0 #The len has been changed, scan again.

        idx += 1

    return Level1(exp)

def Level1(exp):
    idx = 0
    while idx < len(exp):
        if (exp[idx] in ("+", "-")) and (idx != 0):
            #Addition and Subtraction
            sub_exp = BinaryOperation(exp, idx)
            if sub_exp[1] == "+":
                value = int(sub_exp[0]) + int(sub_exp[2])
            if sub_exp[1] == "-":
                value = int(sub_exp[0]) - int(sub_exp[2])

            value = str(value)

            exp = exp.replace(''.join(sub_exp), value)
            idx = 0 #The len has been changed, scan again.

        idx += 1

    return exp

def solve(exp):
    exp = Brackets(exp)
    return float(exp) if "." in exp else int(exp)

def remove_whitespace(exp):
    """ Gets a string and removes all whitespaces and tabs """
    exp = exp.replace(" ", "")
    exp = exp.replace("\t", "")
    return exp

while True:
    exp = raw_input("")
    exp = remove_whitespace(exp)
    print solve(exp)

I have written this program after a lot of effort, and I was wondering about the efficiency of that solution and if it's neat.

So my question is, how plain is this program and is there any better way to rewrite it?

Lior
  • 5,841
  • 9
  • 32
  • 46

3 Answers3

1

just for the point.

>>> eval(raw_input("input calculation: "))
input calculation: 1+1
2
>>> eval(raw_input("input calculation: "))
input calculation: (6*4^2)
26
>>> eval(raw_input("input calculation: "))
input calculation: (3/2.3)*4
5.2173913043478262

for an innocent program, you can use eval

but you really shouldn't use it ever. its only real use is confusing people, and being a fun novelty if you write programs fro yourself and decide you want a calculator.

there are many ways to write a calculator function.

try some of these other answers:

Python creating a calculator

Basic calculator program in python

python calculator program

Community
  • 1
  • 1
Inbar Rose
  • 41,843
  • 24
  • 85
  • 131
1

If you want to check out some custom class-based evaluation engines in Python, these might help you:

Noctis Skytower
  • 21,433
  • 16
  • 79
  • 117
0
again = True
answer = ""
while again is True:
    try:
        expression = raw_input("Enter your expression: ")
        found = False
        oper = -1
        operator1 = 0
        operator2 = 0
        while found==False:
            if (expression.find("+")>0 and expression.find("+")<len(expression)-1):
                found = True
                oper = expression.find("+")
                operator1 = float(expression[:oper])
                operator2 = float(expression[oper+1:])
                print "{} + {} = {}".format(operator1,operator2,operator1+operator2)
            elif(expression.find("-")>0 and expression.find("-")<len(expression)-1):
                found = True
                oper = expression.find("-")
                operator1 = float(expression[:oper])
                operator2 = float(expression[oper+1:])
                print "{} - {} = {}".format(operator1,operator2,operator1-operator2)
            elif(expression.find("*")>0 and expression.find("*")<len(expression)-1):
                found = True
                oper = expression.find("*")
                operator1 = float(expression[:oper])
                operator2 = float(expression[oper+1:])
                print "{} * {} = {}".format(operator1,operator2,operator1*operator2)
            elif(expression.find("/")>0 and expression.find("/")<len(expression)-1):
                found = True
                oper = expression.find("/")
                operator1 = float(expression[:oper])
                operator2 = float(expression[oper+1:])
                print "{} / {} = {}".format(operator1,operator2,operator1/operator2)
            else:
                oper = -1
                found = False
                print "Incorrect expression, please try again"
                break
            again = False
            answer = raw_input("Try again?: ")
            if(answer == "y" or answer=="yes" or answer =="Y" or answer == "YES"):
                again = True
            else:
                again = False
                print "Thank you for playing! See you next time."
                break

    except:
        print "Failed, check your expression and try again"
Faridoon
  • 154
  • 2
  • 4
  • 8