C#Windows Form简易计算器实现(中)

  昨天花了一天的时间弄计算器。也算是做出来了,还是简易的(怀疑猿生!!)。在此先感谢昨天被我骚扰的朋友。

先贴一张界面看看

  其实健壮性还是挺差的,用户体验也是极差的。比如说用户输入了不合理运算式子,我就直接抛出一个异常完事了,因为要在原来的算法里加判断实在晕乱。所以趁热打铁,希望在写博客的时候再把思路理理,完善不足。



思路一:

  因为计算的是四则混合运算,比如2*6-4/(2+3)。我们最开始得到的是一个表达式字符串,计算机是不会帮你计算的。而四则混合运算有优先等级的计算,那么该怎么计算呢?于是问了问度娘,度娘说你可以用逆波兰式计算。于是我二话不说看了看逆波兰式子,果然高明。下面是贴一下逆波兰式计算步骤:

/// <summary>
/// 使用逆波兰表示法求四则混合运算
/// 首先,需要两个栈
/// 栈s1用于临时存储运算符(含一个结束符号),此运算符在栈内遵循越往栈顶优先级越高的原则;
/// 栈s2用于输入逆波兰式;
/// 为方便起见,栈s1需要放入一个优先级最低的运算符,在这里假定为“#”;
/// 读取运算式入栈的步骤
/// 1.若x是操作数,则分析出完整的运算数,压入栈s2;
/// 2.若x是运算符,则分情况而定:
///   若x是‘(’,则直接压入栈s1
///   若x是‘)’,则将距离栈s1栈顶的最近的‘(’之间的运算符,逐个出栈,依次压入栈s2,此时抛弃‘(’
///   若x是除了‘(’和‘)’以外的运算符,则再分如下情况
///      若当前的栈顶元素是‘(’,则直接将x压入栈s1
///      若当前的栈顶元素不是‘(’,则将x与栈s1的栈顶元素进行对比,如果优先级比较高,则压入栈,如果优先级低,则把栈s1的栈顶元素弹出压入栈s2,直到栈s1的栈顶元素优先级低于x,
///      或则栈s2的栈顶运算符为‘(’,此时再将x压入栈s1
/// 3.进行完以上操作之后,检查栈s1是否为空,若不为空,则将栈中元素依次弹出并压入栈s2中。
/// </summary>

  把上面的2*6-4/(2+3)转成逆波兰式是这样的:-/+324*62。注意,转换完之后的逆波兰式是没有括号的。



思路二:

  首先想到的是写一个函数,实现这一转换。但是在写的时候会发现,①会用到判断是否是操作数还是操作符,②以及符号的优先级。因为符号很多,写在一个判断里,代码看起来会很长,所以就先把这两个判断写成函数,以方便使用,增强代码可读性。

  ①判断是数字还是符号函数如下:

  

        //
        //判断是操作数还是操作符
        //
        static bool IsNumber(string str)
        {
            if (str == "(" || str == ")" || str == "*" || str == "/" || str == "-" || str == "+")
                return false;
            else
                return true;
        }

  ②定义优先等级。在这里,我用到的是泛型集合Dictionary。(我上一篇博文提到过这个集合)

        //
        //定义优先等级,数字越大,优先等级越高
        //
        static void DefinePriority()
        {
            Dictionary<string, int> dic = new Dictionary<string, int>();
            dic.Add("(", 7);
            dic.Add(")", 6);
            dic.Add("*", 5);
            dic.Add("/", 5);
            dic.Add("-", 4);
            dic.Add("+", 4);
            dic.Add("#", 3);
        }

  然后接着写转换逆波兰式的函数:

  

        //
        //接受一个字符串数组,转逆波兰式子
        //
        public void ReverseToPolish(string[] str)
        {

            stack1.Push("#");                                   //栈1压入#
            if (str != null)                                    //如果字符串数组不为空,执行判断
            {
                //因为是处理栈堆,很容易出现内存分配读取异常,加一个try catch
                try
                {
                    for (int i = 0; i < str.Length; i++)
                    {
                        if (IsNumber(str[i]))                       //如果是数字,直接压入栈2
                            stack2.Push(str[i]);
                        else
                        {
                            if (dic[str[i]] == 7)                      //如果是“(”,直接压入栈1
                            {
                                stack1.Push(str[i]);
                            }
                            else if (dic[str[i]] == 6)               //如果是“)”,将栈顶元素依次压入栈2,直到遇到“(”
                            {
                                while (stack1.Peek() != "(")
                                {
                                    stack2.Push(stack1.Pop());
                                }
                                stack1.Pop();                       //移除栈顶元素“(”
                            }
                            else if (dic[str[i]] == 5 || dic[str[i]] == 4)    //除了“(”和“)”的情况
                            {
                                if (stack1.Peek() == "(")              //如果栈顶元素是“(”,直接压入栈1
                                {
                                    stack1.Push(str[i]);
                                }
                                //若当前的栈顶元素不是‘(’,则将x与栈s1的栈顶元素进行对比,如果优先级比较高,则压入栈
                                //如果不是,则把栈s1的栈顶元素弹出压入栈s2,直到栈s1的栈顶元素优先级低于x
                                else
                                {
                                    while (dic[str[i]] <= dic[stack1.Peek()])   //如果优先等级不高于栈顶
                                    {
                                        stack2.Push(stack1.Pop());
                                    }
                                    stack1.Push(str[i]);
                                }
                            }//end else if
                        }//end else
                    }//end for
                     //进行完以上操作,检查栈1是否为“#”,不是,则把栈顶元素依次压入栈2
                    while (stack1.Peek() != "#")
                    {
                        stack2.Push(stack1.Pop());
                    }
                }
                catch
                {
                    stack2.Push("0");
                }

            }
            //检查下是否正确
            foreach (var item in stack2)
                Console.Write(item);
            Console.WriteLine();

        }

  下面贴下整个类的代码:

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

