-1

My task is to take in a math operation in the form of a string and return the answer as an integer. Order of operations is also ignored and the math operation has to be solved left to right.

My code could probably be shorter/simpler, but this is what I've come up with. My strategy is to go through each character in the string until I hit an operator(+,-,x,/). Then the string of numbers before the operator is turned into an integer. The operator is stored as the variable c so that the operation can be done with the current number and the next number encountered(c=1 refers to addition, etc.).

When I run the program, it's supposed to print the variable total, but doesn't print anything. Do the return statements exit out of the current if statement or do they exit out of the while loop? Is the problem something else?

def compute(e):
    a=0 #starting index used to check specific characters in string
    b=0 #to keep track of index after last operator
    l=len(e) #length of e to use for while loop
    st=""
    total=0 #result of all previous calculations
    count=0 #keeps track of number of operators passed
    c=0 #Used to keep track of what operator was last passed
        #c=1 is addition
        #c=2 is subtraction
        #c=3 is multiplication
        #c=4 is division
    while l>0:
        if e[a].isdigit():
            return
        elif e[a]=="+" or e[a]=="-" or e[a]=="x" or e[a]=="/": #When an operator is detected in string
            st=e[b:a] #To combine all numbers between two operators into one string
            count=count+1 #to keep track of number of operators passed
            b=a+1 #now b is the index after the last operator
            if count==1: #This is the first operator encountered in string
                total=int(st) #The string is stored as total because this is the first integer stored
            else:
                num=int(st) #The string of numbers is stored as num instead of total
            if count==1: #This means this is the first operator and there should not be an operation done yet
                return
            elif c==1:
                total=total+num
            elif c==2:
                total=total-num
            elif c==3:
                total=total*num
            elif c==4:
                total=total/num
            if e[a]=="+":
                c=1
            elif e[a]=="-":
                c=2
            elif e[a]=="x":
                c=3
            elif e[a]=="/":
                c=4
        else:
            return None
        a=a+1
        l=l-1
    print(total)

compute("22-11x4")
input("Wait")
Devin
  • 7,690
  • 6
  • 39
  • 54
