1

I am trying to make calculator that can solve expressions with basic 4 operators, like 1+2*3-4/5, however it does not work and I do not know what is wrong. Please check my code. When I run it, I am getting infinte number of errors in 8. line return ret(parts[0]) * ret(parts[2]) Here is code

def ret(s):
    s = str(s)
    if s.isdigit():
        return float(s)
    for c in ('*','/','+','-'):
        parts = s.partition(c)
        if c == '*':
            return ret(parts[0]) * ret(parts[2])
        elif c == '/':
            return ret(parts[0]) / ret(parts[2])
        elif c == '+':
            return ret(parts[0]) + ret(parts[2])
        elif c == '-':
            return ret(parts[0]) - ret(parts[2])
print(ret('1+2'))

And the error traceback ends with:

  File "C:\Python33\calc.py", line 8, in ret
    return ret(parts[0]) * ret(parts[2])
  File "C:\Python33\calc.py", line 2, in ret
    s = str(s)
RuntimeError: maximum recursion depth exceeded while calling a Python object
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
c0ntrol
  • 908
  • 2
  • 9
  • 14

9 Answers9

1

The main thing you are doing wrong is that you are checking the value of c rather that the partitioned operator. You can also unpack the result from s.partition to make things a little easier by using left and right for the actual operations.

def ret(s):
    s = str(s)
    if s.isdigit():
        return float(s)
    for c in ('-','+','*','/'):
        left, op, right = s.partition(c)
        if op == '*':
            return ret(left) * ret(right)
        elif op == '/':
            return ret(left) / ret(right)
        elif op == '+':
            return ret(left) + ret(right)
        elif op == '-':
            return ret(left) - ret(right)
print(ret('1+2'))

Also, you will need to reverse the order of your operations as you want to first do addition and subtraction, followed by multiplication and division.

What I mean is, if you have an expression like 4+4*3, you want to divide it into

ret(4) + ret(4 * 3)

Since it is a recursive call, you want the operators with the highest precedence to be the last on the call stack so they are executed first when the function returns.

As an example:

print(ret('1+2*6'))
print(ret('3*8+6/2'))

OUTPUT

13.0
27.0
sberry
  • 128,281
  • 18
  • 138
  • 165
  • But when I go through the operators the c contains same operator as parts[1] so it does not matter. – c0ntrol Feb 27 '13 at 10:23
  • @user1295618 `parts[1]` is `c` only when `s` contains `c`. – freakish Feb 27 '13 at 10:24
  • 1
    It does matter. Think about your example, '1+2'. You are first checking '*', which is not present. The result of the partition is `('1+2', '', '')`. Since `op == '*'` is true, you are getting True on the first if, and running `ret('1+2') * ret('')`. That creates a recursive call that will never end. – sberry Feb 27 '13 at 10:26
1

You partition the input string regardless, never checking if the operator is even there. .partition() returns empty strings if the partition character is not present in the input:

 >>> '1+1'.partition('*')
 ('1+1', '', '')

So you'll call s.partition('*') but never check if there is any such operator present, resulting in unconditional calls to ret(). You'll always call ret(parts[0]) * ret(parts[2]) regardless of wether * is present in s or not.

The solution is to either test for the operator first or to check the return value of .partition(). The latter is probably easiest:

for c in ('+','-','*','/'):
    parts = s.partition(c)
    if parts[1] == '*':
        return ret(parts[0]) * ret(parts[2])
    elif parts[1] == '/':
        return ret(parts[0]) / ret(parts[2])
    elif parts[1] == '+':
        return ret(parts[0]) + ret(parts[2])
    elif parts[1] == '-':
        return ret(parts[0]) - ret(parts[2])

Note that I reversed the operator order; yes, multiplication and division need to be applied before addition and subtraction, but you are working in reverse here; splitting up the expression into smaller parts, and the operations are then applied when the sub-expression has been processed.

You could use assignment unpacking to assign the 3 return values of .partition() to easier names:

