实验一 简单计算器的实现

实验要求:

1)能通过设计的按钮控件输入并实现算术表达式,表达式在文本框中显示,运算结果输出显示;保存和浏览历史运算记录;

2)能够检验算术表达式的合法性;

3)能够实现混合运算的求解,算术表达式中包括加、减、乘、除、括号等运算符;

4)要求交互界面友好,程序健壮。

实验步骤

1、确定如何实现基本四则运算:

      涉及运算符:+,-,*,/,(,)

      首先是将输入的表达式存放在字符串str中,当遇到输入的字符为"="时,开始逐个分析字符串str中的字符。

      如果遇到的字符是数字,则直接放入堆栈stack2中;

      如果是运算符,则先分析堆栈stack1中的情况:若stack1为空,则直接放入stack1中,若不为空,则将运算符与stack1中顶部运算符的优先级进行比较,栈顶优先级不小于当前运算符,则弹出栈顶,并在stack2中依次取出两个数字进行运算,运算后将结果存入stack2栈(符号为'+'||'-',且栈顶不为'('||'+'||'-'弹出,符号为'*'||'/'入栈,符号为')'弹出,直到peak优先级小于符号)并继续将当前运算符与stack1栈顶优先级进行比较。当遇到运算符是"="时,stack1全部出栈,依次进行运算。

具体实现代码:

public class Program {
    //存放运算符;
    Stack<Character> stack1 = new Stack<>();
    //存放中间结果;
    Stack<Double> stack2 = new Stack<>();
    Actionlistener al = new Actionlistener();

    //判断运算符优先级;
    public boolean judge(char ch) {
        if (stack1.empty()) {
            return true;
        }
        //返回值为true,则入栈,反之出栈
        if (ch == '(') {
            return true;
        }
        if (ch == ')') {
            return false;
        }
        //比较优先级,当优先级小于peak时,弹出;
        if ((ch == '*' || ch == '/')) {
            if (stack1.peek() != '*' && stack1.peek() != '/') {
                return true;
            }
            return false;
        }
        if (ch == '+' || ch == '-') {
            if (stack1.peek() == '/' || stack1.peek() == '*' || stack1.peek() == '+' || stack1.peek() == '-') {
                return false;
            }
            return true;
        }
        return true;
    }

    //stack1入栈及其条件;
    public void pushChar(char ch) {
        //如果符号为'=',代表表达式已经输完,stack1全部出栈;
        if (ch == '=') {
            while (!stack1.empty()) {
                stack2.push(cacular());
            }
        }
        //栈为空,直接入栈;
        if (stack1.empty()) {
            stack1.push(ch);
        }
        //判断优先级,如果为true,则入栈;
        else if (judge(ch)) {
            stack1.push(ch);
        }
        //出栈,直到栈顶优先级小于ch,或为空;
        else {
            while ((!(stack1.empty())) && (!judge(ch)) && (stack1.peek() != '(')) {
                stack2.push(cacular());
                System.out.println(al.show);
            }
            if ((!stack1.empty()) && stack1.peek() == '(') {
                stack1.pop();
            }
            if (ch != ')') {
                stack1.push(ch);
            }
        }
    }

    //计算中间结果;
    public double cacular() {
        double a1 = 0, a2 = 0;
        switch (stack1.pop()) {
            case '+':
                return stack2.size() > 1 ? stack2.pop() + stack2.pop() : stack2.pop();
            case '-':
                return stack2.size() > 1 ? -(stack2.pop() - stack2.pop()) : -stack2.pop();
            case '*':
                return stack2.size() > 1 ? stack2.pop() * stack2.pop() : stack2.pop();
            case '/': {
                if (stack2.size() > 1 && stack2.peek() != 0) {
                    a1 = stack2.pop();
                    a2 = stack2.pop();
                    return a2 / a1;
                }
                if (stack2.peek() == 0) {

                    stack1.removeAll(stack1);
                    al.left.removeAll(al.left);

                    return 0;
                }
                if (stack2.size() == 1) return stack2.pop();
            }
            case '(':
                return stack2.pop();
            default:
                break;
        }
        return stack2.pop();
    }

}

      一些问题和注意事项:
1)、注意输入等号后,使stack1中符号全部出栈时,要用循环来调用,不然就只进行了一次运               算,若还有符号就无法参与运算
2)、出栈条件:当栈不为空&&judge()==false&&栈顶不为'('
3)、括号,要考虑 当出栈符号"("的时候,应当如何操作:忽视,并且 stack1取栈中下一个元素。4)、输入括号后所有答案均为0(?):

         测试时主函数在将字符串入栈的时候,应当考虑当符号为'(',')','='这三种特殊情况,当输入了           这三个符号后就应进入下一循环,否则stack2中会错误的入栈一个0。
