0

I'm trying to create a function which will solve for some numeric computation – which is given as a string.

Example:

def calculate(expression):
   # Solve the expression below
   return

# Result should be 19
calculate("5+8-3+9")

I have tried using .split() but got stuck.

Yaakov Bressler
  • 9,056
  • 2
  • 45
  • 69
rose
  • 31
  • 5
  • 3
    Does this answer your question? [Evaluating a mathematical expression in a string](https://stackoverflow.com/questions/2371436/evaluating-a-mathematical-expression-in-a-string) – Hephaistos-plus Jun 01 '20 at 04:54
  • 1
    Q is a bit different from that provided @Hephaistos-plus – Yaakov Bressler Jun 01 '20 at 05:32
  • 1
    @YaakovBressler That's true, they're a bit far apart. I wasn't sure what to refer to since the questions that are closer to what the poster asked, such as [calculate the equation given a string](https://stackoverflow.com/questions/40106976/calculate-the-equation-given-a-string/40107056) are usually listed as duplicates of the question I linked to, and I thought I might as well link directly to the most popular instance. But perhaps I should have linked to one of the closer questions instead. – Hephaistos-plus Jun 01 '20 at 05:39
  • Good points @Hephaistos-plus – thanks for making SO a neater and more collaborative space :) – Yaakov Bressler Jun 01 '20 at 05:47

3 Answers3

0

For a problem like this we can try tackling it by using this, a string calculator.

'''
Calculates a string of characters only for addition and subtraction
with parentheses. This calculator utilizes the stack method.
'''

import re # imports regular expression library for usage

def calculate(s: str) -> int:

    s = re.sub(r'[A-Za-z\s\t]+', '', s) 
    res = 0
    num = 0
    sign = 1
    stack = []
    for ss in s:
        # checks if each element is a digit
        if ss.isdigit():
            num = 10 * num + int(ss)
        # if not a digit, checks if its + or - sign
        elif ss in ["-", "+"]:
            res = res + sign * num
            num = 0
            sign = [-1, 1][ss == "+"]
            '''
            sign = [-1, 1][ss=="+"]  is the same as:
            # int(True) = 1, int(False) = 0. Hence,
            if ss == "+":
                        sign = 1
            else:
                        sign = -1
            '''
    return res + num * sign

s = input("Enter your string: ") 
# OR if you'd like, can uncomment this line below and comment the line above. 
# s = "5+8-3+9" # As an expression in a string

print(calculate(s))
penguin
  • 628
  • 2
  • 10
  • 22
0

I would suggest breaking the question down to its numbers and operators.

Also, I've made the assumption that only whole numbers will be used – and only addition and subtraction.

def calculate(expression):
  # Get all components
  components = re.findall("[+-]{1}[0-9]*|^[0-9]*",expression)

  # get each number with its positive or negative operator
  operators = re.compile("[-+]")

  # Iterate and add to a list
  all_nums = []
  for x in components:  
    # get the number
    n = int(re.sub(operators,"",x))
    # For all terms after the first
    if operators.search(x):
      op = operators.search(x).group()
      if op=="+":
        n = n
      elif op=="-":
        n=-n
    # Save the number
    all_nums.append(n)

  # Finally, add them up
  return sum(all_nums)

x = "5+8-3+9"
calculate(x)
# returns 19
Yaakov Bressler
  • 9,056
  • 2
  • 45
  • 69
0

Hi rose

First of all, it's okay to be a beginner - I was in your exact shoes just a few years ago!

I'm going to attempt to provide an elementary/beginner approach to solving this problem, with just the basics.

So first we want to determine what the limits of our function input will be. For this, I'll assume we only accept mathematical expressions with basic addition/subtraction operators.

import re

def calculate(expression: str) -> int:
    if not re.match("^[0-9\+-]*$", expression):
        return None

For this you'll see I opted for regex, which is a slightly more advanced concept, but think about it like a validity check for expression. Basically, the pattern I wrote checks that there is a fully qualified string that has only integers, plus sign, and minus sign. If you want to learn more about the expression ^[0-9\+-]*$, I highly recommend https://regexr.com/.

For our purposes and understanding though, these test cases should suffice:

>>> re.match("^[0-9\+-]*$", "abcs")
>>> re.match("^[0-9\+-]*$", "1+2")
<re.Match object; span=(0, 3), match='1+2'>
>>> re.match("^[0-9\+-]*$", "1+2/3")
>>> 

Now that we have verified our expression, we can get to work on calculating the final value.

Let's try your idea with str.split()! It won't be entirely straightforward because split by definition splits a string up according to a delimiter(s) but discards them in the output. Fear not, because there's another way! The re package I imported earlier can come into handy. So the re library comes with a handy function, split!

By using capture groups for our separator, we are able to split and keep our separators.

>>> re.split("(\d+)", "1+393958-3")
['', '1', '+', '393958', '-', '3', '']

So, with this up our sleeve...

import re

def calculate(expression: str) -> int:
    if not re.match("^[0-9\+-]*$", expression):
        return None
    expression_arr = re.split("(\d+)", expression)[1:-1]
    while len(expression_arr) > 1:
        # TODO stuff
    return int(expression[0])

We can now move onto our while loop. It stands to reason that as long as the array has more than one item, there is some sort of operation left to do.

import re

def calculate(expression: str) -> int:
    if not re.match("^[0-9\+-]*$", expression):
        return None
    expression_arr = re.split("(\d+)", expression)[1:-1]
    while len(expression_arr) > 1:
        if expression_arr[1] == "+":
            eval = int(expression_arr[0]) + int(expression_arr[2])
        if expression_arr[1] == "-":
            eval = int(expression_arr[0]) - int(expression_arr[2])
        del expression_arr[:3]
        expression_arr.insert(0, eval)

    return int(expression_arr[0])

It's pretty straightforward from there - we check the next operator (which always has to be at expression_arr[1]) and either add or subtract, and make the corresponding changes to expression_arr.

We can verify that it passes the test case you provided. (I added some logging to help with visualization)

>>> calculate("5+8-3+9")
['5', '+', '8', '-', '3', '+', '9']
[13, '-', '3', '+', '9']
[10, '+', '9']
[19]
19
notacorn
  • 3,526
  • 4
  • 30
  • 60