c# 另类计算器

很早的时候碰到的一个问题,当时写不出来,一直觉得很难,前几天试了一下,发现也就一般般,贴一下,记录。

需求如下,输入一个任意的符合规则的式子,包含{},[],(),+,-,*,/ 计算结果,如输入{3*[3*(1-2)]},结果为-9 。 可能很多人咋一看觉得很简单,不就是计算器么,但是实际去考虑的话也没有很简单。

思路的话就是递归吧。步骤就是 1.写个基础运算,计算没有括号的式子;2. 根据第一步依次计算小括号的值,消掉所有小括号;3.中括号替换成小括号,计算完继续大括号替换成小括号,消掉所有括号之后就到了第一步,得出最后的值。这里只根据例子说思路,到最后贴整个代码。

拿个式子举例吧:-3*{3*[2*(-1+2*1-4/2)]} + 2*[6/(1-3)] 

首先第一步,我的计算思路是,将一个式子的计算符号和数字分别按序存入两个列表,然后遍历符号列表,根据符号列表的索引取到这个符号两边的数字,值得注意的是,会有负号开头的式子,这样的话负号就只有右边才有数字,左边是没有的,所以需要先处理式子,如果式子是负号开头的,则补0,然后进行计算,计算得出结果之后,移除掉计算过的符号,替换计算过的数字为计算结果,注意取字符串的时候的索引。这里先留一个坑位。

第二步,根据第一步先计算最左边小括号的值,然后用计算结果替换掉这个小括号以及小括号中间的字符串,这样算是消去掉了一个小括号,接着递归消去后面的所有小括号。这里会踩到一个坑,比如式子3*(1-2),计算括号内容之后替换,就成了3*-1,这种的式子,我们按照步骤一是算不出来的,因为这种式子是不合格的,正确的式子应该是3*(-1),但是我们刚消掉括号,总不能再加一个括号。所以说在步骤一计算的时候,我们应该先处理掉式子的一些特殊符号,比如上面说的*-,当然也有/-,--,+-。--,+-的替换规则是负负得正,负正得负,没什么好说的,*-,/-的处理方式是,去找到*-,/-的左边的最近的一个+或者-,然后将*-,/-的负号去跟找到的这个符号去运算,得出来一个符号进行替换,比如 3+3*(1-2) ,计算1: 3+3*-1,计算2 :3+-3*1,计算3:3-3*1,这样的式子就可以用步骤1计算出结果。

第三步,就是先替换所有中括号为小括号,根据步骤2计算,消掉所有中括号之后,所有大括号替换为小括号,继续消除,最后得出一个式子,用步骤一计算结果。完毕。