5)、操作数超过3个,多项式运算出错:

         注意当操作stack1中的运算符时,里面的符号已经弹出来了,再进行栈顶是否是'('的判断                 时,由于栈可能是空的,所以会出错

6)、当stack2中只有一个操作数,且stack1中有除括号以外的操作符时,运算后后返回的结果应是stack2栈顶的元素。

2、JAVA Swing搭建基本计算器页面

 主要涉及的类:

JFrame – java的GUI程序的基本思路是以JFrame为基础,它是屏幕上window的对象,能够最大化、最小化、关闭。

JPanel – Java图形用户界面(GUI)工具包swing中的面板容器类,包含在javax.swing 包中,可以进行嵌套,功能是对窗体中具有相同逻辑功能的组件进行组合,是一种轻量级容器,可以加入到JFrame窗体中。。

JTextField –一个轻量级组件,它允许编辑单行文本。

JButton – JButton 类的实例。用于创建按钮类似实例中的 "Login"。

1)、首先用JFrame搭建基本框架

         设置好计算器的名称,大小,以及位置,并设置窗体可见

 JFrame jf = new JFrame("简单计算器");
        jf.setBounds(200,200,350,470);
        //设置同步开关,关窗口的同时停止程序运行
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

2)、向搭建好的框架中添加组件

         利用JPanel新建一个面板jpanel,并设置布局为边框布局管理器(BorderLayout)

在这里插入图片描述

          此后的所有组件都将安装在这个面板上

          利用JTextField新建一个文本框ans,作为计算器显示表达式的地方,并将其存放在jpanel的NORTH区域

//创建文本框;
JTextField ans = new JTextField();
ans.setFont(new Font("", Font.PLAIN, 20));
//文本框中文本左对齐; 
ans.setHorizontalAlignment(SwingConstants.LEFT);
jpanel.add(ans, BorderLayout.NORTH);

 再创建2个面板panel,panel1用于装计算器按钮(这里我为了排版美观创建了两个面板)

panel用于存放数字和四则运算符按钮,界面布局为网格布局管理器(GridLayout);panel1用于存放"AC"和"=",并将panel放于CENTRAL,panel1放于RIGHT

创建按钮

 //设置按钮;
 JButton n1 = new Button("1");

一共所需按钮:数字按钮0~9,符号:+,-,*,/,(,),=,AC,C,H,.,D

D:删除一个输入字符

H:查看历史记录

3)、建立事件监听
在事件处理的过程中,主要涉及三类对象。
Event(事件):用户对组件的一次操作称为一个事件,以类的形式出现。例如,键盘操作对应的事件类是 KeyEvent。
Event Source(事件源):事件发生的场所,通常就是各个组件,例如按钮 Button。
Event Handler(事件处理者):接收事件对象并对其进行处理的对象事件处理器,通常就是某个 Java 类中负责处理事件的成员方法。
此处的计算器触发事件是鼠标点击按钮:

当鼠标点击按钮时,就向字符串str中输入一个对应字符,当点击代表"="的 按钮时,就调用上述Program类进行运算。将结果作为字符串存入字符串show中,并显示在文本框中,最后将该字符串放入列表list中,用于存储历史记录。此后,所有字符串清零,避免干扰下一次运算。

输入字符时需要注意监控输入的表达式是否符合规则,是否能进行正常运算。如果符合规则,则存入字符串show中,并进行压栈处理

st.pushChar(str.charAt(i));
show = show + str.charAt(i);
i++;

4)、界面美观设计

//不显示聚焦点
n0.setFocusPainted(false);
//不显示边框
n0.setBorderPainted(false);
//设置字体大小
n0.setFont(new Font("", Font.PLAIN, 30));
//设置背景色
n0.setBackground(new Color(153, 204, 204));
//设置前景色
n0.setForeground(new Color(255, 255, 255));

注意:

1)、界面颜色是基于RGB的参数进行设置

2)、若list为空时,此时调用会提示指针出错,为避免出错,应先判断list是否为空,再进行调用

if (!click.list.isEmpty()) {
                    if (click.i < 0) {
                        click.i = click.list.size() - 1;
                    }
                    ans.setText(click.list.get(click.i));
                    click.i = click.i - 1;
                }
                ans.setText("没有历史记录,请输入");

