博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
表达式计算器的实现
阅读量:5745 次
发布时间:2019-06-18

本文共 30364 字,大约阅读时间需要 101 分钟。

一、前言

    关于表达式计算器的实现,在这里分享一下我的思路,也希望大家提出一些改进建议。

 

二、实现表达式计算的主要思路。

    1、使用的数据结构。

     以前的版本实现表达式计算用的是二叉树数据结构,二叉树有两个子节点,最多支持双目运算符或者带两个参数的函数,可是如果函数的参数很多,就不好处理了,所以当前的版本,用的数据结构是动态数组,实现原理就是先把字符串表达式转换成动态数组,数组中存储运算符、参与运算的数、括号等。这样运算符或者函数的参数个数就不受限制,可以支持更多类型的运算符。

 

    2、对运算符进行分类。

    对运算符的分类处理是该程序的一个重要思路,运算符虽然有很多,但是可以将运算符归类,针对每一类运算符分别处理,这样核心算法就不会涉及到具体的运算符,只会涉及到运算符的类别。这样以后如果要扩展更多的运算符就会很方便,不需要再修改核心算法的代码,只需要修改运算符集合类就可以了。

     用一个枚举类表示运算符的种类,代码如下:

///     /// 运算符类型    ///     enum OpeType    {        ///         /// 该运算因子不是运算符        ///         notOperator,        ///         /// 常数或变量(没有参数的运算符)        ///         noParameter,        ///         /// '+'或'-'        /// 这两个运算符即可以当正负号使用又可以当加减运算符使用        ///         PlusOrMinus,        ///         /// 参数全在左边的运算符        ///         left,        ///         /// 参数全在右边的运算符        ///         right,        ///         /// 左边和右边都有参数的运算符        ///         bothSides,        ///         /// 左括号        ///         leftParenthesis,        ///         /// 右括号        ///         rightParenthesis,        ///         /// 分隔符,这里指逗号        ///         Separator    }
View Code

 

    3、对表达式的进行合法性检查的思路。

    对表达式的合法性检查,我没有学过编译原理,不懂什么是文法分析、词法分析。这个程序检查表达式的合法性的原理是:针对每一类运算符,分别进行判断,检查其参数的个数,参数是在右边还是在左边等等,主要代码如下:

