• .net表达式计算器(中缀表达式转后缀表达式,支持20多个数学函数,支持函数嵌套)


    最近在网上查了一下表达工计算器的类库,发现Java版本的有一个比较成熟的叫W3EVal,好像是一个IBM工程师写的,.net就很少了(可能是我了解不够多),但投机取巧的实现思路有很多,比如:
      (1)将Javasript中代码编译成.net类库,利用Javascript中的eval函数来实现;
      (2)利用ScriptControl执行JavaScript脚本实现;
      (3)利用DataTable的计算功能实现简单计算;
      (4)利用.net动态编译功能来实现等
      这些方法在csdn的坛里有讨论,请见:http://topic.csdn.net/u/20070301/13/c8c33bd1-f146-4b44-9882-aab6d430f724.html

      心想既然还没有成熟的.net类库,何不自己做一个呢,其实并不难,只要稍有点耐心调试一下就好了。于是参考一编中缀表达式转后缀表达式的论文,很快写了一个,发现效果不错,所以发出来跟大家共享,希望对大家有用。

      中缀表达式转后缀表达式的步骤为:
      1.新建一个Stack栈,用来存放运算符
      2.新建一个post栈,用来存放最后的后缀表达式
      3.从左到右扫描中缀表达式:
        (1)若读到的是操作数,直接存入post栈,以#作为数字的结束
        (2)若读到的是(,则直接存入stack栈
        (3)若读到的是),则将stack栈中(前的所有运算符出栈,存入post栈
        (4)若读到的是其它运算符,则将该运算符和stack栈顶运算符作比较:若高于或等于栈顶运算符,则直接存入stack栈,否则将栈顶运算符(所有优先级高于读到的运算符的,不包括括号)出栈,存入post栈。最后将读到的运算符入栈。
      4.当扫描完后,stack栈中还在运算符时,则将所有的运算符出栈,存入post栈
                 

      计算后缀表达式的值的步骤为:
      1.初始化一个空堆栈
      2.从左到右读入后缀表达式
      3.如果字符是一个操作数,把它压入堆栈。
      4.如果字符是个操作符,弹出两个操作数,执行恰当操作,然后把结果压入堆栈。如果您不能够弹出两个操作数,后缀表达式的语法就不正确。
      5.到后缀表达式末尾,从堆栈中弹出结果。若后缀表达式格式正确,那么堆栈应该为空。
     

      类库的名称就名为NEval ,感觉还比较健壮,速度很快,支持20个数学函数,很容易扩充。目前还没有表达式合法性检查的功能,有兴趣的朋友可以扩充一下,先谢谢。
      DevExpress 控件 ;

      代码如下:

    代码
    /// <summary>
        
    /// 表达式计算类。支持数学函数,支持函数嵌套
        
    /// 作者watsonyin
        
    /// 开发日期:2010年10月 版本1.0
        
    /// </summary>
        public class NEval
        {
            
    public NEval()
            {

            }

            
    public double Eval(string expr)
            {
                
    try
                {
                    
    string tmpexpr = expr.ToLower().Trim().Replace(" "string.Empty);
                    
    return Calc_Internal(tmpexpr);
                }
                
    catch (ExpressionException eex)
                {
                    
    throw eex;
                }
                
    catch
                {
                    
    throw new Exception("表达式错误");
                }
            }

            
    private Random m_Random = null;
            
    private double Calc_Internal(string expr)
            {
                
    /*
                 * 1.    初始化一个空堆栈 
                 * 2.    从左到右读入后缀表达式 
                 * 3.    如果字符是一个操作数,把它压入堆栈。 
                 * 4.    如果字符是个操作符,弹出两个操作数,执行恰当操作,然后把结果压入堆栈。如果您不能够弹出两个操作数,后缀表达式的语法就不正确。 
                 * 5.    到后缀表达式末尾,从堆栈中弹出结果。若后缀表达式格式正确,那么堆栈应该为空。
                
    */

                Stack post2 = ConvertExprBack(expr);
                Stack post = new Stack();
                
    while (post2.Count > 0)
                    post.Push(post2.Pop());

                Stack stack = new Stack();
                
    while (post.Count > 0)
                {
                    
    string tmpstr = post.Pop().ToString();
                    
    char c = tmpstr[0];
                    LetterType lt = JudgeLetterType(tmpstr);
                    
    if (lt == LetterType.Number)
                    {
                        stack.Push(tmpstr);
                    }
                    
    else if (lt == LetterType.SimpleOperator)
                    {
                        
    double d1 = double.Parse(stack.Pop().ToString());
                        
    double d2 = double.Parse(stack.Pop().ToString());
                        
    double r = 0;
                        
    if (c == '+')
                            r = d2 + d1;
                        
    else if (c == '-')
                            r = d2 - d1;
                        
    else if (c == '*')
                            r = d2 * d1;
                        
    else if (c == '/')
                            r = d2 / d1;
                        
    else if (c == '^')
                            r = Math.Pow(d2, d1);
                        
    else
                            
    throw new Exception("不支持操作符:" + c.ToString());
                        stack.Push(r);
                    }
                    
    else if (lt == LetterType.Function)  //如果是函数
                    {
                        
    string[] p;
                        
    double d = 0;
                        
    double d1 = 0;
                        
    double d2 = 0;
                        
    int tmpos = tmpstr.IndexOf('(');
                        
    string funcName = tmpstr.Substring(0, tmpos);
                        
    switch (funcName)
                        {
                            
    case "asin":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Asin(d).ToString());
                                
    break;
                            
    case "acos":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Acos(d).ToString());
                                
    break;
                            
    case "atan":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Atan(d).ToString());
                                
    break;
                            
    case "acot":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push((1 / Math.Atan(d)).ToString());
                                
    break;
                            
    case "sin":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Sin(d).ToString());
                                
    break;
                            
    case "cos":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Cos(d).ToString());
                                
    break;
                            
    case "tan":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Tan(d).ToString());
                                
    break;
                            
    case "cot":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push((1 / Math.Tan(d)).ToString());
                                
    break;
                            
    case "log":
                                SplitFuncStr(tmpstr, 2out p);
                                d1 = double.Parse(p[0]);
                                d2 = double.Parse(p[1]);
                                stack.Push(Math.Log(d1, d2).ToString());
                                
    break;
                            
    case "ln":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Log(d, Math.E).ToString());
                                
    break;
                            
    case "abs":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Abs(d).ToString());
                                
    break;
                            
    case "round":
                                SplitFuncStr(tmpstr, 2out p);
                                d1 = double.Parse(p[0]);
                                d2 = double.Parse(p[1]);
                                stack.Push(Math.Round(d1, (int)d2).ToString());
                                
    break;
                            
    case "int":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push((int)d);
                                
    break;
                            
    case "trunc":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Truncate(d).ToString());
                                
    break;
                            
    case "floor":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Floor(d).ToString());
                                
    break;
                            
    case "ceil":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Ceiling(d).ToString());
                                
    break;
                            
    case "random":
                                
    if (m_Random == null)
                                    m_Random = new Random();
                                d = m_Random.NextDouble();
                                stack.Push(d.ToString());
                                
    break;
                            
    case "exp":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Exp(d).ToString());
                                
    break;
                            
    case "pow":
                                SplitFuncStr(tmpstr, 2out p);
                                d1 = double.Parse(p[0]);
                                d2 = double.Parse(p[1]);
                                stack.Push(Math.Pow(d1, d2).ToString());
                                
    break;
                            
    case "sqrt":
                                SplitFuncStr(tmpstr, 1out p);
                                d = double.Parse(p[0]);
                                stack.Push(Math.Sqrt(d).ToString());
                                
    break;
                            
    default:
                                
    throw new Exception("未定义的函数:" + funcName);

                        }

                    }
                }
                
    object obj = stack.Pop();
                
    return double.Parse(obj.ToString());
            }

            
    /// <summary>
            
    /// 将函数括号内的字符串进行分割,获得参数列表,如果参数是嵌套的函数,用递归法计算得到它的值
            
    /// </summary>
            
    /// <param name="funcstr"></param>
            
    /// <param name="paramCount"></param>
            
    /// <param name="parameters"></param>
            private void SplitFuncStr(string funcstr, int paramCount, out string[] parameters)
            {
                parameters = new string[paramCount];
                
    int tmpPos = funcstr.IndexOf('('0);
                
    string str = funcstr.Substring(tmpPos + 1, funcstr.Length - tmpPos - 2);
                
    if (paramCount == 1)
                {
                    parameters[0= str;
                }
                
    else
                {
                    
    int cpnum = 0;
                    
    int startPos = 0;
                    
    int paramIndex = 0;
                    
    for (int i = 0; i <= str.Length - 1; i++)
                    {
                        
    if (str[i] == '(')
                            cpnum++;
                        
    else if (str[i] == ')')
                            cpnum--;
                        
    else if (str[i] == ',')
                        {
                            
    if (cpnum == 0)
                            {
                                
    string tmpstr = str.Substring(startPos, i - startPos);
                                parameters[paramIndex] = tmpstr;
                                paramIndex++;
                                startPos = i + 1;
                            }
                        }
                    }
                    
    if (startPos < str.Length)
                    {
                        
    string tmpstr = str.Substring(startPos);
                        parameters[paramIndex] = tmpstr;
                    }
                }

                
    //如果参数是函数, 进一步采用递归的方法生成函数值
                for (int i = 0; i <= paramCount - 1; i++)
                {
                    
    double d;
                    
    if (!double.TryParse(parameters[i], out d))
                    {
                        NEval calc = new NEval();
                        d = calc.Eval(parameters[i]);
                        parameters[i] = d.ToString();
                    }
                }
            }


            
    /// <summary>
            
    /// 将中缀表达式转为后缀表达式
            
    /// </summary>
            
    /// <param name="expr"></param>
            
    /// <returns></returns>
            private Stack ConvertExprBack(string expr)
            {
                
    /*
                 * 新建一个Stack栈,用来存放运算符
                 * 新建一个post栈,用来存放最后的后缀表达式
                 * 从左到右扫描中缀表达式:
                 * 1.若读到的是操作数,直接存入post栈,以#作为数字的结束
                 * 2、若读到的是(,则直接存入stack栈
                 * 3.若读到的是),则将stack栈中(前的所有运算符出栈,存入post栈
                 * 4 若读到的是其它运算符,则将该运算符和stack栈顶运算符作比较:若高于或等于栈顶运算符, 则直接存入stack栈,
                 * 否则将栈顶运算符(所有优先级高于读到的运算符的,不包括括号)出栈,存入post栈。最后将读到的运算符入栈
                 * 当扫描完后,stack栈中还在运算符时,则将所有的运算符出栈,存入post栈
                 * */


                Stack post = new Stack();
                Stack stack = new Stack();
                
    string tmpstr;
                
    int pos;
                
    for (int i = 0; i <= expr.Length - 1; i++)
                {
                    
    char c = expr[i];
                    LetterType lt = JudgeLetterType(c, expr, i);

                    
    if (lt == LetterType.Number)  //操作数
                    {
                        GetCompleteNumber(expr, i, out tmpstr, out pos);
                        post.Push(tmpstr);
                        i = pos;// +1;
                    }
                    
    else if (lt == LetterType.OpeningParenthesis) //左括号(
                    {
                        stack.Push(c);
                    }
                    
    else if (lt == LetterType.ClosingParenthesis) //右括号)
                    {
                        
    while (stack.Count > 0)
                        {
                            
    if (stack.Peek().ToString() == "(")
                            {
                                stack.Pop();
                                
    break;
                            }
                            
    else
                                post.Push(stack.Pop());
                        }
                    }
                    
    else if (lt == LetterType.SimpleOperator)  //其它运算符
                    {
                        
    if (stack.Count == 0)
                            stack.Push(c);
                        
    else
                        {

                            
    char tmpop = (char)stack.Peek();
                            
    if (tmpop == '(')
                            {
                                stack.Push(c);
                            }
                            
    else
                            {
                                
    if (GetPriority(c) >= GetPriority(tmpop))
                                {
                                    stack.Push(c);
                                }
                                
    else
                                {
                                    
    while (stack.Count > 0)
                                    {
                                        
    object tmpobj = stack.Peek();
                                        
    if (GetPriority((char)tmpobj) > GetPriority(c))
                                        {
                                            
    if (tmpobj.ToString() != "(")
                                                post.Push(stack.Pop());
                                            
    else
                                                
    break;
                                        }
                                        
    else
                                            
    break;
                                    }
                                    stack.Push(c);
                                }
                            }


                        }
                    }
                    
    else if (lt == LetterType.Function)  //如果是一个函数,则完整取取出函数,当作一个操作数处理
                    {
                        GetCompleteFunction(expr, i, out tmpstr, out pos);
                        post.Push(tmpstr);
                        i = pos;// +1;
                    }

                }
                
    while (stack.Count > 0)
                {
                    post.Push(stack.Pop());
                }

                
    return post;
            }


            
    private LetterType JudgeLetterType(char c, string expr, int pos)
            {
                
    string op = "*/^";
                
    if ((c <= '9' && c >= '0'|| (c == '.'))  //操作数
                {
                    
    return LetterType.Number;
                }
                
    else if (c == '(')
                {
                    
    return LetterType.OpeningParenthesis;
                }
                
    else if (c == ')')
                {
                    
    return LetterType.ClosingParenthesis;
                }
                
    else if (op.IndexOf(c) >= 0)
                {
                    
    return LetterType.SimpleOperator;
                }
                
    else if ((c == '-'|| (c == '+'))//要判断是减号还是负数
                {
                    
    if (pos == 0)
                        
    return LetterType.Number;
                    
    else
                    {
                        
    char tmpc = expr[pos - 1];
                        
    if (tmpc <= '9' && tmpc >= '0')  //如果前面一位是操作数
                            return LetterType.SimpleOperator;
                        
    else if (tmpc == ')')
                            
    return LetterType.SimpleOperator;
                        
    else
                            
    return LetterType.Number;
                    }
                }
                
    else
                    
    return LetterType.Function;
            }

            
    private LetterType JudgeLetterType(char c)
            {
                
    string op = "+-*/^";
                
    if ((c <= '9' && c >= '0'|| (c == '.'))  //操作数
                {
                    
    return LetterType.Number;
                }
                
    else if (c == '(')
                {
                    
    return LetterType.OpeningParenthesis;
                }
                
    else if (c == ')')
                {
                    
    return LetterType.ClosingParenthesis;
                }
                
    else if (op.IndexOf(c) >= 0)
                {
                    
    return LetterType.SimpleOperator;
                }
                
    else
                    
    return LetterType.Function;
            }

            
    private LetterType JudgeLetterType(string s)
            {
                
    char c = s[0];
                
    if ((c == '-'|| (c == '+'))
                {
                    
    if (s.Length > 1)
                        
    return LetterType.Number;
                    
    else
                        
    return LetterType.SimpleOperator;
                }

                
    string op = "+-*/^";
                
    if ((c <= '9' && c >= '0'|| (c == '.'))  //操作数
                {
                    
    return LetterType.Number;
                }
                
    else if (c == '(')
                {
                    
    return LetterType.OpeningParenthesis;
                }
                
    else if (c == ')')
                {
                    
    return LetterType.ClosingParenthesis;
                }
                
    else if (op.IndexOf(c) >= 0)
                {
                    
    return LetterType.SimpleOperator;
                }
                
    else
                    
    return LetterType.Function;
            }

            
    /// <summary>
            
    /// 计算操作符的优先级
            
    /// </summary>
            
    /// <param name="c"></param>
            
    /// <returns></returns>
            private int GetPriority(char c)
            {
                
    if (c == '+' || c == '-')
                    
    return 0;
                
    else if (c == '*')
                    
    return 1;
                
    else if (c == '/')  //除号优先级要设得比乘号高,否则分母可能会被先运算掉
                    return 2;
                
    else
                    
    return 2;
            }

            
    /// <summary>
            
    /// 获取完整的函数表达式
            
    /// </summary>
            
    /// <param name="expr"></param>
            
    /// <param name="startPos"></param>
            
    /// <param name="funcStr"></param>
            
    /// <param name="endPos"></param>
            private void GetCompleteFunction(string expr, int startPos, out string funcStr, out int endPos)
            {
                
    int cpnum = 0;
                
    for (int i = startPos; i <= expr.Length - 1; i++)
                {
                    
    char c = expr[i];
                    LetterType lt = JudgeLetterType(c);
                    
    if (lt == LetterType.OpeningParenthesis)
                        cpnum++;
                    
    else if (lt == LetterType.ClosingParenthesis)
                    {
                        cpnum--;//考虑到函数嵌套的情况,消除掉内部括号
                        if (cpnum == 0)
                        {
                            endPos = i;
                            funcStr = expr.Substring(startPos, endPos - startPos + 1);
                            
    return;
                        }


                    }

                }
                funcStr = "";
                endPos = -1;
            }

            
    /// <summary>
            
    /// 获取到完整的数字
            
    /// </summary>
            
    /// <param name="expr"></param>
            
    /// <param name="startPos"></param>
            
    /// <param name="numberStr"></param>
            
    /// <param name="endPos"></param>
            private void GetCompleteNumber(string expr, int startPos, out string numberStr, out int endPos)
            {
                
    char c = expr[startPos];
                
    for (int i = startPos + 1; i <= expr.Length - 1; i++)
                {
                    
    char tmpc = expr[i];
                    
    if (JudgeLetterType(tmpc) != LetterType.Number)
                    {
                        endPos = i - 1;
                        numberStr = expr.Substring(startPos, endPos - startPos + 1);
                        
    return;
                    }
                }
                numberStr = expr.Substring(startPos);
                endPos = expr.Length - 1;
            }        
        }


        
    /// <summary>
        
    /// 可以检测到的表达式错误的Exception
        
    /// </summary>
        public class ExpressionException : Exception
        {
            
    public override string Message
            {
                
    get
                {
                    
    return base.Message;
                }
            }
        }

        
    /// <summary>
        
    /// 字符类别
        
    /// </summary>
        public enum LetterType
        {
            Number,
            SimpleOperator,
            Function,
            OpeningParenthesis,
            ClosingParenthesis
        }


     

      调用代码如下:

      string expr = txtExpression.Text;
     

      NEval neval = new NEval();
           return neval.Eval(expr); 

  • 相关阅读:
    Introduce myself
    二叉搜索树的后序遍历序列(剑指offer-23)
    从上到下打印二叉树(剑指offer-22)
    二叉树的深度(剑指offer-38)
    不用加减乘除做加法(剑指offer-48)
    栈的压入、弹出序列(剑指offer-21)
    Java容器
    包含min函数的栈(剑指offer-20)
    在Docker中创建Mongodb数据库
    词向量聚类实验
  • 原文地址:https://www.cnblogs.com/wwwzzg168/p/4111345.html
Copyright © 2020-2023  润新知