本来想举个例子来说的,但是没有手写设备,只是打印的话不够直观,就算了。记一下思路,贴代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Calculator
{
    class Program
    {
        static List<char> allSigns = new List<char>() { '+', '-', '*', '/' };
        static void Main(string[] args)
        {
            Console.WriteLine("输入要计算的字符串(+-*/()[]{}):");
            string str = Console.ReadLine();
            GetString(ref str);
            Console.WriteLine("结果为:  "+str);
            Console.ReadKey();
        }

        static void GetString(ref string str)
        {
            Console.WriteLine("getstring: "+ str );
            if (!str.Contains('(') && !str.Contains('[') && !str.Contains('{'))//没有括号,直接计算
            {
                GetValue(ref str);
                return;
            }
            if (str.Contains('('))
            {
                int leftIndex = str.IndexOf('(');
                int rightIndex = str.IndexOf(')');
                string str1 = str.Substring(0,leftIndex);
                string str2 = str.Substring(leftIndex+1,rightIndex-leftIndex-1);
                string str3 = str.Substring(rightIndex+1);
                GetValue(ref str2);
                str = str1 + str2 + str3;//根据'()'将字符串截为三段,中间一段计算值,替换,重组字符串
            }
            else if (str.Contains('['))
            {
                str = str.Replace('[','(');
                str = str.Replace(']',')');
            }
            else
            {
                str = str.Replace('{', '(');
                str = str.Replace('}', ')');
            }
            GetString(ref str);
        }

        //计算无括号的式子
        static void GetValue(ref string str)
        {
            Console.WriteLine("GetValue: "+ str );
            if (str[0] == '-')  str = "0" + str; //负数开头,补0
            else if(str[0]=='+')  str = str.Substring(1); // --得+,删掉开头的+
            else str = "0+" + str; //补0,如果第一个符号为*-或/-,可以用来替换
            DealSign(ref str);

            if (str[0] == '-') str = "0" + str;// 负数开头,补0

            string[] strs = str.Split(new char[] { '+', '-', '*', '/' });

            List<int> signs = new List<int>();//符号列表
            List<int> nums = new List<int>();//数字列表

            for (int i = 0; i < str.Length; i++)
            {
                if (allSigns.Contains(str[i]))
                {
                    signs.Add(i);
                }
            }//获取符号列表

            for (int i = 0; i < signs.Count + 1; i++)
            {
                string numStr = "";
                int currentIndex = 0;
                int nextIndex = 0;
                if (i == 0)
                {
                    currentIndex = 0;
                    nextIndex = signs[i];

                }
                else if (i < signs.Count)
                {
                    currentIndex = signs[i - 1] + 1;
                    nextIndex = signs[i];
                }
                else
                {
                    currentIndex = signs[signs.Count - 1] + 1;
                    nextIndex = str.Length;
                }
                numStr = str.Substring(currentIndex, nextIndex - currentIndex);//取两个符号之间的字符串

                nums.Add(int.Parse(numStr));
            }//获取数字列表
            int endValue = GetNumValue(signs,nums,ref str);
            str = endValue.ToString();
        }

        //根据符号列表和数字列表,计算
        static int GetNumValue( List<int> signs,  List<int> nums, ref string str)
        {
            if (signs.Count == 0)
            {
                return nums[0];
            }
            else
            {
                bool hasMul = false;//是否为乘法
                bool hasDiv = false;//是否为除法
                int index = 0;
                for (int i = 0; i < signs.Count; i++)
                {
                    if (str[signs[i]] == '*')
                    {
                        index = i;
                        hasMul = true;
                        break;
                    }
                    else if (str[signs[i]] == '/')
                    {
                        index = i;
                        hasDiv = true;
                        break;
                    }
                }
                if (hasMul || hasDiv)
                {
                    nums[index] = hasMul ? nums[index] * nums[index + 1] : nums[index] / nums[index + 1];
                    nums.RemoveAt(index + 1);
                    signs.RemoveAt(index);
                }
                else
                {
                    nums[0] = str[signs[0]] == '+' ? nums[0] + nums[1] : nums[0] - nums[1];
                    nums.RemoveAt(1);
                    signs.RemoveAt(0);
                }//计算,处理列表
                return GetNumValue( signs,  nums, ref str);
            }
        }

        //获取最近的索引
        static int GetNearIndex(string str, int startIndex)
        {
            int index = 0;
            int length = startIndex;//遍历结束的地方
            for (int i = 0 ; i < length; i++)//这里反过来遍历更好,懒得改了....
            {
                if (str[i] == '+' || str[i] == '-' )
                {
                    index = Math.Max(i, index);
                }
            }
            return index;

        }

        //处理+-,--,*-,/-
        static void DealSign(ref string str)
        {
            Console.WriteLine("DealSign: "+ str );
            str = str.Replace("+-", "-");
            str = str.Replace("--", "+");
            if (! str.Contains("*-") && !str.Contains("/-"))
            {
                return;
            }
            else
            {
                int startIndex1 = str.IndexOf("*-");
                int startIndex2 = str.IndexOf("/-");

                //找到/-,*-左边距离最近的+,-,变号,分段,重新组合

                if ((startIndex1 < startIndex2 && startIndex1 != -1 )|| startIndex2 == -1)// *-在/-左边,或没有/-
                {
                    int index = GetNearIndex(str, startIndex1);
                    string str1 = str.Substring(0,index+1);
                    string str2 = str.Substring(index + 1, startIndex1-index-1);
                    string str3 = str.Substring(startIndex1+2);
                    str = str1 + "-" + str2 + "*" + str3;
                }
                else if ((startIndex1 > startIndex2 && startIndex2 != -1)||startIndex1 == -1)
                {
                    int index = GetNearIndex(str, startIndex2);
                    string str1 = str.Substring(0, index + 1);
                    string str2 = str.Substring(index + 1, startIndex2-index-1);
                    string str3 = str.Substring(startIndex2 + 2);
                    str = str1 + "-" + str2 + "/" + str3;
                }
                DealSign(ref str);
            }
        }
    }
}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值