how to convert a string to a mathematical expression programmatically
Asked Answered
M

7

7

I am a beginner at C#. I am facing a problem while converting a string to a mathematical expression. I have a UI where user can create formula using random formula field. And in another UI user will give input of those formula field.

like for example at first time the formula may be (a+b)^n and another the formula may be ((a+b+c)^n+b).

In my calculation UI for the first time user will give input for a,b,n and for 2nd formula user will give input for a,b,c,n. Can anyone please help me about how to get the result for both of the formula programmatic-ally? Thanks in advance

Maintopmast answered 13/2, 2014 at 9:58 Comment(5)
What is random formula field and another UI?Trocki
Convert it to ExpressionTreeChuvash
i meant user can create a formula randomly using random variable. @ TarecMaintopmast
how to convert to expression tree?Maintopmast
try github.com/mparlak/Flee/wiki/Getting-StartedAnastase
W
5

This is how it should be done:

public class StringToFormula
{
    private string[] _operators = { "-", "+", "/", "*","^"};
    private  Func<double, double, double>[] _operations = {
        (a1, a2) => a1 - a2,
        (a1, a2) => a1 + a2,
        (a1, a2) => a1 / a2,
        (a1, a2) => a1 * a2,
        (a1, a2) => Math.Pow(a1, a2)
    };

    public double Eval(string expression)
    {
        List<string> tokens = getTokens(expression);
        Stack<double> operandStack = new Stack<double>();
        Stack<string> operatorStack = new Stack<string>();
        int tokenIndex = 0;

        while (tokenIndex < tokens.Count) {
            string token = tokens[tokenIndex];
            if (token == "(") {
                string subExpr = getSubExpression(tokens, ref tokenIndex);
                operandStack.Push(Eval(subExpr));
                continue;
            }
            if (token == ")") {
                throw new ArgumentException("Mis-matched parentheses in expression");
            }
            //If this is an operator  
            if (Array.IndexOf(_operators, token) >= 0) {
                while (operatorStack.Count > 0 && Array.IndexOf(_operators, token) < Array.IndexOf(_operators, operatorStack.Peek())) {
                    string op = operatorStack.Pop();
                    double arg2 = operandStack.Pop();
                    double arg1 = operandStack.Pop();
                    operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
                }
                operatorStack.Push(token);
            } else {
                operandStack.Push(double.Parse(token));
            }
            tokenIndex += 1;
        }

        while (operatorStack.Count > 0) {
            string op = operatorStack.Pop();
            double arg2 = operandStack.Pop();
            double arg1 = operandStack.Pop();
            operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
        }
        return operandStack.Pop();
    }

    private string getSubExpression(List<string> tokens, ref int index)
    {
        StringBuilder subExpr = new StringBuilder();
        int parenlevels = 1;
        index += 1;
        while (index < tokens.Count && parenlevels > 0) {
            string token = tokens[index];
            if (tokens[index] == "(") {
                parenlevels += 1;
            }

            if (tokens[index] == ")") {
                parenlevels -= 1;
            }

            if (parenlevels > 0) {
                subExpr.Append(token);
            }

            index += 1;
        }

        if ((parenlevels > 0)) {
            throw new ArgumentException("Mis-matched parentheses in expression");
        }
        return subExpr.ToString();
    }

    private List<string> getTokens(string expression)
    {
        string operators = "()^*/+-";
        List<string> tokens = new List<string>();
        StringBuilder sb = new StringBuilder();

        foreach (char c in expression.Replace(" ", string.Empty)) {
            if (operators.IndexOf(c) >= 0) {
                if ((sb.Length > 0)) {
                    tokens.Add(sb.ToString());
                    sb.Length = 0;
                }
                tokens.Add(c);
            } else {
                sb.Append(c);
            }
        }

        if ((sb.Length > 0)) {
            tokens.Add(sb.ToString());
        }
        return tokens;
    }
}

Call the class and method like this:

string formula = "type your formula here"; //or get it from DB
StringToFormula stf = new StringToFormula();
double result = stf.Eval(formula);
Wizened answered 20/10, 2016 at 14:32 Comment(3)
Good effort! But it is not working as desired. For Eg. I tried (12 / 12 * (10 + 10)) / 2 and it is giving 0.025 instead of 10Lafleur
@VenugopalM I think the error comes from Line 31 where the while loop is looking for IndexOf the 'token' to be less than the IndexOf operatorStack.Peek(). This will ALWAYS evaluate something like '1/2*3' as 2 operations. The use of .Pop() (actually the use of Stacks in general) means that we're always evaluating from right-to-left. If you remove this second condition of the while loop, then the expression is evaluated correctly. The other option would be to change everything from Stack<T> to List<T> and using .RemoveAt(0) so it's evaluated left-to-right. We want FIFO not LIFO.Tody
Actually, all you have to do is make sure the precedence is the same for +- and */, then change the < to <=. That way it still does */ before doing +- (right to left), but if the precedence value is the same it evaluates left to right. See my answer posted belowSamford
T
0

