1

I am trying to make my own "calculator" though I'm getting into a problem. When I separate the operators and the digits, negative numbers do not work. Because the '-' symbol is counted as an operator. Now my question is how can I separate between BOTH operators and numbers including negative numbers?

I don't expect negative numbers to work with my current code, though I am trying to make them work.
Any ideas on how can I do that?

Here is the full code for the calculator.

import re

def calc(equation):

    equationList = re.findall('[+-/*]|\d+', equation)
    operators = [i for i in equationList if not i.isdigit()]
    numbers = [i for i in equationList if i.isdigit()]
    operators.insert(0,'+')
    answer = 0

    for i, operator in enumerate(operators):
        number = numbers[i]
        if operator == '+': answer += int(number)
        if operator == '-': answer -= int(number)
        if operator == '*': answer *= int(number)
        if operator == '/': answer /= int(number)

    print(f'Equation: {"".join(equationList)}\nAnswer: {answer}')


while True:
    calc(input('>>> '))

When I try to run an equation with negative numbers I get an error:

>>> -5*10

Traceback (most recent call last):                                                                                                                                                 
  File "main.py", line 22, in <module>                                                                                                                                             
    calc(input('>>> '))                                                                                                                                                            
  File "main.py", line 12, in calc                                                                                                                                                 
    number = numbers[i]                                                                                                                                                            
IndexError: list index out of range
Akaisteph7
  • 5,034
  • 2
  • 20
  • 43
Verify
  • 78
  • 1
  • 6
  • 2
    Add your sample input and output ! – Rex5 Jul 31 '19 at 12:13
  • 1
    You could require wrapping the number like so `(-X)` or split the input on spaces and process one section at a time. – CMMCD Jul 31 '19 at 12:17
  • you will have a little more problems here. have you thought about how to solve the "priority problem" like that multiplying or dividing are caluclating before adding or subtracting numbers – ncica Jul 31 '19 at 12:32
  • The example you should be showing is the one were you are having issues, i.e. with negative numbers – Akaisteph7 Jul 31 '19 at 12:46
  • also, do you want to allow parentheses? – Akaisteph7 Jul 31 '19 at 13:03
  • Yeah I will allow parentheses and priority. Thought I should figure out Negative numbers first but I guess it isn't the case. – Verify Jul 31 '19 at 13:33
  • I think tackling negatives first is good. You have to do at some point anyways. Implementing everything will be quite a challenge though. See my answer below. – Akaisteph7 Jul 31 '19 at 15:37

1 Answers1

0

So, I have a lot of information for you, but it's very relevant. I also drastically changed your code:

Easily separating the operators and the numbers will not work unless you require something such as there being parentheses/spaces around every negative number. But not only will this make it so that you cannot enter regular, unformatted equations in your calculator, it also will require you to treat negative numbers as special. Which they are, but this is not necessary. This is because you can basically ignore that they are negative. The code changes I propose will more easily let you handle any operator as well.

Any negative number can be broken down into an operation: -x is the same as 0 - x. The question is: when should the zero be added? There are two cases: when the first input is a negative sign and when there is a negative sign after another operator. All you need to do is add some code to handle those conditions. Let us look at some examples:

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

The problem with negatives in your approach comes from the fact that when you separate the operators from the numbers, you do not know what should come next: an operator or a number (this is a problem for all the above examples).


One solution is to keep track of the locations of all the numbers! Doing this will allow you to know exactly how many operators are in between each number and you can figure out which numbers are negative (or where you should add a zero) through that.

I have rewritten your code for this and it is pretty different but the main idea is still there. See the code below:

import re

# Takes care of applying the operation on the left number given the right number
def applyOperation(operator, left, right):
    answer = left
    if operator == '(':
        return NotImplemented
    elif operator == ')':
        return NotImplemented
    elif operator == '^':
        return NotImplemented
    elif operator == '*': answer *= int(right)
    elif operator == '/': answer /= int(right)
    elif operator == '+': answer += int(right)
    elif operator == '-': answer -= int(right)
    else:
        print("Error. Only '*', '/', '+',and '-' are allowed at the moment.")
        # You could also allow a period, exponents (^), modulo, exponential, etc.
        exit(1)
    return answer

def calc(equation):
    """
    Magical calculator (It handles negative numbers)

    DISCLAIMER: 
     -This version does not allow parentheses or float inputs (but does allow float outputs)
     -It also does not follow the order of operations
    """
    numIndices = [m.span() for m in re.finditer("\d+", equation)]
    prevNumber = 0
    prevEnd = ''

    for numIndex in numIndices:
        number = float(equation[numIndex[0]:numIndex[1]])

        # If at the start, just add the number
        if numIndex[0] == 0:
            prevNumber = number
            prevEnd = numIndex[1]
            continue

        # Starting at the second spot of the equation is special
        if numIndex[0] == 1:
            # Remember this for order of operations (if implemented)
            if equation[0] == "(":
                # I think handling these would be best done recursively
                # Or you could split on all parentheses and compute each in turn
                # Or ...
                return NotImplemented
            # It's a negative number
            elif equation[0] == "-":
                prevNumber -= number
                prevEnd = numIndex[1]
                continue
            else:
                print("Error. Only numbers, '-' and '(' are allowed at the " 
                      +"beginning of the equation.")
                # You could allow a period as well.
                exit(1)

        # If reached here, then should have passed the first number
        # Extract relevant operators and remove any spaces
        operators = equation[prevEnd:numIndex[0]]
        operators = "".join(operators.split())
        if len(operators) == 1:
            prevNumber = applyOperation(operators[0], prevNumber, number)

        elif len(operators) == 2:
            if (operators[1] == '-'):
                prevNumber = applyOperation(operators[0], prevNumber, -number)
            else:
                print("Error. Currently, the second operator must always be a '-'.")
                exit(1)

        # If it reaches here, then parentheses are probably involved 
        # or syntax is probably incorrect
        else:
            print("Error. Only two operators are currently allowed between numbers." 
                  + " The second one always being a '-'.")
            # You could allow all sorts of nesting with parentheses if you want.
            exit(1)
        prevEnd = numIndex[1]

    # Do not display the decimal places if they are all 0
    prevNumber = int(prevNumber) if prevNumber-int(prevNumber) == 0 else prevNumber
    print("Equation:", equation,"\nAnswer:",prevNumber)


while True:
    calc(input('>>> '))

Calculators are not a joke! xD

P.S. It also accepts equations of just letters.

Akaisteph7
  • 5,034
  • 2
  • 20
  • 43
  • A note: `return NotImplemented` is an error (or at least a misuse of `NotImplemented`) in any case aside from implementing operator overloads (including comparison operators and the like). It's a singleton to indicate that Python should let the other side of a binary operator take a stab at performing the operation, because the current side doesn't know how to do it. If there is an actual error, you want to raise an exception, not return marker values like `NotImplemented` (which will cause a confusing exception later on when you try to use it, given you never check the return values). – ShadowRanger Aug 02 '19 at 13:15