2

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.

desperatecoder
  • 187
  • 1
  • 3
  • 13
  • This is a very fragile way of solving that problem. You may benefit from a little more research. – Selcuk Apr 05 '19 at 02:24
  • have you tried making it handle more digits? If it's only handling one digit, which bit of it is doing the wrong thing? What would it have to do differently, to do the right thing? – TessellatingHeckler Apr 05 '19 at 02:26
  • 3
    You might want to take a look at SymPy. – fractals Apr 05 '19 at 02:34
  • @Selcuk Yeah, I know. But I don't mind if it's fragile. I need the method to handle simple functions only. – desperatecoder Apr 05 '19 at 03:06
  • "I only need a simple function that translates" – I think you drastically underestimate the difficulty and complexity of getting a computer to work with equations. You'll either need a library designed for this, or spend weeks building your own computer algebra system. – MultiplyByZer0 Apr 05 '19 at 03:08
  • 1
    If you wish to do this in a more extendible way, in other languages, in absence of eval, I've done similar things; and it basically boils down to three steps: (1) tokenize your string. (notice where operators end, where numbers start and what functions are). (2) analyze and translate list of tokens: for a calculator this can be as simple as translating to reverse polish notations using the shunting yard algorithm: but if you would ever need flow control you might wish to go for an ast. (3) actually execute, this is often a step that's messy with lots of switches. – paul23 Apr 05 '19 at 04:33

1 Answers1

1

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)
Mahmoud Elshahat
  • 1,873
  • 10
  • 24
  • 5
    Didn't downvote, but you may wish to address: [Why is using eval a bad practice?](https://stackoverflow.com/questions/1832940/why-is-using-eval-a-bad-practice) – TrebledJ Apr 05 '19 at 02:28
  • 4
    Not the downvoter, but I suspect the goal here is to actually parse and evaluate the mathematical expression as math, rather than kick in the full Python code evaluation machinery (where all sorts of non-math related actions can occur). – ShadowRanger Apr 05 '19 at 02:29
  • I remember also using eval to evaluate math expressions for a discord bot (heck, it's quick and easy to implement), but I stripped the string of any alphabetic characters before eval-ing it to prevent any injections. Not sure whether that'd be a good idea or not. – TrebledJ Apr 05 '19 at 02:34
  • @TrebledJ , @ ShadowRanger I edited the answer, thanks for clearing this out – Mahmoud Elshahat Apr 05 '19 at 02:51
  • @desperatecoder Try adding `from math import *` somewhere. – MultiplyByZer0 Apr 05 '19 at 03:09