desperatecoder
desperatecoder

Reputation: 187

Translate a string of math function to a function

I want to have a function that takes in a string of function, such as 'x**2+x' or 'log(x)', and a number as x, and return a number. For example:

translate(2, "x**2") return 4
translate(10, "x**2") return 100

This is what I've tried. However, I can only handle single digit for x.

def translate(x, function):
        func = function.replace('x', str(x))
        res = 0
        i = 0
        while i in range(len(function)):
            if func[i] == '*':
                if func[i+1] == '*':
                    res = res**int(func[i+2])
                    i+=3
                else:
                    res *= int(func[i+1])
                    i+=2
            elif func[i] == '+':
                res += int(func[i+1])
                i+=2
            elif func[i] == '-':
                res -= int(func[i+1])
                i+=2
            elif func[i] == 'l':
                res += math.log(int(func[i+3]))
                i+=4
            else:
                res += int(func[i])
                i+=1

        return res

Edit: I only need a simple function that translates since I'm not passing in crazy complicated function.

Upvotes: 2

Views: 133

Answers (1)

Mahmoud Elshahat
Mahmoud Elshahat

Reputation: 1959

Edit: it looks like there is a lot of debates regarding eval() insecure to use which should be mentioned before someone use it see this thread: Why is using 'eval' a bad practice?

Use the builtin method eval().

def translate(x, function):
        return eval(function)

result = translate(10, "x**2")

print(result)

Output: 100

Edit2: another way without eval

def translate(s):
    symbols = ['+', '-', '*', '/']
    buff = ''
    num = []
    operations = []

    for i, c in enumerate(s):
        if c in symbols:  # check for operators
            # check for double operators like **
            if s[i + 1] in symbols:  # i.e. checking the first '*' in '**'
                operations.append(2 * c)
                continue
            elif s[i - 1] in symbols:  # i.e. checking the second '*' in '**'
                num.append(float(buff))
                buff = ''
                continue

            operations.append(c)
            num.append(float(buff))
            buff = ''
            continue

        else:
            buff += c
    num.append(float(buff))

    print('input string:', s)
    print('numbers:', num)
    print('operations', operations)

    # "power calculations" to be done first
    for i, x in enumerate(operations):
        if x == '**':
            num[i] = perform[operations[i]](num[i], num[i + 1])
            num.pop(i + 1)
            operations.pop(i)
    # multiply/division
    for i, x in enumerate(operations):
        if x in ['*', '/']:
            num[i] = perform[operations[i]](num[i], num[i + 1])
            num.pop(i + 1)
            operations.pop(i)
    # last addition/subtraction
    for i, op in enumerate(operations):
        if op == '-':
            num[i + 1] = -num[i + 1]

    return sum(num)


# define all operations you need, no need to add + or -
perform = {'*': lambda x, y: x * y, '/': lambda x, y: x / y, '**': lambda x, y: x ** y }

result = translate('5+3+10**2+210-30/2')
print('result =', result)

Output:

input string: 5+3+10**2+210-30/2
numbers: [5.0, 3.0, 10.0, 2.0, 210.0, 30.0, 2.0]
operations ['+', '+', '**', '+', '-', '/']
result = 303.0

Edit3: shorter one with regex

import re

def translate(s):
    num = re.findall(r'\d+', s)  # '\d' means digits only
    operations = re.findall(r'\D+', s)  # '\D' means anything but digits

    print('input string:', s)
    print('numbers:', num)
    print('operations', operations)

    # "power calculations" to be done first
    for i, x in enumerate(operations):
        if x == '**':
            num[i] = perform[operations[i]](num[i], num[i + 1])
            num.pop(i + 1)
            operations.pop(i)
    # multiply/division
    for i, x in enumerate(operations):
        if x in ['*', '/']:
            num[i] = perform[operations[i]](num[i], num[i + 1])
            num.pop(i + 1)
            operations.pop(i)
    # last addition/subtraction
    for i, op in enumerate(operations):
        if op == '-':
            num[i + 1] = -num[i + 1]

    return sum(num)


# define all operations you need, no need to add + or -
perform = {'*': lambda x, y: x * y, '/': lambda x, y: x / y, '**': lambda x, y: x ** y }

result = translate('5+3+10**2+210-30/2')
print('result =', result)

Upvotes: 1

Related Questions