实验内容
实验思路
1. 通过高级程序设计语言(本次使用Java)实现对计算器功能的描述;
2. 通过图形界面框架(本次使用Java swing)实现图形界面可视化(GUI)。
涉及知识
数据结构(堆栈),前缀(波兰)/中缀/后缀(逆波兰)表达式的选择与使用;
优先级考虑,Java程序设计,Java swing框架等。
具体步骤
1. 学习计算器的几种算法实现,考虑运算符的优先级问题(带括号),决定使用 “双栈算符优先级” 法。
2. 使用 Java swing 实现 GUI :下载Java IDE: Eclipse,添加 WindowBuilder 工具包。
3. 学习数据结构中关于堆栈的相关知识,以及栈在Java程序中的实现。
①栈:栈(stack)是一种只允许在一端进行插入或删除的线性表。这次实验需要用双栈。
Java定义:
private Stack<Long> numberStack = null; //数字栈(用于存储表达式中的操作数),设为长整型,初始置空值
private Stack<Character> symbolStack = null; //符号栈(用于存储表达式中的运算符,包含括号),设为符号型,初始置空值
②表达式:使用双栈算符优先级法无需考虑后缀表达式的转换,直接使用中缀表达式即可。
③优先级考虑:
基本四则运算的优先级;
特别注意括号的优先级。
④Java程序设计:
学习Java的基本语法和格式;
了解关于栈、swing等包的接口调用;
//图形界面
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
//栈
import java.util.Stack;
//swing框架
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
写好注释方便以后调试。
⑤Java swing框架:
顶层容器 JFrame,中间容器 JPanel 等的创建和使用;
JFrame win = new JFrame("计算器");
JPanel pa = new JPanel();
基本组件 JButton 等的创建和使用;
JButton bn = new JButton("计算");
不可编辑信息的显示组件 JLabel 等的创建和使用;
JPanel pa = new JPanel();
可编辑单行文本组件 JTextField 等的创建和使用。
final JTextField formulaText = new JTextField(num, 20);
实验元素
1.实验总体框架:
2.具体算法:
①双栈算法
StringBuffer temp = new StringBuffer();
for (int i = 0; i < numStr.length(); i++) {
char s = numStr.charAt(i);
if (isNumber(s)) { //若当前字符是数字
temp.append(s); //加入到数字缓存中
}
else { //若当前字符是运算符(含括号)
String tempStr = temp.toString(); //转为字符串
if (!tempStr.isEmpty()) {
long num = Long.parseLong(tempStr); //转为长整型数
numberStack.push(num); //将长整型运算符压栈
temp = new StringBuffer(); //重置数字缓存
}
}
②运算符优先级比较算法
for (int i = 0; i < numStr.length(); i++) {
private boolean comparePri(char symbol) {
if (symbolStack.empty()) { //空栈返回true
return true;
}
char top = (char) symbolStack.peek(); //符号栈顶部对象
if (top == '(') {
return true;
}
//比较优先级
switch (symbol) {
case '(': //优先级最高
return true;
case '*': {
if (top == '+' || top == '-') //优先级比+和-高
return true;
else
return false;
}
case '/': {
if (top == '+' || top == '-') //优先级比+和-高
return true;
else
return false;
}
case '+':
return false;
case '-':
return false;
case ')': //优先级最低
return false;
case '=': //结束符
return false;
default:
break;
}
return true;
}
}
3.完整源码。
//图形界面
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
//栈
import java.util.Stack;
//swing框架
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Calculator {
//数字栈:用于存储表达式中的各个数字
private Stack<Double> numberStack = null;
//符号栈:用于存储运算符和括号
private Stack<Character> symbolStack = null;
//解析并计算四则运算表达式(含括号)
public double calculate(String numStr) {
numStr = removeStrSpace(numStr); //去除空格
//如果算术表达式尾部没有‘=’号,则在尾部添加结束符‘=’
if (numStr.length() > 1 && !"=".equals(numStr.charAt(numStr.length() - 1) + "")) {
numStr += "=";
}
//检查表达式是否合法
if (!isStandard(numStr)) {
System.err.println("错误:算术表达式有误!");
return 0;
}
//初始化栈
numberStack = new Stack<Double>();
symbolStack = new Stack<Character>();
//用于缓存数字,因为数字可能是多位的
StringBuffer temp = new StringBuffer();
//从表达式的第一个字符开始处理
for (int i = 0; i < numStr.length(); i++) {
char s = numStr.charAt(i); //获取一个字符
if (isNumber(s)) { //若当前字符是数字
temp.append(s); //加入到数字缓存中
}
else { //若当前字符是运算符(含括号)
String tempStr = temp.toString(); //将运算符缓存转为字符串
if (!tempStr.isEmpty()) {
double num = Double.parseDouble(tempStr); //将运算符字符串转为长整型数
numberStack.push(num); //将长整型运算符压栈
temp = new StringBuffer(); //重置数字缓存
}
//判断运算符的优先级,若当前优先级低于栈顶的优先级,则先把前面两个数字取出来
while (!comparePri(s) && !symbolStack.empty()) {
double b = numberStack.pop(); //出栈,取出数字,后进先出
double a = numberStack.pop();
//取出运算符进行运算,并把结果压栈
switch ((char) symbolStack.pop()) { //强制转换为字符串
case '+':
numberStack.push(a + b);
break;
case '-':
numberStack.push(a - b);
break;
case '*':
numberStack.push(a * b);
break;
case '/':
numberStack.push(a / b);
break;
default:
break;
}
} //while循环结束
if (s != '=') {
symbolStack.push(s); //将运算结果压入符号栈
if (s == ')') { //如果是括号,先去掉括号再弹出运算符
symbolStack.pop();
symbolStack.pop();
}
}
}
} //for循环结束
return numberStack.pop(); //返回计算结果
}
//去除字符串中的所有空格
private String removeStrSpace(String str) {
return str != null ? str.replaceAll(" ", "") : "";
}
//检查算术表达式的基本合法性,符合返回true,否则false
private boolean isStandard(String numStr) {
if (numStr == null || numStr.isEmpty()) //表达式不能为空
return false;
Stack<Character> stack = new Stack<Character>(); //用来保存括号,检查左右括号是否匹配
boolean b = false; //用来标记'='是否存在多个
for (int i = 0; i < numStr.length(); i++) {
char n = numStr.charAt(i);
//如果运算符中含有数字或者有连续的运算符则返回false
if (!(isNumber(n) || "(".equals(n + "") || ")".equals(n + "")
|| "+".equals(n + "") || "-".equals(n + "")
|| "*".equals(n + "") || "/".equals(n + "")
|| "=".equals(n + ""))) {
return false;
}
//将左括号压栈与后面的右括号进行匹配
if ("(".equals(n + "")) {
stack.push(n);
}
if (")".equals(n + "")) { //匹配括号
if (stack.isEmpty() || !"(".equals((char) stack.pop() + "")) //括号是否匹配
return false;
}
//检查是否有多个'='号,最后一个元素弹出前有括号则返回false
if ("=".equals(n +" ")) {
if (b)
return false;
b = true;
}
}
//可能会有缺少右括号的情况
if (!stack.isEmpty())
return false;
return true;
}
//判断字符是否是0-9的数字
private boolean isNumber(char num) {
if (num >= '0' && num <= '9')
return true;
return false;
}
//如果当前运算符比栈顶元素运算符优先级高则返回true,否则返回false
private boolean comparePri(char symbol) {
if (symbolStack.empty()) { //空栈返回true
return true;
}
//符号优先级说明(从高到低):
//第1级: (
//第2级: * /
//第3级: + -
//第4级: )
char top = (char) symbolStack.peek(); //查看符号堆栈顶部的对象
if (top == '(') {
return true;
}
//比较优先级
switch (symbol) {
case '(': //优先级最高
return true;
case '*': {
if (top == '+' || top == '-') //优先级比+和-高
return true;
else
return false;
}
case '/': {
if (top == '+' || top == '-') //优先级比+和-高
return true;
else
return false;
}
case '+':
return false;
case '-':
return false;
case ')': //优先级最低
return false;
case '=': //结束符
return false;
default:
break;
}
return true;
}
//主方法实现图形界面
public static void main(String args[]) {
String num = null;
//创建一个窗口
JFrame fra = new JFrame("计算器"); //GUI名字
Container con = fra.getContentPane();
JPanel pan = new JPanel();
pan.add(new JLabel("输入运算式:")); //添加一个标签
final JTextField formulaText = new JTextField(num, 20); //算式输入框
pan.add(formulaText);
pan.add(new JLabel("="));
final JTextField resultText = new JTextField(10); //结果文本框
pan.add(resultText);
con.add(pan); //将组件pan放置到容器con中
//界面布局
JButton bn = new JButton("计算"); //实例化按钮对象
con.add(bn, BorderLayout.EAST); //将按钮添加到右边
fra.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //设置关闭窗口退出程序
fra.pack(); //自动调整窗口大小
fra.setLocationRelativeTo(null); //设置窗口居中于屏幕
fra.setVisible(true); //显示窗口
//添加按钮点击事件
bn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { //每当按钮点击时调用该方法
Calculator cal = new Calculator(); //计算器操作
String numStr = formulaText.getText(); //获得算式文本框中的文字
double result = cal.calculate(numStr); //计算算式的结果
numStr = cal.removeStrSpace(numStr); //去空格
formulaText.setText(numStr); //将去空格的算式放回算式文本框中
resultText.setText(result + ""); //在结果文本框中显示结果
}
});
}
}