namespace 简易计算器
{
    class ReversePolish
    {
        Dictionary<string, int> dic = new Dictionary<string, int>();        //字典
        Stack<string> stack1 = new Stack<string>();                         //栈1
        Stack<string> stack2 = new Stack<string>();                         //栈2

        public ReversePolish()
        {
            DefinePriority();
        }

        //
        //接受一个字符串数组,转逆波兰式子
        //
        public void ReverseToPolish(string[] str)
        {

            stack1.Push("#");                                   //栈1压入#
            if (str != null)                                    //如果字符串数组不为空,执行判断
            {
                //因为是处理栈堆,很容易出现内存分配读取异常,加一个try catch
                try
                {
                    for (int i = 0; i < str.Length; i++)
                    {
                        if (IsNumber(str[i]))                       //如果是数字,直接压入栈2
                            stack2.Push(str[i]);
                        else
                        {
                            if (dic[str[i]] == 7)                      //如果是“(”,直接压入栈1
                            {
                                stack1.Push(str[i]);
                            }
                            else if (dic[str[i]] == 6)               //如果是“)”,将栈顶元素依次压入栈2,直到遇到“(”
                            {
                                while (stack1.Peek() != "(")
                                {
                                    stack2.Push(stack1.Pop());
                                }
                                stack1.Pop();                       //移除栈顶元素“(”
                            }
                            else if (dic[str[i]] == 5 || dic[str[i]] == 4)    //除了“(”和“)”的情况
                            {
                                if (stack1.Peek() == "(")              //如果栈顶元素是“(”,直接压入栈1
                                {
                                    stack1.Push(str[i]);
                                }
                                //若当前的栈顶元素不是‘(’,则将x与栈s1的栈顶元素进行对比,如果优先级比较高,则压入栈
                                //如果不是,则把栈s1的栈顶元素弹出压入栈s2,直到栈s1的栈顶元素优先级低于x
                                else
                                {
                                    while (dic[str[i]] <= dic[stack1.Peek()])   //如果优先等级不高于栈顶
                                    {
                                        stack2.Push(stack1.Pop());
                                    }
                                    stack1.Push(str[i]);
                                }
                            }//end else if
                        }//end else
                    }//end for
                     //进行完以上操作,检查栈1是否为“#”,不是,则把栈顶元素依次压入栈2
                    while (stack1.Peek() != "#")
                    {
                        stack2.Push(stack1.Pop());
                    }
                }
                catch
                {
                    stack2.Push("0");
                }

            }
            //检查下是否正确
            foreach (var item in stack2)
                Console.Write(item);
            Console.WriteLine();

        }
        //
        //判断是操作数还是操作符
        //
        private bool IsNumber(string str)
        {
            if (str == "(" || str == ")" || str == "*" || str == "/" || str == "-" || str == "+")
                return false;
            else
                return true;
        }
        //
        //定义优先等级,数字越大,优先等级越高
        //
        private void DefinePriority()
        {

            dic.Add("(", 7);
            dic.Add(")", 6);
            dic.Add("*", 5);
            dic.Add("/", 5);
            dic.Add("-", 4);
            dic.Add("+", 4);
            dic.Add("#", 3);
        }
    }
}

  笔者已经检测过,逻辑是没有问题的。总之我真的是写了很久,因为在写的时候会遇到如下的问题:就是当接受了加括号的一元运算符比如:1+(-2)。转换得到的式子是不能正确计算的。

  

  下次的博文我会分享解决上述问题的方法以及笔者自己的关于如何判断用户是否输入正确的式子的方法

  Tip:关于这些逻辑性比较强的代码,可能写过一段时间到回去看就会看不懂了。所以,笔者的经验是:写的时候一定要严谨,想周到点。写完如果不确定是否正确,就进行测试,亲测几次没问题之后就不要管它了,把它缩起来,以后拿来用就好。写的时候注释一定要详细点,万一要到回去看呢!!!!

  

  

