pythonmath

Solving math in python without any built in eval functions or external libraries


I need to code a program that can get values from strings like this

abs(add(add(9465,38),multiply(add(63303,146),46)))

The format works as, add(x,y) = x+y, sub(x,y) = x-y, multiply(x,y) = x*y, abs returns absolute value but that is simpler.

I am confused at first how to break the string and then after that how I am going to break at parenthesis pairs to follow order of operations.


Solution

  • Interesting problem, here is a potential solution. No doubt you could make a much more elegant solution using libraries or lambdas etc. as thefourtheye did in their answer, but this seems to work.

    I have done a few test cases at the bottom, set the global verbose to True if you want debug info:

    # globals
    verbose = False # set to True if you want debug info printed
    max_iter = 1000 # this stops infinate loops incase the code does not allow for some input
    
    def solve(problem_str):
    
        def multiply(arg_list):
            x = 1
            for i in arg_list:
                x *= i
            return x
    
        def find_innermost(x_str):
            a, b, c, i = [0], [0], 0, 0
            while True:
                i += 1
                start = a[-1]+1
                a.append(x_str.find('(', start)) # find next (
                b.append(x_str.find(',', start)) # find next ,
                c = x_str.find(')', start)       # find next )
                if (a[-1] > c) or (a[-1] == -1):
                    if (b[-2] > a[-3]) and (b[-2] < a[-2]):
                        return x_str[b[-2]+1:c+1]
                    else:
                        return x_str[a[-3]+1:c+1] 
                if i >= max_iter:
                    raise Exception("Infinite loop")
    
        def do_sum(x_str):
            args = [int(x) for x in x_str[x_str.find('(')+1:x_str.find(')')].split(',')]
            task = x_str[:3].lower()
            if task == 'add':
                return sum(args)
            elif task == 'sub':
                return args[0] - sum(args[1:])
            elif task == 'abs':
                return abs(args.pop())
            elif task == 'mul':
                return multiply(args)
            else:
                print x_str + ': Task not recognised, please modify program or input'
                raise Exception("Invalid input")
    
        i = 0
        while True:
            i += 1
            if verbose: print 'debug: problem_str:', problem_str
            if problem_str.count('(') > 1:
                x_str = find_innermost(problem_str)
            else:
                x_str = problem_str
            if verbose: print '.'*6, 'x_str:\t', x_str
            x = do_sum(x_str)
            if verbose: print '.'*6, 'x:\t', x, '\n'
            problem_str = problem_str.replace(x_str, str(x))
            if problem_str.count('(') == 0:
                return int(problem_str)
            if i >= max_iter:
                raise Exception("Infinite loop")
    
    if __name__ == '__main__':
        p1 = 'abs(add(add(9465,38),multiply(add(63303,146),46)))'
        p2 = 'abs(add(multiply(95,multiply(-1,multiply(13,18875))),multiply(-1,add(18293,26))))'
        p3 = 'abs(add(subtract(add(add(151,26875),122),254),subtract(237,multiply(-1,56497))))'
        r1, r2, r3 = solve(p1), solve(p2), solve(p3)
        print 'p1 evaluates to:', r1
        print 'p2 evaluates to:', r2
        print 'p3 evaluates to:', r3
    

    Let me know if you have any questions about the code.