pythonmathalgebra

Is there a way to extract a variable from an equation?


I am trying to make an algebraic calculator using python, with an input like so:

eg1. x=1, x+5
eg2. x=6+12

resulting in output:

eg1. 6
eg2. x=18

So far my code looks like so:

def algebraic_calculator():
    variables = {}
    while True:
        user_input = input("Enter expression (or 'quit' to exit): ").strip()
        if user_input.lower() == 'quit':
            break
        try:
            if '=' in user_input:
                # Handle assignment like x=1 or x=6+12
                var, expr = user_input.split('=')
                var = var.strip()
                expr = expr.strip()
                variables[var] = eval(expr, {}, variables)
                print(f"{var} = {variables[var]}")
            else:
                # Evaluate expression using stored variables
                result = eval(user_input, {}, variables)
                print(f"Result: {result}")
        except Exception as e:
            print(f"Error: {e}")

algebraic_calculator()

This is a sample interaction:

Enter expression (or 'quit' to exit): x=1, x+1
Error: name 'x' is not defined

How would I fix it to properly accept equations in the previously mentioned format (eg1, eg2) and provide a suitable response?


Solution

  • Here is your solution:

    At the time of developing the code, there was no specified requirement for handling user input (this requirement was introduced later). Consequently, I initially used eval() due to its performance advantages. However, eval() is not suitable for processing external input safely, I have since replaced it with sympify() for improved security and symbolic expression handling.

    from sympy import symbols, sympify
    import re
    
    def algebraic_calculator(input_str):
        context = {}
        expressions = input_str.split(',')
    
        for expr in expressions:
            expr = expr.strip()
            assign_match = re.match(r'^([a-zA-Z_]\w*)\s*=\s*(.+)$', expr)
            if assign_match:
                var_name, value_expr = assign_match.groups()
                var = symbols(var_name)
                try:
                    value = sympify(value_expr, locals=context)
                    context[var_name] = value
                    print(f"{var_name} = {value}")
                except Exception as e:
                    print(f"Error parsing assignment '{expr}': {e}")
            else:
                try:
                    result = sympify(expr, locals=context)
                    print(f"{expr} = {result}")
                except Exception as e:
                    print(f"Error parsing expression '{expr}': {e}")
    
    algebraic_calculator("x=1, x+5")
    algebraic_calculator("x=6+12")
    

    The algebraic_calculator function leverages the sympy library to parse and compute algebraic assignments and expressions. It accepts a string of comma-separated inputs, such as variable assignments (e.g., x = 6 + 12) or expressions (e.g., x + 5), and processes each one sequentially. Variable assignments are identified using regular expressions, then converted into symbolic form using sympy.symbols() and evaluated with sympify(), which also supports using previously defined variables. Non-assignment expressions are similarly evaluated using sympify() in the context of the current variable environment. This approach enables reliable and secure algebraic manipulation, making it particularly suitable for symbolic mathematics tasks while avoiding the risks associated with Python's native eval() function.

    For detailed information on the sympify function, please refer to: https://www.tutorialspoint.com/sympy/sympy_sympify_function.htm

    For additional details on the symbols function, kindly see: https://www.tutorialspoint.com/sympy/sympy_symbols.htm

    sympify is much safer than Python’s built-in eval(), but it is not completely risk-free in all scenarios. For performance considerations, the following code may be appropriate, particularly in scenarios where security is not a concern and no user input is involved.

    import re
    
    def algebraic_calculator(input_str):
        variables = {}
        expressions = input_str.split(',')
    
        for expr in expressions:
            expr = expr.strip()
            assign_match = re.match(r'^([a-zA-Z_]\w*)\s*=\s*(.+)$', expr)
            if assign_match:
                var, value_expr = assign_match.groups()
                try:
                    value = eval(value_expr, {}, variables)
                    variables[var] = value
                    print(f"{var} = {value}")
                except Exception as e:
                    print(f"Error evaluating assignment '{expr}': {e}")
            else:
                try:
                    result = eval(expr, {}, variables)
                    print(f"{expr} = {result}")
                except Exception as e:
                    print(f"Error evaluating expression '{expr}': {e}")
    
    algebraic_calculator("x=1, x+5")
    algebraic_calculator("x=6+12")
    

    Output:

    x = 1
    x+5 = 6
    x = 18