时间: 2024-10-12 08:43:36

C#Windows Form简易计算器实现(中)的相关文章

C#Windows Form简易计算器实现(上)

第一次写博客,来分享一个简易计算器的代码.作为一名准程序员,就是要多写代码才能孰能生巧.重视基础知识才能飞的更快更高以及更稳. 代码可能会写的很糟糕,不完美不安全之处希望发现的越多越好 c#编写计算器带窗口的,对于新手来说是如何建立窗体以及实现按钮的响应事件吧!那么,首先来探索下窗口是怎么实现的吧! 步骤1:新建项目→C#windows窗体应用程序→新建解决方案 此时你会发现有两个名称,一个是解决方案名称,一个是项目名称.对于小程序来说其实没什么区别.但对于大点的程序最好就要区别开了.解决方案就

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

陆陆续续更新这个计算器用了一个礼拜了,今天无论如何也要把它更完.笔者有点追求完美,再者每天都有课,晚上还有作业,还有每晚都会写一些其他的博文. 上一次漏了写如何实现计算的.思路如下: 之前得到一个栈2stack2,里面存的是逆波兰式.但是注意了,式子是反过来的.因为栈只能从栈顶写入,从栈顶拿出.所以,必须把栈2的数反过来. 下面是转换的代码: // //将栈2数值倒过来 // private void UpSideDown() { string[] strTemp = stack2.ToArra

在.NET Windows Form Application中统一处理未处理的异常总结

在Windows Form Application中,异常若最终得不到处理,则会触发AppDomain.CurrentDomain.UnhandledException事件进行以进行最终的异常记录(使用此事件无法避免应用程序的终结).在此事件被触发之前,分以下情况可决定是否将异常继续传播. 主线程 使用Application.SetUnhandledExceptionMode(bool)方法预先设置处理模式: 若为UnhandledExceptionMode.ThrowException,则表示

Windows Form 中快捷键设置

在Windows Form程序中使用带下划线的快捷键只需要进行设置: 就能够工作. Windows Form 中快捷键设置

C# 简易计算器

编写如下界面的简易计算器界面代码: using System; using System.Windows.Forms; using exp; namespace calculator { public partial class Form1 : Form { public Form1() { InitializeComponent(); } enum symbol { plus,dec,mult,div}; private void button1_Click(object sender, Ev

mini dc与简易计算器 20165235

mini dc 任务内容 本次mini dc任务就是通过补充代码来实现整型数据的后缀表达式计算 相关知识 通过利用堆栈这一先进后出的数据结构来实现后缀表达式的计算.通过Stack<Integer> stack建立一个为int类型的堆栈stack.可以使用stack.push();来进行压栈,stack.pop();来进行弹栈. 通过调用private boolean isOperator (String token)来判断按空格标记分隔好的String数据是否为运算符. 使用正则表达式&quo

JAVA(利用jsp+javabean+servlet)实现简易计算器

代码解释基本都在代码中给出 1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loo

基于mini2440简易计算器

基于mini2440简易计算器使用的是数组实现,并非逆波兰式,因此功能不够强大,仅供驱动学习,以及C语言基础编程学习之用.有时间读者可以用逆波兰式来实现强大功能计算器,原理也很简单,建议读<c程序设计第二版>里面有算法的代码.读者自行研究.此程序基于电子相册的改进,触摸屏,LCD,字符现实,数字输入等等 mini2440  索尼X35   LCD液晶屏 主函数部分: #include "def.h" #include "option.h" #includ

如何用jsp实现一个简易计算器(三)

做这个jsp页面,主要是为了实现在同一个页面提交和接受数据的功能. 这个小程序存在很多不足,希望大家多多批评指正. <%@ page language="java" contentType="text/html;" pageEncoding="gbk"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://w