首页 > 代码库 > Windows Form简易计算器实现(下)

Windows Form简易计算器实现(下)

  陆陆续续更新这个计算器用了一个礼拜了,今天无论如何也要把它更完。笔者有点追求完美,再者每天都有课,晚上还有作业,还有每晚都会写一些其他的博文。

  上一次漏了写如何实现计算的。思路如下:

  之前得到一个栈2stack2,里面存的是逆波兰式。但是注意了,式子是反过来的。因为栈只能从栈顶写入,从栈顶拿出。所以,必须把栈2的数反过来。

  下面是转换的代码:

        //
        //将栈2数值倒过来
        //
        private void UpSideDown()
        {
            string[] strTemp = stack2.ToArray();                        //将栈stack2数值临时存储在数组里
            stack2.Clear();                                             //将栈stack2清空
            for(int i=strTemp.Length-1;i>=0;i--)
            {
                stack2.Push(strTemp[i]);
            }
        }

 

  将栈的数值倒过来之后,我们就可以进行计算了,计算的思路如下:按下图步骤

技术分享

  1:从栈顶取元素,如果是数字直接存入新栈 2:如果是操作符,则不存入,将新栈的前两个元素依次取出,先取出的数字放在运算符右边,后取出的放在左边 3:计算的结果放回新栈。重复以上步骤,知道计算完。

  注意!新栈保存的数字,所以要注意栈的类型。

        //
        //计算四则运算
        //
        public double DoMath()
        {
            var stackTemp = new Stack<double>();
            try
            {
                while (myStack2.Count != 0)
                {
                    if (!IfNumber(myStack2.Peek()))
                    {
                        double d1 = stackTemp.Pop();
                        double d2 = stackTemp.Pop();
                        switch (myStack2.Pop())
                        {
                            case "+":
                                stackTemp.Push(d2 + d1);
                                break;
                            case "-":
                                stackTemp.Push(d2 - d1);
                                break;
                            case "*":
                                stackTemp.Push(d2 * d1);
                                break;
                            case "/":
                                stackTemp.Push(d2 / d1);
                                break;
                        }//end switch
                    }//end if
                    else
                    {
                        stackTemp.Push(Convert.ToDouble(myStack2.Pop()));
                    }
                }//end while
                return stackTemp.Pop();
            }
            catch
            {
                return 0;
            }
        }

 

  四则混合运算的大体步骤和解决思路就这么多,现在开始解决如何确保用户在乱输入公式以及得到的公式该怎么解析(前面提到的问题:就是当接受了加括号的一元运算符比如:1+(-2)。转换得到的式子是不能正确计算的。)

  思路一:怎么解决用户乱输入数字

  用栈保存用户每次按的按键内容,除了第一次输入外,在每次输入的时候与栈顶元素进行比较。如果连续两次输入操作符,则视第二次有效:

        private string str = "";                                    //声明切定义一个空字符串,用于在label显示用户输入
        Stack<string> tempStack = new Stack<string>();              //声明一个数组,用去存储用户输入

        //判断用户点击是否有效
        public void IfValid(string s)
        {
            if (tempStack != null)
            {
                if (Calculate.IfNumber(s))                        //如果用户按得是数字,则输入有效
                {
                    tempStack.Push(s);
                }
                else
                {
                    if (!Calculate.IfNumber(tempStack.Peek()))       //如果用户连续两次按的是操作符,则视第二次有效
                    {
                        tempStack.Pop();
                        tempStack.Push(s);
                    }
                    else
                        tempStack.Push(s);
                }
                
            }//end if
            else
            {
                tempStack.Push(s);
            }
            str = "";
            string[] tempStr = tempStack.ToArray();
            //将得到的数组反转
            for (int i = 0; i < tempStr.Length / 2; i++)
            {
                string temp = tempStr[i];
                tempStr[i] = tempStr[tempStr.Length - i - 1];
                tempStr[tempStr.Length - i - 1] = temp;
            }
            foreach (var item in tempStr)
                str += item;
            label1.Text = str;

        }
        private void button1_Click(object sender, EventArgs e)
        {
            IfValid(button1.Text);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            IfValid(button2.Text);
        }

  笔者有很多地方都用到将数组元素前后倒过来的代码,但却没有将其写成独立函数,造成代码的冗余。

 

  这里代码是写在Form1里的,用到了ReversePolish的IsNumber的方法,因为是不同类的,而且没有ReversePolish就直接调用,所以要把之前定义的修饰private 改成public static修饰才能直接调用。

  思路二:当接受了加括号的一元运算符比如:1+(-2)。通过逆波兰法则转换得到的式子是不能正确计算的。

  把式子进行解析,写成:1+(0-2)的形式!

  代码如下:

  

       string[] strAnalysis;                                           //存放解析完原始计算公式之后的公式
        string[] str;                                                   //string数组str用于存放原始计算公式//
        //解析原始计算公式
        //
        public void Analysis()
        {
            var stackTemp = new Stack<string>();

            int count = 0;
            try
            {
                while (count < strAnalysis.Length)
                {
                    if (strAnalysis[count] == "(" && (strAnalysis[count + 1] == "-" || strAnalysis[count + 1] == "+"))     //如果公式操作数(的后一位操作数是+或则-,
                    {
                        stackTemp.Push(strAnalysis[count]);
                        stackTemp.Push("0");
                        count++;
                    }
                    else if (!IfNumber(strAnalysis[count]))
                    {
                        stackTemp.Push(strAnalysis[count]);
                        count++;
                    }
                    else if (IfNumber(strAnalysis[count]))
                    {
                        string strTemp = "";
                        while (count < strAnalysis.Length && IfNumber(strAnalysis[count]))          //如果是数字,包括小数点.,则拼成一个字符串,直到遇到一个操作符或则数组遍历完毕
                        {
                            strTemp += strAnalysis[count];
                            count++;
                        }
                        stackTemp.Push(strTemp);
                    }
                }
            }
            catch
            {
                stackTemp = null;
            }
            
            str = stackTemp.ToArray();
            //反转字符串数组
            for (int i = 0; i < str.Length / 2; i++)
            {
                string temp = str[i];
                str[i] = str[str.Length - i - 1];
                str[str.Length - i - 1] = temp;
            }
        }        

  大体思路思路就这么多了,总之是问题逐一突破,把解决的问题方案写成方法,最后凑在一起就成功了。以上的代码并不是所有的代码,笔者只是想把自己的思路分享一下。因为我猜如果我把所有代码都贴出来是要花很长时间看的。毕竟每个人的思维不一样。本人就不擅长读别人的代码T T。

  提示:一般遇到访问内存,比如取栈压栈访问栈顶的代码,最好上异常处理,因为很容易就出现错误。

  欢迎大家提出质疑!

 

Windows Form简易计算器实现(下)