There are plenty methods for formula evaluation, take a look. Just take your input, replace a, b, n chars in your string to values provided by user and resolve equation with one of methods mentioned.

Trocki answered 13/2, 2014 at 10:6 Comment(1)
Try flee for example, simple sample provided: flee.codeplex.com/…Trocki
E
0
string input= "(12 + 4 * 6) * ((2 + 3 * ( 4 + 2 ) ) ( 5 + 12 ))";       
    string str4 = "(" + input`enter code here`.Replace(" ", "") + ")";
            str4 = str4.Replace(")(", ")*(");
            while (str4.Contains('('))
            {
                string sub1 = str4.Substring(str4.LastIndexOf("(") + 1);
                string sub = sub1.Substring(0, sub1.IndexOf(")"));
                string sub2 = sub;
                string str21 = sub2.Replace("^", "~^~").Replace("/", "~/~").Replace("*", "~*~").Replace("+", "~+~").Replace("-", "~-~");
                List<string> str31 = str21.Split('~').ToList();
                while (str31.Count > 1)
                {
                    while (str31.Contains("*"))
                    {
                        for (int i = 0; i < str31.Count; i++)
                        {
                            if (str31[i] == "*")
                            {
                                val = Convert.ToDouble(str31[i - 1]) * Convert.ToDouble(str31[i + 1]);
                                str31.RemoveRange(i - 1, 3);
                                str31.Insert(i - 1, val.ToString());
                            }
                        }
                    }
                    while (str31.Contains("/"))
                    {
                        for (int i = 0; i < str31.Count; i++)
                        {
                            if (str31[i] == "/")
                            {
                                val = Convert.ToDouble(str31[i - 1]) / Convert.ToDouble(str31[i + 1]);
                                str31.RemoveRange(i - 1, 3);
                                str31.Insert(i - 1, val.ToString());
                            }
                        }
                    }
                    while (str31.Contains("+"))
                    {
                        for (int i = 0; i < str31.Count; i++)
                        {
                            if (str31[i] == "+")
                            {
                                val = Convert.ToDouble(str31[i - 1]) + Convert.ToDouble(str31[i + 1]);
                                str31.RemoveRange(i - 1, 3);
                                str31.Insert(i - 1, val.ToString());
                            }
                        }
                    }
                    while (str31.Contains("-"))
                    {
                        for (int i = 0; i < str31.Count; i++)
                        {
                            if (str31[i] == "-")
                            {
                                val = Convert.ToDouble(str31[i - 1]) - Convert.ToDouble(str31[i + 1]);
                                str31.RemoveRange(i - 1, 3);
                                str31.Insert(i - 1, val.ToString());
                            }
                        }
                    }
                }
                str4 = str4.Replace("(" + sub + ")", str31[0].ToString());
            }

            string sum = str4;
Emotional answered 13/2, 2014 at 13:46 Comment(2)
this is kind of simple parser. you can enhance it more. below are few related links... dreamincode.net/forums/topic/… mathosparser.codeplex.com codeproject.com/Articles/274093/Math-Parser-NET codeproject.com/Articles/88435/… mathparsers.com/math-parser-for-csharp-dotnetEmotional
it will work for expression containing add, subtract, multiplication and division and for more you can enhance it...Emotional
I
0

The most structural approach which comes to my mind would be to define a grammar consisting out of operator symbols (which are apparently +,-,*,/ and ^ in your case) and operands; then, if a derivation of the input in the defined grammar exists, the derivation basically is the expression tree which can then be traversed recursively while the operators are directry translated to the actual operations. I admit that the description is a bit vague, but good parsing can be a bit difficult. Perhaps a look at LL parser can help a bit.

Inexorable answered 14/3, 2014 at 14:27 Comment(0)
W
0

i think this is the solution

 Expression e = new Expression("((a+b+c)^n+b)");
 e.Evaluate();
Wizened answered 20/10, 2016 at 14:25 Comment(1)
Yes it works If ncalc Nuget package is installedCuda
R
0

convert String To Mathematical Expression

var s3 = "3 - 4 + 5 * 9"

var s4 = NSExpression(format: s3).expressionValue(with: nil, context: nil) as! Double // 44.0

Answer : 44

Rajab answered 19/12, 2021 at 18:6 Comment(0)
S
0

Old post, but I liked the solution source posted and thought I'd post a fixed version. It uses the source posted above, but now correctly calculates the result with operator precedence and negative numbers. Eg. 10 + 12*10 - -47 % 30.0 = 147 or -2 + -4 * (-2 - -2*10) = -74

