计算器的设计与实现
实验要求
①能通过设计的按钮控件输入并实现简单算术运算,要求表达式在编辑框中显示,能将运算结果,输出在编辑框内显示;
②能够实现混合运算的求解,算术表达式中包括加、减、乘、除、括号等运算符;并且能够识别括号,优先级正确。
③并保存历史的表达式运算记录。
**技术准备:**图形界面的开发;
**难点:**中缀表达式→后缀表达式,后缀表达式的计算。
实验准备
针对本实验难点,有三种解决方法,下面将一一列举。
方法一:操作符栈、队列实现法
*中缀表达式→后缀表达式
(1)创建后缀表达式队列:postQueue,用于存储逆波兰表达式。
(2)创建操作符栈:opStack,对用户输入的操作符进行处理,用于存储运算符。
(3)从左向右依次读取算术表达式的元素X,分以下情况进行不同的处理:
①如果X是操作数,直接入队;
②如果X是运算符,再分以下情况:
a)如果栈为空,直接入栈。
b)如果X=="(",直接入栈。
c)如果X==")",则将栈里的元素逐个出栈,并入队到后缀表达式中,直到第一个配对的"(“出栈。(注:”(“和”)"都不入队)
③最后将栈中剩余的操作符全部入队。
*计算后缀表达式
(1)创建一个结果栈Res_Stack,用于存放计算的中间过程的值和最终结果。
(2)从左开始向右遍历后缀表达式的元素。
(3)如果取到的元素是操作数,直接入栈Res_Stack,如果是运算符,从栈中弹出2个数进行运算,然后把运算结果入栈。
(4)当遍历完后缀表达式时,计算结果就保存在栈里了。
方法二:双栈算符优先级法
(1)双栈算符优先级法为了实现表达式的求值,需要设置两个栈:
一个是运算符栈OP,用于寄存运算符;
另一个成为操作数栈OPND,用于寄存运算符和运算结果。
(2)自左向右扫描表达式中的每一个字符:
①当扫描到的是运算数,则将其压入栈OPND;
②当扫描到的是运算符时:
如这个运算符比OP栈顶运算符的优先级高,则入栈;
如这个运算符比OP栈顶运算符的优先级低,则从OPND栈中弹出两个运算符,从栈OP中弹出栈顶运算符进行运算,并将运算结果压入栈OPND。
(3)继续处理当前字符,直到遇到结束符而止。
方法三:用二叉树来求解后缀表达式的值
(1)首先准备一个二叉树节点栈s
(2)从左开始向右遍历表达式的元素
(3)新建一个树节点p,值为当前元素的值,如果取到的元素是操作数,直接把p入栈s,如果是运算符,从栈中弹出2个节点,把第一个弹出的节点作为p的右子树,第二个弹出的节点作为p的左子树,然后把p入栈。
(4)当遍历完后缀表达式时,树的根节点就保存在栈里了。
实验过程
本实验我采用Java来实现,由于对Java接触时间不长,对Java的使用还停留在简单的代码编写上。因此,需要提前查阅资料了解Java的监听器和图形用户界面(GUI)的使用方法。
*通过查阅相关资料,了解了一些Java图形用户界面中常用的组件,例如文本框(JTextField)、标签(JLable)等。
还有Java布局管理的相关知识,例如FlowLayout(流布局)、BorderLayout(边界布局)、GridLayout(网络布局)等。
初步了解了GUI相关知识后,便可以着手进行本实验的核心,也就是难点部分的代码设计。
本次实验我用三部分来实现。
Process类
用Porcess类来实现计算器的运算功能,即中缀表达式到后缀表达式的转换,和后缀表达式的计算。
部分重要函数代码段如下:
priority函数和testPriority函数,实现对不同运算符的优先级判断
// 设定优先级
private static int priority(char a) {
if (a == '*' || a == '/')
return 2;
else if (a == '+' || a == '-')
return 1;
else
return 0;
}
// 优先级判断
private static boolean testPriority(Character c1, char c2) {
if (c1 == null) {
return false;
}
return priority(c1) >= priority(c2);
}
testNumber函数,实现判断是否为数字
// 判断是否为数字
private static boolean testNumber(char a) {
if (a >= '0' && a <= '9') {
return true;
}
return false;
}
calculation函数,完成计算
private static double calculation(double num1,double num2,String op) {
switch(op) {
case "+":
return num2+num1;
case "-":
return num2-num1;
case"*":
return num2*num1;
case"/":
return num2/num1;
default:
return 0;
}
}
convert函数,完成中缀表达式向后缀表达式的转换
//中缀表达式转换为后缀表达式
public double convert(String expression) {
Stack stack=new Stack();
StringBuilder sb=new StringBuilder();
char[] ch = expression.toCharArray(); // 字符串转换为字符数组
for(int i=0;i<ch.length;++i) {
char c=ch[i];
if(testNumber(c)) {
sb.append(c);
}
else if(leftBracket(c)) {
stack.push(c);
}
else if(rightBracket(c)) {
while(!leftBracket((char)stack.peek())) {
sb.append(stack.pop());
}
stack.pop();
}
else {
while(stack.size()!=0&&testPriority((Character)stack.peek(),c)) {
sb.append(stack.pop());
}
stack.push(c);
}
}
while(stack.size()!=0) {
sb.append(stack.pop());
}
return convertResult(sb);
}
convertResult函数,实现后缀表达式的计算
public double convertResult(StringBuilder sb) {
String str =sb.toString();
str=str.replace(""," ").trim();
String s[]=str.split(" ");
Stack<Double>exp=new Stack<>();
double result=0;
for(int i=0;i<s.length;i++) {
if(!testOperator(s[i])) {
exp.push(Double.parseDouble(s[i])); //若不是运算符,则压栈
}
else {
result=calculation(exp.pop(),exp.pop(),s[i]); //若为运算符,取出栈顶两数运算
exp.push(result);
}
}
return result;
}
Calculation类
Calculation类来完成计算器界面的设计和按键输入
部分重要代码如下
*创建相关按钮
//创建按钮
String [][] buttons= {
{
"7",<