for c in ('+','-','*','/'):
    left, operator, right = s.partition(c)
    if operator == '*':
        return ret(left) * ret(right)
    elif operator == '/':
        return ret(left) / ret(right)
    elif operator == '+':
        return ret(left) + ret(right)
    elif operator == '-':
        return ret(left) - ret(right)

Next you can simplify all this by using the operator module, which has functions that perform the same operations as your arithmetic operations. A map should do:

import operator
ops = {'*': operator.mul, '/': operator.div, '+': operator.add, '-': operator.sub}

for c in ('+','-','*','/'):
    left, operator, right = s.partition(c)
    if operator in ops:
        return ops[operator](ret(left), ret(right))
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • @MartijnPieters Testing this I can see that the order of operations ('+', '-', '*', '/') is significant, but I can't figure out why. Can you explain? – j b Feb 16 '20 at 13:07
  • 1
    @jb because multiplication and division bind more tightly. `42 + 17 * 13` should be evaluated as `42 + (17 * 13)`. By splitting on addition and subtraction *first* you automatically end up with the right groupings, so `ret('42') + ret('17 * 13')`. – Martijn Pieters Feb 16 '20 at 23:46
0

Your dispatching is incorrect. The way you defined your function it will always try to split by '*', which basically calls the ret function recursively with the same arguments...

You'll have to check first whether an "operator" is present in your argument string at all.

Think again!

BenMorel
  • 34,448
  • 50
  • 182
  • 322
0

In your code you have no condition on the result of s.partition(c), so even if the partition results in ('anything', '', '') you will do a recursion on the first if.

Edited

0

Here is a simple python calculator program, feel free to use it:

#Python calculator

def menu():
    print ("Welcome to calculator.py")
    print ("your options are:")
    print (" ")
    print ("1) Addition")
    print ("2) Subtraction")
    print ("3) Multiplication")
    print ("4) Division")
    print ("5) Quit calculator.py")
    print (" ")
    return input ("Choose your option: ")

def add(a,b):
    print (a, "+", b, "=", a + b)

def sub(a,b):
    print (b, "-", a, "=", b - a)

def mul(a,b):
    print (a, "*", b, "=", a * b)

def div(a,b):
    print (a, "/", b, "=", a / b)

loop = 1
choice = 0
while loop == 1:
    choice = menu()
    if choice == 1:
        add(input("Add this: "),input("to this: "))
    elif choice == 2:
        sub(input("Subtract this: "),input("from this: "))
    elif choice == 3:
        mul(input("Multiply this: "),input("by this: "))
    elif choice == 4:
        div(input("Divide this: "),input("by this: "))
    elif choice == 5:
        loop = 0

print ("Thank you for using calculator.py!")
0

how to condition users input of number 1 & number 2, if its in string. with if/else or try/except.

print("\n Select A Number \n")
print("Press 1 for Addition")
print("Press 2 for Subtraction")
print("Press 3 for multiplication")
print("Press 4 for Division \n")

while True:
    user = input("Enter A Choice(1/2/3/4): ")
    if user in ('1','2','3','4'):
        num1 = int(input("Enter First Number: "))
        num2 = int(input("Enter Second Number: "))

        if user == '1':
            print(f"{num1} + {num2} = {num1 + num2}")
            print("Thanks For Usnig This Calculator")
        elif user == '2':
            print(f"{num1} - {num2} = {num1 - num2}")
            print("Thanks For Usnig This Calculator")
        elif user == '3':
            print(f"{num1} * {num2} = {num1 * num2}")
            print("Thanks For Usnig This Calculator")
        elif user == '4':
            print(f"{num1} / {num2} = {num1 / num2}")
            print("Thanks For Usnig This Calculator")
        else:
            print("Please Enter Correct Choice")
    else:
        print("Invalid")
  • 2
    Please provide additional details in your answer. As it's currently written, it's hard to understand your solution. – Community Sep 06 '21 at 12:55
