用Java实现计算器

在学习《数据结构》这门课的时候,老是会想到模拟计算器的运算。虽说实验里面也有设计逆波兰计算器的题目,但它只考察了栈和队列的操作思想,没有考虑到运算符的优先级以及复杂组合情况(比如多层括号),因此其实用性并不大。
今天试着写了一下,看似很简单,还是花费了一段时间的。

处理纯符号(+-)字符串(1)

这是最简单的情况。
1. 首先+-的运算等级最低,而且没有括号的限制。
2. 其次我们只需要把相应的数字部分读到一个对象中,运算符存放到另外一个对象里面。
3. 最后将字符串数字转换为int型,运算符转换为char型,依次计算得出结果。

下面直接贴代码了:
public static double HandleJJFH(String s) {
        int q = 0;
        int i = 0;

        Queue<Double> Q = new LinkedList<Double>();
        String strnum = null;
        String fuhao = new String("");
        while (i < s.length()) {
            if (JudgeNumAndDot(s.charAt(i)) && i + 1 != s.length()) {
                i++;
                continue;
            }
            if (JudgeFH(s.charAt(i)) || (i + 1 == s.length())) {
                //截取数字部分
                if (i + 1 == s.length())
                    strnum = s.substring(q, i + 1);
                if (JudgeFH(s.charAt(i))) {
                    fuhao += s.charAt(i);
                    strnum = s.substring(q, i);
                }
                Q.offer(StrToNum(strnum));
                q = i + 1;
                i++;
            }
        }

        //取出头部元素
        Double a = Q.poll();
        i = 0;
        while (!Q.isEmpty()) {
            a = TwoCalu(a, fuhao.charAt(i), Q.poll());
            i++;
        }
        return a;
    }

需要注意的是Stack和Queue的泛型参数必须是类类型
用到的辅助函数如下:

public static double TwoCalu(double a, char c, double b);
public static double StrToNum(String s);

同样的*/运算符也适用


处理带有(+-*/)字符串(2)

*/的运算等级要比+-的高,因此情况稍微复杂一点。
大致思路是:将处于相邻+-之间的字符串截取下来。那么截取下来的字符串只有*/运算符或者没有,这样情况便简化成了处理纯符号(+-)字符串(1)
直接贴代码:

    //该方法可能要做优化
    public double HandleAll_NonBracket(String s) {
        if(s.charAt(0) == '-' && JudgeStrnum(s.substring(1,s.length())))//可能s为-3
            return StrToNum(s);

        int i = 0, q = 0, countJJ = 0;
        String CCstr = "", fuhao = "";

        Queue<Double> Q = new LinkedList<Double>();
        while (i < s.length()) {
            //数字或*/
            if (JudgeNumAndDot(s.charAt(i)) || JudgeCC(s.charAt(i))) {
                i++;
                continue;
            }

            //发现+/-运算符
            if (JudgeJJ(s.charAt(i))) {
                countJJ++;
                fuhao += s.charAt(i);
                //发现第一个+-
                if (countJJ == 1) {
                    CCstr = s.substring(0, i);
                    //如果含有*/
                    if (JudgeStrcc(CCstr))
                        Q.offer(HandleJJFH(CCstr));
                    else
                        Q.offer(StrToNum(CCstr));
                }
                //发现非第一个+-
                else {
                    //截取是否含有连续*/的字符串
                    CCstr = s.substring(q + 1, i);

                    //中间纯数字
                    if (JudgeStrnum(CCstr))
                        Q.offer(StrToNum(CCstr));

                    //中间包含*/
                    if (JudgeStrcc(CCstr))
                        Q.offer(HandleJJFH(CCstr));
                }
                q = i;
                i++;
            }
        }

        if (countJJ == 0)
            return HandleJJFH(s);
        CCstr = s.substring(q + 1, s.length());
        //如果含有*/
        if (JudgeStrcc(CCstr))
            Q.offer(HandleJJFH(CCstr));
        else//如果纯数字
            Q.offer(StrToNum(CCstr));

        //取出头部并且返回头部值
        double a = Q.poll();
        i = 0;
        while (!Q.isEmpty()) {
            a = TwoCalu(a, fuhao.charAt(i), Q.poll());
            i++;
        }
        return a;
    }

用到的辅助函数如下:

public static boolean JudgeNum(char c);
public static boolean JudgeCC(char c);
public static double HandleJJFH(String s);
public static double StrToNum(String s);
public static double TwoCalu(double a,char c,double b);

处理带( )的字符串(3)

带有( )的字符串运算优先级是最高的,但无非也就是首先考虑它的运算,与*/类似。
我们首先考虑只有一对括号的字符串:
带( )的String a截取下来,计算得出结果double b,并将结果b转换为String类型返回替代原有a的位置,这样情况便简化成了处理带有(+-*/)字符串(2)
Queue< Kuohao > L ,Stack< Kuohao > R是需要借助的数据结构。L用于记录左括号的位置,并依次入队列;R用于记录右括号的位置,并且依次入栈。这样左括号和右括号便能够匹配成功。截取括号便有了成功的把握。

多括号的字符串处理

涉及到多括号处理的时候,最简单的方法是使用递归。

public double HandleBracket(String s) {
        Queue<Kuohao> L = new LinkedList<Kuohao>();
        Stack<Kuohao> R = new Stack<Kuohao>();
        String t = "";
        int num = KuohaoNum(s, L, R);
        if (num == 0)
            return HandleAll_NonBracket(s);
        else {
            Kuohao q = L.poll();
            Kuohao p = R.pop();
            String str = s.substring(q.index + 1, p.index);//将最外层括号去掉
            t += s.substring(0, q.index);//括号左边部分字符串
            double temp = HandleBracket(str);//处理左右括号中间字符串
            String midres = NumToStr(temp);
            if (t.length() == 0)//例如(1+2*3),那么t=1+2*3
                t = t + midres;
            if (t.charAt(t.length() - 1) == '+' && midres.charAt(0) == '-')//可能会出现123+ -4的情况
                t = t.substring(0, t.length() - 1);
            if (!t.equals(midres))//例如(1+2.5*4),那么t=11,midres=11,但结果只能返回7而不是14
                t = t + midres;

            t = t + s.substring(p.index + 1, s.length());
            return HandleAll_NonBracket(t);
        }
    }

用到的辅助函数如下:

public double KuohaoNum(String s,Queue<Kuohao> left,Stack<Kuohao> right);
public double HandleAll(String s);
public static String NumToStr(double t);

计算器的运算过程大致就是这样了!

源代码


不足之处

  • 对于用户输入的字符串并未进行合法性的检查,因此计算时程序可能会出现异常或报错。
  • 递归虽然通俗易懂,但是执行效率低,方法需要改进。
  • 一些辅助函数过于冗余,耦合性较高。
  • 最终程序应以图形界面的方式显示。

感触

看似简单的计算器实则大有门道。对于非程序员而言,他们可能对实现这样的功能不屑一顾,但对程序员却是极大的挑战。就像现在的我,熬夜的痛苦谁人知!果然是术业有专攻,以后不敢小瞧其它专业了!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值