#region 第四次处理            //第四次处理,判断表达式是否合法,并在合适的地方插入乘号            for (int i = 0; i < valList.Count - 1; i++)            {                #region 当前运算因子是数字                if (valList[i].type == ValType.Number)//数字                {                    if (valList[i + 1].type == ValType.Operator)//右边是运算符                    {                        switch (valList[i + 1].OpeType)                        {                            case OpeType.bothSides://正确                                break;                            case OpeType.left://正确                                break;                            case OpeType.leftParenthesis:                                Insert(opeList.GetOpe("*"), i + 1);                                break;                            case OpeType.noParameter:                                Insert(opeList.GetOpe("*"), i + 1);                                break;                            case OpeType.PlusOrMinus://正确                                break;                            case OpeType.right:                                Insert(opeList.GetOpe("*"), i + 1);                                break;                            case OpeType.rightParenthesis://正确                                break;                            case OpeType.Separator://正确                                break;                        }                    }                    else//右边是数字                    {                        Insert(opeList.GetOpe("*"), i + 1);                    }                }                #endregion                #region 当前运算因子是运算符                if (valList[i].type == ValType.Operator)//运算符                {                    #region 当前运算符右边是运算符                    if (valList[i + 1].type == ValType.Operator)//右边是运算符                    {                        switch (valList[i].OpeType)//左运算符                        {                            #region case OpeType.bothSides://左运算符参数信息                            case OpeType.bothSides://左运算符参数信息                                switch (valList[i + 1].OpeType)//右运算符                                {                                    case OpeType.bothSides:                                        MakeException(i);                                        break;                                    case OpeType.left:                                        MakeException(i);                                        break;                                    case OpeType.leftParenthesis://正确                                        break;                                    case OpeType.noParameter://正确                                        break;                                    case OpeType.PlusOrMinus:                                        MakeException(i);                                        break;                                    case OpeType.right://正确                                        break;                                    case OpeType.rightParenthesis:                                        MakeException(i);                                        break;                                    case OpeType.Separator:                                        MakeException(i);                                        break;                                }                                break;                            #endregion                            #region case OpeType.left://左运算符参数信息                            case OpeType.left://左运算符参数信息                                switch (valList[i + 1].OpeType)//右运算符                                {                                    case OpeType.bothSides://正确                                        break;                                    case OpeType.left://正确                                        break;                                    case OpeType.leftParenthesis:                                        Insert(opeList.GetOpe("*"), i + 1);                                        break;                                    case OpeType.noParameter:                                        Insert(opeList.GetOpe("*"), i + 1);                                        break;                                    case OpeType.PlusOrMinus://正确                                        break;                                    case OpeType.right:                                        Insert(opeList.GetOpe("*"), i + 1);                                        break;                                    case OpeType.rightParenthesis://正确                                        break;                                    case OpeType.Separator: //正确                                        break;                                }                                break;                            #endregion                            #region case OpeType.leftParenthesis://左运算符参数信息                            case OpeType.leftParenthesis://左运算符参数信息                                switch (valList[i + 1].OpeType)//右运算符                                {                                    case OpeType.bothSides:                                        int pos = i + 1 >= 0 ? i + 1 : i;                                        MakeException(pos);                                        break;                                    case OpeType.left:                                        pos = i + 1 >= 0 ? i + 1 : i;                                        MakeException(pos);                                        break;                                    case OpeType.leftParenthesis://正确                                        break;                                    case OpeType.noParameter://正确                                        break;                                    case OpeType.PlusOrMinus://正确                                        break;                                    case OpeType.right://正确                                        break;                                    case OpeType.rightParenthesis:                                        pos = i - 1 >= 0 ? i - 1 : i;                                        MakeException(pos);                                        break;                                    case OpeType.Separator:                                        pos = i - 1 >= 0 ? i - 1 : i;                                        MakeException(pos);                                        break;                                }                                break;                            #endregion                            #region case OpeType.noParameter://左运算符参数信息                            case OpeType.noParameter://左运算符参数信息                                switch (valList[i + 1].OpeType)//右运算符                                {                                    case OpeType.bothSides://正确                                        break;                                    case OpeType.left://正确                                        break;                                    case OpeType.leftParenthesis:                                        Insert(opeList.GetOpe("*"), i + 1);                                        break;                                    case OpeType.noParameter:                                        Insert(opeList.GetOpe("*"), i + 1);                                        break;                                    case OpeType.PlusOrMinus://正确                                        break;                                    case OpeType.right:                                        Insert(opeList.GetOpe("*"), i + 1);                                        break;                                    case OpeType.rightParenthesis://正确                                        break;                                    case OpeType.Separator: //正确                                        break;                                }                                break;                            #endregion                            #region case OpeType.PlusOrMinus://左运算符参数信息                            case OpeType.PlusOrMinus://左运算符参数信息                                switch (valList[i + 1].OpeType)//右运算符                                {                                    case OpeType.bothSides:                                        MakeException(i);                                        break;                                    case OpeType.left:                                        MakeException(i);                                        break;                                    case OpeType.leftParenthesis://正确                                        break;                                    case OpeType.noParameter://正确                                        break;                                    case OpeType.PlusOrMinus:                                        MakeException(i);                                        break;                                    case OpeType.right://正确                                        break;                                    case OpeType.rightParenthesis:                                        MakeException(i);                                        break;                                    case OpeType.Separator:                                        MakeException(i);                                        break;                                }                                break;                            #endregion                            #region case OpeType.right://左运算符参数信息                            case OpeType.right://左运算符参数信息                                switch (valList[i + 1].OpeType)//右运算符                                {                                    case OpeType.bothSides:                                        MakeException(i);                                        break;                                    case OpeType.left:                                        MakeException(i);                                        break;                                    case OpeType.leftParenthesis://正确                                        break;                                    case OpeType.noParameter:                                        MakeException(i);                                        break;                                    case OpeType.PlusOrMinus:                                        MakeException(i);                                        break;                                    case OpeType.right:                                        MakeException(i);                                        break;                                    case OpeType.rightParenthesis:                                        MakeException(i);                                        break;                                    case OpeType.Separator:                                        MakeException(i);                                        break;                                }                                break;                            #endregion                            #region case OpeType.rightParenthesis://左运算符参数信息                            case OpeType.rightParenthesis://左运算符参数信息                                switch (valList[i + 1].OpeType)//右运算符                                {                                    case OpeType.bothSides://正确                                        break;                                    case OpeType.left://正确                                        break;                                    case OpeType.leftParenthesis:                                        Insert(opeList.GetOpe("*"), i + 1);                                        break;                                    case OpeType.noParameter:                                        Insert(opeList.GetOpe("*"), i + 1);                                        break;                                    case OpeType.PlusOrMinus://正确                                        break;                                    case OpeType.right:                                        Insert(opeList.GetOpe("*"), i + 1);                                        break;                                    case OpeType.rightParenthesis://正确                                        break;                                    case OpeType.Separator://正确                                        break;                                }                                break;                            #endregion                            #region case OpeType.Separator://左运算符参数信息                            case OpeType.Separator://左运算符参数信息                                switch (valList[i + 1].OpeType)//右运算符                                {                                    case OpeType.bothSides:                                        MakeException(i + 1);                                        break;                                    case OpeType.left:                                        MakeException(i + 1);                                        break;                                    case OpeType.leftParenthesis://正确                                        break;                                    case OpeType.noParameter://正确                                        break;                                    case OpeType.PlusOrMinus://正确                                        break;                                    case OpeType.right://正确                                        break;                                    case OpeType.rightParenthesis:                                        MakeException(i);                                        break;                                    case OpeType.Separator:                                        MakeException(i);                                        break;                                }                                break;                            #endregion                        }                    }                    #endregion                    #region 当前运算符右边是数字                    else if (valList[i + 1].type == ValType.Number)//运算符右边是数字                    {                        switch (valList[i].OpeType)                        {                            case OpeType.bothSides://正确                                break;                            case OpeType.left:                                Insert(opeList.GetOpe("*"), i + 1);                                break;                            case OpeType.leftParenthesis://正确                                break;                            case OpeType.noParameter:                                Insert(opeList.GetOpe("*"), i + 1);                                break;                            case OpeType.PlusOrMinus://正确                                break;                            case OpeType.right:                                MakeException(i);                                break;                            case OpeType.rightParenthesis:                                Insert(opeList.GetOpe("*"), i + 1);                                break;                            case OpeType.Separator: //正确                                break;                        }                    }                    #endregion                }                #endregion            }            #endregion
View Code

 

 