krzy122
  • 5
  • 3
  • A return statement exits the entire function, breaking out of any and all loops in it. – BrenBarn Sep 30 '14 at 04:11
  • @BrenBarn so what could be placed where the return statements that just breaks out of the current if statement? – krzy122 Sep 30 '14 at 04:14
  • Use **continue** statement to skip rest of the execution in a while loop instead of return statements – Arun Ghosh Sep 30 '14 at 04:18
  • @ArunGhosh I don't want to skip the entire while loop, just the current if statement. – krzy122 Sep 30 '14 at 04:23
  • There is no syntax construct in python to "break" out of just a if-statement. – ApproachingDarknessFish Sep 30 '14 at 04:27
  • 1
    @krzy122 just put **pass** statement instead of return – Arun Ghosh Sep 30 '14 at 04:31
  • @krzy122 So if I understand your question right then you want to eval an expression which is passed as string but still perform the mathematical operation on the string right ? – d-coder Sep 30 '14 at 04:46
  • @sammy Yes, evaluated from left to right – krzy122 Sep 30 '14 at 04:49
  • `return` doesn't mean "return to the start of the loop" - I think this may be the cause of your confusion. It means return to the place where the method was called from. – Burhan Khalid Sep 30 '14 at 04:57
  • @BurhanKhalid I just want to continue in the while loop, not go to the start. If there is a number in the string, I want to pass it and continue until I find an operator. So I basically want to say do nothing if i encounter a number. Would pass continue through the while loop like this? – krzy122 Sep 30 '14 at 05:02
  • It's a bit like saying "Throw all of this away," but you should really look into the [shunting yard algorithm](http://en.wikipedia.org/wiki/Shunting-yard_algorithm) – TML Sep 30 '14 at 05:04
  • @BurhanKhalid And sorry, I don't know how to distinguish code in a comment. – krzy122 Sep 30 '14 at 05:06
  • with backticks (`) at the start and end – TML Sep 30 '14 at 05:07
  • Ok, I changed the `return` statements to pass and it printed 11 so now I just need to make sure it multiplies that by 4. – krzy122 Sep 30 '14 at 05:14
  • You could also try to tackle the problem in a recursive manner, removing operators until you hit the base case of only one number. – Vedaad Shakib Sep 30 '14 at 05:20

3 Answers3

1

Try this code:

def compute(e):
    l=len(e)
    seperate=[]
    indexf=0
    indexl=0
    count=1
    total=0
    num=0

    for i in range(0,l):
        if (e[i].isdigit()):
            indexl=indexl+1
        else:
            num=e[indexf:indexl]
            indexf=indexl+1
            indexl=indexf
            seperate.append(num)
            seperate.append(e[i])
    num=e[indexf:indexl+1]
    seperate.append(num)

    l=len(seperate)

    for i in range(0,l):
        if (count==1):
            total=int(seperate[i])
            count=count+1
        else:
            if (not(seperate[i].isdigit())):
                if (seperate[i]=="+"):
                    total=total+int(seperate[i+1])
                elif (seperate[i]=="-"):
                    total=total-int(seperate[i+1])
                elif (seperate[i]=="*") or (seperate[i]=="X")or(seperate[i]=="x"):
                    total=total*int(seperate[i+1])
                elif (seperate[i]=="/"):
                    total=total/int(seperate[i+1])

    print("Resault is %d ,Have a good time" %(total))

st=input("Please insert your expression :")
compute(st)
0

Here's an example I dashed together on a different approach to the underlying problem. My 'calculate' function breaks the string up by "things that are not digits" (re.split(r'(\D+)', string)), storing the thing split on); then we walk the resulting list (for item in re.split(r'(\D+)', string):).

If it's a bunch of consecutive digits (if re.match(r'\d+', item):), we'll append it to a collections.deque(); if it's whitespace (if re.match(r'\s+', item):), we'll throw it away (continue). Otherwise, we assume it's an operator, which is just appended to the operators list (special case made for x, we will turn that into a * - this is purely cosmetic on my part).

I defined a dictionary of lambdas (doop, for 'do operations'), where the keys are the operators I support, and the expression is the mathematical operation to perform. All that is left now is to loop over the operators list (for op in operators), find the correct lambda for that operator (doop[op]()), and call it - passing the two "top" values on the operands deque (operands.popleft()), putting the result right back on the top of the list for the next operator to consume (operands.appendleft()).

There are a lot of edge cases my simple example won't handle, and it obviously doesn't know about operator precedence (but you said it should not try to apply that anyway). I just wanted to give you an alternate approach that might set your thoughts down a different path - one that is, perhaps, a bit easier to maintain than all the if/elsif/else's.

import collections
import re

doop = {
    '+': lambda x,y: x+y,
    '*': lambda x,y: x*y,
    '-': lambda x,y: x-y,
    '/': lambda x,y: x//y
}

def calculate(string):
    operands = collections.deque()
    operators = []
    for item in re.split(r'(\D+)', string):
        if re.match(r'\d+', item):
            operands.append(int(item))
            continue
        if re.match(r'\s+', item):
            continue
        else:
            if item == 'x':
                item = '*' # *shrug*
            operators.append(item)
    for op in operators:
        operands.appendleft(doop[op](operands.popleft(),operands.popleft()))
    result = operands.pop()
    print("Calculated %s to be %s" % (string, result))
    return result


strings = ["15+2*3-7", "22-11x4"]
for string in strings:
    calculate(string)
TML
  • 12,813
  • 3
  • 38
  • 45
  • Thanks for the input. The `if`/`else` statements in my code are a bit much. I see what you're saying, but your code looks a little too complex for my knowledge, I'm currently taking cs1. Thanks for the help, though! – krzy122 Sep 30 '14 at 16:52
  • I figured as much - I tried to provide an answer that would give you a good survey of some ideas to make sure you're comfortable with as your course continues. Best of luck! – TML Sep 30 '14 at 17:08
-1

I will point out some things that will help you resolve this code. First, like many have pointed out the return statement breaks out of the function and therefore should not be used in the while loop. Replace them with break and continue if need be. Secondly, this piece of code :

 if e[a].isdigit():
        return

Is not necessary I feel. Since the loop continues anyway no matter the value of e[a] and the value keeps getting accumulated in the total variable.

Third, you assign the operator characters to integers if I'm not wrong. While that can be done away with, you are using those integer codes to perform calculations before assigning them as you do the check to assign the value to c after you update the total variable. So I think as soon as you enter the elif block, where you check if the present character is an operator, your first step should be to assign appropriate value to c and do the needful calculation.

Something like:

 elif e[a]=="+" or e[a]=="-" or e[a]=="x" or e[a]=="/": #When an operator is detected in string
        st=e[b:a] #To combine all numbers between two operators into one string
        count=count+1 #to keep track of number of operators passed
        b=a+1 #now b is the index after the last operator
        if count==1: #This is the first operator encountered in string
            total=int(st) #The string is stored as total because this is the first integer stored
        else:
            num=int(st) #The string of numbers is stored as num instead of total

        #Assign operator values
        if e[a]=="+":
            c=1
        elif e[a]=="-":
            c=2
        elif e[a]=="x":
            c=3
        elif e[a]=="/":
            c=4

        #Perform arithmetic operation
        if count !=1: #Not the first operator, we can accumulate the values
          if c==1:
            total=total+num
          elif c==2:
            total=total-num
          elif c==3:
            total=total*num
          elif c==4:
            total=total/num

I have restructured your elif block just a little bit. I have done away with the return statement and just performed the check before we do the calculation. Actually this is the only check you should do so the outermost if and else statements that return can be removed and safely replaced by just one if statement that does this. I hope it gets you some clarity about your code and gets you started in the right direction.

Vivek Pradhan
  • 4,777
  • 3
  • 26
  • 46
  • Can you please explain why you voted down my answer? – Vivek Pradhan Sep 30 '14 at 05:33
  • I didn't downvote it. I see your point about not needing `isdigit()`, it will just continue since the character is not an operator. The reason I am assigning the `c` value after doing the operation is that each time you encounter an operator, you need to then find the number after that operator and use it with the number before the operator. So when it encounters an operator, it takes the number before it and uses the `c` value that is stored to perform the operation from the last operator encountered. `c` is assigned after that operation is done so that it can use it with the next full number – krzy122 Sep 30 '14 at 16:46