public static class StringToFormula
{
    private static readonly string[] operators = { "+", "-", "/", "%", "*", "^" };
    private static readonly Func<double, double, double>[] operations = {
        (a1, a2) => a1 + a2,
        (a1, a2) => a1 - a2,
        (a1, a2) => a1 / a2,
        (a1, a2) => a1 % a2,
        (a1, a2) => a1 * a2,
        (a1, a2) => Math.Pow(a1, a2)
    };

    public static bool TryEval(string expression, out double value)
    {
        try
        {
            value = Eval(expression);
            return true;
        }
        catch
        {
            value = 0.0;
            return false;
        }
    }

    public static double Eval(string expression)
    {
        if (string.IsNullOrEmpty(expression))
            return 0.0;

        if (double.TryParse(expression, NumberStyles.Any, CultureInfo.InvariantCulture, out double value))
            return value;

        List<string> tokens = GetTokens(expression);
       tokens.Add("$"); // Append end of expression token
        Stack<double> operandStack = new Stack<double>();
        Stack<string> operatorStack = new Stack<string>();
        int tokenIndex = 0;

        while (tokenIndex < tokens.Count - 1)
        {
            string token = tokens[tokenIndex];
            string nextToken = tokens[tokenIndex + 1];
            
            switch (token)
            {
                case "(":
                {
                    string subExpr = GetSubExpression(tokens, ref tokenIndex);
                    operandStack.Push(Eval(subExpr));
                    continue;
                }
                case ")":
                    throw new ArgumentException("Mis-matched parentheses in expression");
                
                // Handle unary ops
                case "-":
                case "+":
                {
                    if (!IsOperator(nextToken) && operatorStack.Count == operandStack.Count)
                    {
                        operandStack.Push(double.Parse($"{token}{nextToken}", CultureInfo.InvariantCulture));
                        tokenIndex += 2;
                        continue;
                    }
                }
                break;
            }
            
            if (IsOperator(token))
            {
                while (operatorStack.Count > 0 && OperatorPrecedence(token) <= OperatorPrecedence(operatorStack.Peek()))
                {
                    if (!ResolveOperation()) {
                        throw new ArgumentException(BuildOpError());
                    }
                }
                operatorStack.Push(token);
            }
            else
            {
                operandStack.Push(double.Parse(token, CultureInfo.InvariantCulture));
            }
            tokenIndex += 1;
        }

        while (operatorStack.Count > 0)
        {
            if (!ResolveOperation())
                throw new ArgumentException(BuildOpError());
        }

        return operandStack.Pop();

        bool IsOperator(string token)
        {
            return Array.IndexOf(operators, token) >= 0;
        }
        int OperatorPrecedence(string op)
        {
            switch (op)
            {
            case "^":
                return 3;
            case "*":
            case "/":
            case "%":
                return 2;
                
            case "+":
            case "-":
                return 1;
            default:
                return 0;
            }
        }
        
        string BuildOpError() {
            string op = operatorStack.Pop();
            string rhs = operandStack.Any() ? operandStack.Pop().ToString() : "null";
            string lhs = operandStack.Any() ? operandStack.Pop().ToString() : "null";
            return $"Operation not supported: {lhs} {op} {rhs}";
        }
       
        bool ResolveOperation()
        {
            if (operandStack.Count < 2)
            {
                return false;
            }
            
            string op = operatorStack.Pop();
            double rhs = operandStack.Pop();
            double lhs = operandStack.Pop();
            operandStack.Push(operations[Array.IndexOf(operators, op)](lhs, rhs));
            Console.WriteLine($"Resolve {lhs} {op} {rhs} = {operandStack.Peek()}");
            return true;
        }
    }

    private static string GetSubExpression(List<string> tokens, ref int index)
    {
        StringBuilder subExpr = new StringBuilder();
        int parenlevels = 1;
        index += 1;
        while (index < tokens.Count && parenlevels > 0)
        {
            string token = tokens[index];
            switch (token) {
                case "(": parenlevels += 1; break;
                case ")": parenlevels -= 1; break;
            }
            
            if (parenlevels > 0)
                subExpr.Append(token);
            
            index += 1;
        }

        if (parenlevels > 0)
            throw new ArgumentException("Mis-matched parentheses in expression");
        
        return subExpr.ToString();
    }

    private static List<string> GetTokens(string expression)
    {
        string operators = "()^*/%+-";
        List<string> tokens = new List<string>();
        StringBuilder sb = new StringBuilder();

        foreach (char c in expression.Replace(" ", string.Empty))
        {
            if (operators.IndexOf(c) >= 0)
            {
                if ((sb.Length > 0))
                {
                    tokens.Add(sb.ToString());
                    sb.Length = 0;
                }
                tokens.Add(c.ToString());
            }
            else
            {
                sb.Append(c);
            }
        }

        if ((sb.Length > 0))
        {
            tokens.Add(sb.ToString());
        }
        return tokens;
    }
}
Samford answered 2/11, 2023 at 12:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.