三、下面是表达式计算过程的主要步骤:

1、将字符串表达式中的数字、运算符、括号、逗号等提取出来,按顺序存储在一个动态数组中。

    原理很简单,就是通过字符串搜索来实现,扫描字符串表达式,依次把运算符取出来放到动态数组中,运算符之间的内容就是参与运算的数了。

    把字符串表达式转换成运算符、括号、逗号和数字的动态数组后,后面将处理这个数组,完成运算。

    部分代码如下:

#region 将字符串表达式转换成运算因子(数字、常数及运算符)列表        ///         /// 将字符串表达式转换成运算因子列表        ///         /// 字符串表达式        private void ToList(List
valList, string str) { do { //字符串中首个运算符的位置 int opePos = GetOpePos(str); CalNum num = null; CalOpe ope = null; if (opePos > 0)//找到运算符且它前面有数字 { num = GetNum(ref str, opePos); } else if (opePos == 0)//找到运算符且它在字符串最前面 { ope = GetOpe(ref str); } else//没有找到运算符 { num = GetNum(ref str); } if (num != null) { valList.Add(new CalVal(num)); } if (ope != null) { valList.Add(new CalVal(ope)); } } while (str != ""); } #endregion #region 获取字符串中首个运算符或常数的位置 ///
/// 获取字符串中首个运算符或常数的位置 /// ///
字符串 ///
值若为-1表示未找到
private int GetOpePos(string str) { CalOpeList opeList = new CalOpeList(); int pos = -1; for (int i = 0; i < opeList.count; i++) { int k = -1; int opeIndex = str.IndexOf(opeList.opeList[i].tag); //科学计数法的情况:如果'+'或'-'的前面是'E' if (opeList.opeList[i].opeType == OpeType.PlusOrMinus && opeIndex - 1 >= 0 && str.Substring(opeIndex - 1, 1) == "E") { //从'+'或'-'的后面重新查找 k = str.Substring(opeIndex + 1).IndexOf(opeList.opeList[i].tag); if (k != -1)//如果找到 { //计算该运算符的位置 k += opeIndex + 1; } } else { k = opeIndex; } if (pos == -1) { pos = k; } else if (k >= 0 && k < pos) { pos = k; } } return pos;//值若为-1表示未找到 } #endregion #region 获得运算符 ///
/// 获得运算符 /// private CalOpe GetOpe(ref string str) { CalOpeList opeList = new CalOpeList(); CalOpe ope = null; for (int i = 0; i < opeList.count; i++) { if (str.IndexOf(opeList.opeList[i].tag) == 0) { ope = opeList.opeList[i]; //更新str str = str.Substring(opeList.opeList[i].tag.Length); break; } } return ope; } #endregion #region 获取数字 ///
/// 获取数字 /// private CalNum GetNum(ref string str, int opePos) { CalNum result = null; result = new CalNum(str.Substring(0, opePos)); //更新str str = str.Substring(opePos); return result; } ///
/// 获取数字 /// private CalNum GetNum(ref string str) { CalNum result = null; result = new CalNum(str); //更新str str = ""; return result; } #endregion
View Code

 

2、优先级的处理。

    优先级的处理是表达式计算过程中的一个关键问题。

    动态数组中的每个元素(运算符、分隔符或数字等)都有一个优先级,对存储表达式的动态数组从前往后扫描,遇到左括号,将运算符的优先级提升,遇到右括号的话,将运算符的优先级降低。这样处理过后,动态数组中的运算符的优先级各不相同,括号中的运算符的优先级就会高于括号外的运算符的优先级。比如括号外的加号的优先级低于括号内的加号的优先级,因为在处理的过程中把括号内的加号的优先级提升了。

///         /// 优先级处理,提高括号中的算式的运算符的优先级        ///         private void LevelProcess()        {            int delLevel = 0;//优先级增量            for (int i = 0; i < valList.Count; i++)            {                if (valList[i].type == ValType.Operator)                {                    //如果是左括号                    if (valList[i].OpeType == OpeType.leftParenthesis)                    {                        delLevel += 1000;                        continue;                    }                    //如果是右括号                    if (valList[i].OpeType == OpeType.rightParenthesis)                    {                        delLevel -= 1000;                        continue;                    }                    valList[i].level = valList[i].ope.level + delLevel;                }            }        }
View Code

 

3、在计算存储在动态数组中的表达式前,先生成优先级列表,并按优先级从高到低排好序。

#region 生成优先级列表        ///         /// 生成优先级列表        ///         private void MakeLevelList()        {            #region 生成优先级列表,优先级按从高到低存储            //逐个扫描运算因子,提取优先级列表,并按优先级从高到低存储            for (int i = 0; i < valList.Count; i++)            {                //如果是运算符                if (valList[i].type == ValType.Operator)                {                    //如果是括号或分隔符                    if (valList[i].OpeType == OpeType.leftParenthesis                        || valList[i].OpeType == OpeType.rightParenthesis                        || valList[i].OpeType == OpeType.Separator)                    {                        continue;                    }                    InsertIntoLevelList(ref levelList, valList[i].level);                }            }            #endregion        }        #endregion#region 将优先级插入到优先级列表中        ///         /// 将优先级插入到优先级列表中        ///         /// 优先级列表        /// 要插入的优先级        private void InsertIntoLevelList(ref List
levelList, int level) { //列表为空的情况 if (levelList.Count == 0) { levelList.Add(level); return; } //该优先级是否已存在 for (int i = 0; i < levelList.Count; i++) { //该优先级已存在 if (level == levelList[i]) { return; } } int k = 0; for (; k < levelList.Count; k++) { if (level > levelList[k]) { levelList.Insert(k, level); break; } } if (k >= levelList.Count) { levelList.Add(level); } } #endregion
View Code

 

4、遍历优先级列表,按照优先级从高到低,扫描表达式动态数组,先计算优先级高的运算,用计算结果替换掉计算之前的运算符和参与运算的数。当把优先级列表中的优先级都处理完,对表达式的计算也就结束了,这时,表达式动态数组中将只剩下计算结果。

    下面是计算过程的主要代码:

#region 核心算法,对于当前优先级,扫描运算因子列表并计算                if (levelList.Count != 0)//优先级列表不为空                {                    #region 处理当前优先级                    //对于当前优先级,逐个扫描运算因子,处理具有该优先级的运算                    for (int i = 0; i < valList.Count; i++)                    {                        //找到具有该优先级的运算符                        if (valList[i].type == ValType.Operator                            && valList[i].level == levelList[0])//该运算因子的优先级等于当前优先级                        {                            bool isSign = false;//Sign为true表示当前运算因子是正负号                            currentPos = valList[i].primalPos;//记录当前运算因子在列表中的原始位置                            //参数列表                            List
numList = new List
(); //临时计算结果 CalNum num; switch (valList[i].OpeType)//i表示该运算因子的位置 { #region case OpeType.PlusOrMinus: case OpeType.PlusOrMinus: //若该运算符前面没有运算因子 //或前一个运算因子是运算符,则按正负号计算 if (i == 0 || (i - 1 >= 0 && valList[i - 1].type == ValType.Operator)) { isSign = true;//是正负号 //获取该运算符的参数 if (i + 1 < valList.Count && valList[i + 1].type == ValType.Number) { //注意,下面第一句不可省略 numList = new List
(); numList.Add(valList[i + 1].num); } //计算 num = CalOpeList.Calculate(valList[i].ope, numList); //更新运算因子列表 ReplaceVal(num, 2, i); //i无需调整 } else//若前一个运算因子是操作数,则按加减号计算 { //获取该运算符的参数 if (valList[i - 1].type == ValType.Number && i + 1 < valList.Count && valList[i + 1].type == ValType.Number) { //注意,下面第一句不可省略 numList = new List
(); numList.Add(valList[i - 1].num); numList.Add(valList[i + 1].num); } //计算 num = CalOpeList.Calculate(valList[i].ope, numList); //更新运算因子列表 ReplaceVal(num, 3, i - 1); //调整i i--; } break; #endregion #region case OpeType.bothSides: case OpeType.bothSides: //获取该运算符的参数 if (i >= 1 && valList[i - 1].type == ValType.Number && i + 1 < valList.Count && valList[i + 1].type == ValType.Number) { //注意,下面第一句不可省略 numList = new List
(); numList.Add(valList[i - 1].num); numList.Add(valList[i + 1].num); } //计算 num = CalOpeList.Calculate(valList[i].ope, numList); //更新运算因子列表 ReplaceVal(num, 3, i - 1); //调整i i--; break; #endregion #region case OpeType.left: case OpeType.left: //获取该运算符的参数 if (i >= 1 && valList[i - 1].type == ValType.Number) { //注意,下面第一句不可省略 numList = new List
(); numList.Add(valList[i - 1].num); } //计算 num = CalOpeList.Calculate(valList[i].ope, numList); //更新运算因子列表 ReplaceVal(num, 2, i - 1); //调整i i--; break; #endregion #region case OpeType.noParameter: case OpeType.noParameter: //注意,下面第一句不可省略 numList = new List
(); //计算 num = CalOpeList.Calculate(valList[i].ope, numList); //更新运算因子列表 ReplaceVal(num, 1, i); break; #endregion #region case OpeType.right: case OpeType.right: #region 该运算符只有一个参数,且它的右边是常数 if (i + 1 < valList.Count && valList[i].ParameterNumber == 1 && valList[i + 1].type == ValType.Number) { //获取该运算符的参数 //注意,下面第一句不可省略 numList = new List
(); numList.Add(valList[i + 1].num); //计算 num = CalOpeList.Calculate(valList[i].ope, numList); //更新运算因子列表 ReplaceVal(num, 2, i); } #endregion #region 该运算符不是只有一个参数,或者它的右边不是常数 else { //计算参数个数 int count = 0; int k = i + 1; //从运算符后面开始检测 //如果是左括号、分隔符或数字,则执行while中语句 while (k < valList.Count && (valList[k].type == ValType.Number || valList[k].OpeType == OpeType.Separator || valList[k].OpeType == OpeType.leftParenthesis)) { //如果是数字,参数个数加1 if (valList[k].type == ValType.Number) { count++; } k++; } //注意,下面第一句不可省略 numList = new List
(); //从该运算符后面,逐个扫描,获取该运算符的参数 //j表示已读取的参数个数 //m表示检测位置增量 int m = 0; for (int j = 0; j < count; ) { //如果找到数字,存为参数 if (valList[i + j + m + 1].type == ValType.Number) { numList.Add(valList[i + j + m + 1].num); j++; } //如果是分隔符或左括号,检测位置增量加1,表示跳过该运算因子,继续检测 else if (valList[i + j + m + 1].OpeType == OpeType.Separator || valList[i + j + m + 1].OpeType == OpeType.leftParenthesis) { m++; } } //计算 num = CalOpeList.Calculate(valList[i].ope, numList); //更新运算因子列表,count+m+2中的2表示运算符本身和右括号 ReplaceVal(num, count + m + 2, i); } #endregion break; #endregion }//end switch if (!isSign)//如果不是正负号 { break;//退出for循环 } } } #endregion #region 删除处理完的优先级 bool levelExists = false;//是否存在具有该优先级的运算符 //逐个扫描运算因子,判断是否仍存在具有该优先级的运算符 for (int i = 0; i < valList.Count; i++) { if (levelList[0] == valList[i].level)//存在 { levelExists = true; } } if (!levelExists)//该优先级已处理完则删除它 { levelList.RemoveAt(0); } #endregion } #endregion
View Code

 