0
#1. Create a class with the name Calculator
class Calculator:
    #2. Declare two class variables num1 and num2
    num1=""
    num2=""
    result=""
    option=""
    #3. Initialize the class variables num1 and num2 in constructor __init__()
    def __init__(self):
        self.num1=0
        self.num2=0
        self.result=0
        self.option=0
    #4. Make getter and setter function for variables
    def getNum1(self):
        return self.num1
    def setNum1(self,n1):
        self.num1=n1 
    def getNum2(self):
        return self.num2
    def setNum2(self,n2):
        self.num2=n2
    def getResult(self):
        return self.result
    def setResult(self,r):
        self.result=int(r)
    def getOption(self):
        return self.option
    def setOption(self,op):
        self.option=op
    
    #5. Create Main Manu function to display choices
    def mainMenu(self):
        print("==============")
        print("==CALCULATOR==")
        print("==============")
        print("Menu:")    
        print("1. Addtion")
        print("2. Subtraction")
        print("3. Multiplication")
        print("4. Division")
        print("5. Remainer")
        print("6. Exit")
        print("")
        self.setOption(int(input("Enter Option:")))
        self.setNum1(int(input("Enter Number1:")))
        self.setNum2(int(input("Enter Number2:")))
        
    #6. Make a function Take Input
    def takeInput(self):
        if self.option==1:
            self.setResult(self.getNum1()+self.getNum2())
        elif self.option==2:
            self.setResult(self.getNum1()-self.getNum2())
        elif self.option==3:
            self.setResult(self.getNum1()*self.getNum2())
        elif self.option==4:
            self.setResult(self.getNum1()/self.getNum2())
        elif self.option==5:
            self.setResult(self.getNum1()%self.getNum2())
            
    #7. Make a display function to display the output
    def display(self):
        resultOption=""
        print("")
        if self.option==1:
            resultOption="Addition"
        elif self.option==2:
            resultOption="Subtraction"
        elif self.option==3:
            resultOption="Multiplication"
        elif self.option==4:
            resultOption="Division"
        elif self.option==5:
            resultOption="Remainder"
        if self.option==1 or self.option==2 or self.option==3 or self.option==4 or self.option==5:
            print(resultOption+":"+str(self.getResult()))
        else:
            print("Invalid Input")
        self.option=input("press x to exit, 0 to continue...")
        if self.option=="x":
            pass
        else:
            self.option=int(self.option)
            

#8. Create Object of Calculator Class  
c1=Calculator()
op=0

#9. Get option in while loop until x is pressed
while(c1.getOption()!='x'):
    #10.    Call display menu, input and display functions to display the output
    c1.mainMenu()
    c1.takeInput()
    c1.display()
Rashid
  • 11
  • 4
  • Hi and thanks for the answer. Its great that it works for you, but it would help us if you could explain what you did and how did you solve the initial problem! – Simas Joneliunas Feb 10 '22 at 05:49
  • This is simple calculator in Python. I want to show code for how we can define a class in Pyathon, how we can define a constructor and initialize the variables. How we can make user defined functions in class, how can we make an object of the class and class the functions defined in it. These are the concepts the above code has. – Rashid Feb 11 '22 at 18:21
-1

I have an alternative to your code. The user can enter stuff like: 8*6/4-3+3 and this will still work. Very compact and if you want a command to exit out of the:

while True:

Just add:

if(y == "end" or y == "End"):
    break

And it will exit out of the "while True:"

Code (Python v3.3.0):

while True:
    x = "x="
    y = input(" >> ")
    x += y
    exec(x)
    print(x)
Dark Leviathan
  • 489
  • 7
  • 19
  • Enter `exit()` at the `>>` prompt to get a sense of why this is a bad idea. It's only a small step to deleting random files through this user interface! – cdlane Sep 15 '17 at 16:34
-1

Here is simple code in python for creating a calculator same as in Python terminal.

number = input("")
print number
gre_gor
  • 6,669
  • 9
  • 47
  • 52
Mitul Panchal
  • 592
  • 5
  • 7
  • This is considered a misfeature in Python 2 (which is why everyone calls `raw_input()` instead) and simply echoes the input in Python 3. Enter `exit()` as your number in Python 2 to see why this is not a good solution. – cdlane Sep 15 '17 at 16:30
  • This is not misfeature but it will calculate using the internal function. – Mitul Panchal Dec 02 '17 at 09:51