3、细节化考虑,健壮程序

1)、小数点前后自动补零问题

当遇到小数点,且show中最后一个字符是非操作数时,在将小数点加入show之前先添加一个''0"字符;

if (str.charAt(i) == '.') {
    show = show + "0.";
    j = j + 2;
    i++;
}

当遇到运算符,且show中最后一个字符是小数点时,在运算符加入show之前先添加一个''0"字符;

if (j >= 1 && show.charAt(j - 1) == '.') {
    show = show + '0';
}

2)、同一操作数中多个小数点

运用while循环,只输入遇到的第一个小数点,其余小数点被舍弃

第一种情况,遇到小于1的数

if (str.charAt(i) == '.') {
    show = show + "0.";
    j = j + 2;
    i++;
    while ((str.charAt(i) <= '9' && str.charAt(i) >= '0') || (str.charAt(i) == '.')) {
           if (str.charAt(i) <= '9' && str.charAt(i) >= '0') {
              a = a + ((str.charAt(i) - '0') / b);
              show = show + str.charAt(i);

              i++;
              j++;
              b = b * 10;
            }
            if (str.charAt(i) == '.') {
                        i++;
           }
    }
    st.stack2.push(a);
    a = 0;
    b = 10;
}

第二种情况,遇到大于1的数

if (str.charAt(i) <= '9' && str.charAt(i) >= '0') {
    while (str.charAt(i) <= '9' && str.charAt(i) >= '0') {
          a = (str.charAt(i) - '0') + a * 10;
          show = show + str.charAt(i);
          j++;
          i++;
    }
    //遇到第一个点,可以输入;
    if (str.charAt(i) == '.') {
        show = show + '.';
        j++;
        i++;

        while ((str.charAt(i) <= '9' && str.charAt(i) >= '0') || (str.charAt(i) == '.')) {
               if (str.charAt(i) == '.') {
                  i++;
               }

               if (str.charAt(i) <= '9' && str.charAt(i) >= '0') {
                  a = a + ((str.charAt(i) - '0') / b);
                  show = show + str.charAt(i);
                  j++;
                  i++;
                  b = b * 10;
               }
        }
    }
                st.stack2.push(a);
}

3)、连续输入多个操作符

只取第一个操作符进行运算,其余的将被舍弃

if (str.charAt(i) != '(' && str.charAt(i) != ')' && !((str.charAt(i) <= '9' && str.charAt(i) >= '0') || (str.charAt(i) == '.'))) {
   st.pushChar(str.charAt(i));
   show = show + str.charAt(i);
   j++;
   i++;

   while ((i < str.length() - 1) && ((str.charAt(i + 1) == '+' || str.charAt(i + 1) == '-' || str.charAt(i + 1) == '*' || str.charAt(i + 1) == '/'))) {
                    i++;
   }
}

4)、左右括号配对问题 

左右括号必须匹配,多余的右括号将被舍弃

建立一个列表bracket来进行左右括号配对。当遇到左括号时,向列表中输入一个"(",当遇到右括号时,先检查bracket中有无与之配对的左括号,如有就进行相关运算,并删去bracket中一个左括号;若没有,则舍去,不参与运算。

if (str.charAt(i) == '(' || (str.charAt(i) == ')' && bracket.contains("("))) {
                if (str.charAt(i) == '(') {
                    bracket.add("(");
                    st.pushChar(str.charAt(i));
                    show = show + str.charAt(i);
                    i++;
                }
                if (str.charAt(i) == ')') {
                    bracket.remove(bracket.size() - 1);
                    st.pushChar(str.charAt(i));
                    show = show + str.charAt(i);
                    j++;
                    i++;
                }

            }

            while (str.charAt(i) == ')' && !bracket.contains("(")) {
                i++;
            }

设置一个存放左括号指针的列表left,当有右括号与之配对时,就删除第一组数据,如果最后还省有数据,则根据地址指针删除show中多余左括号

 while (!left.isEmpty()){
            show=show.substring(0,left.get(0))+show.substring(left.get(0)+1,j);
            j--;
            left.remove(0);
            if(!left.isEmpty()){
                left.set(0,left.get(0)-1);
            }

5)、除数为0

停止后面的计算,清空相关堆栈

if(stack2.peek()==0) {
                    al.show="出错,被除数不能为0";
                    stack1.removeAll(stack1);
                    al.left.removeAll(al.left);

                    return stack2.pop();
                }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值