四、结束语。

    没有什么高深的思想,我用最笨的办法实现了表达式计算。这个程序中用的最多的算法就是字符串查找,以及遍历数组。

 

    程序截图:

    

    GitHub地址:https://github.com/0611163/ScientificCalculator.git

    源代码下载:

    从博客园下载:

 

转载于:https://www.cnblogs.com/s0611163/p/3504951.html

你可能感兴趣的文章
“亲切照料”下的领域驱动设计
查看>>
SRE工程师到底是做什么的?
查看>>
解读:Red Hat为什么收购Ansible
查看>>
Ossim下的安全合规管理
查看>>
DelphiWebMVC框架下BPL热部署实现
查看>>
C++与MySQL的冲突
查看>>
siki学习之观察者模式笔记
查看>>
单元测试
查看>>
spring.net 继承
查看>>
ES6:模块简单解释
查看>>
JavaScript indexOf() 方法
查看>>
用Bootstrap写一份简历
查看>>
ZJU PAT 1023
查看>>
WMI远程访问问题解决方法
查看>>
从零开始学习IOS,(UILabel控件)详细使用和特殊效果
查看>>
Android开发历程_15(AppWidget的使用)
查看>>
阿花宝宝 Java 笔记 之 初识java
查看>>
7、设计模式-创建型模式-建造者模式
查看>>
Cesium官方教程11--建模人员必读
查看>>
我国古代的勾股定理
查看>>