首页 > 其他分享 >实验报告

实验报告

时间:2023-10-12 22:23:50浏览次数:28  
标签:实验报告 opStack String infix Result new btn

一、总体思路

利用Java,继承JFrame类实现图形用户页面框架及UI设计布局,继承了ActionListener接口实现按钮事件监听

用户按钮输入结束后获取等号前的字符串,采用双栈法将中缀表达式转化为后缀表达式后计算后缀表达式(单独写出牛顿迭代法计算开方运算)结果并输出在文本框中

二、UI设计

 

class Calculator1 extends JFrame implements ActionListener {
private JPanel jp_north = new JPanel();
//JPanel是java图形用户界面GUI工具包swing中的面板容器类,包含在javax.swing包中,可以进行嵌套,功能是对窗体中具有相同逻辑功能组件进行组合
private JTextField resultText = new JTextField("0"); //创建一个输入框
private JPanel jp_center = new JPanel();
private String input = "";

public Calculator1() throws HeadlessException{//异常HeadException在不支持键盘、显示器或鼠标的环境中调用依赖于键盘、显示器或鼠标的代码时引发
this.init();
this.addNorthComponent();
this.addCenterComponent();
}
//建立计算器界面布局
public void init() {
this.setTitle("计算器");//界面名称
this.setSize(800,800);//界面大小
this.setLayout(new BorderLayout());//建立布局
this.setResizable(false); //计算器界面被拉伸
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//使用system exit方法退出应用程序
}

//向顶层容器添加组件
public void addNorthComponent()
{
this.resultText.setPreferredSize(new Dimension(700,150));
resultText.setHorizontalAlignment(JTextField.RIGHT);;//文本框内容右对齐
resultText.setEditable(false);//文本框不允许修改结果
resultText.setFont(new Font("隶书",Font.PLAIN,36));
jp_north.add(resultText);
this.add(jp_north,BorderLayout.NORTH);//把顶层界面添加到整体布局的上层
}

//向中间层容器添加组件
public void addCenterComponent()
{
String btn_next = "C()/789*456-123+√0.=";//存储中间按钮的所有字符

this.jp_center.setLayout(new GridLayout(5,4,3,3));//设置中间按钮的行列(5行4列的网格布局)
this.add(jp_center,BorderLayout.CENTER);//中层布局添加到整体布局的中层
this.setVisible(true);
String regex = "[\\+\\-\\*\\/\\√\\(\\)\\.\\=C]";
for(int i = 0;i < 20;i++)
{
String temp = btn_next.substring(i,i+1);
JButton btn = new JButton();
btn.setFont(new Font("宋体",Font.BOLD,30));
btn.addActionListener(this);//每个按钮都注册事件监听器
btn.setText(temp);
btn.setBackground(Color.PINK);
jp_center.add(btn);
if(temp.matches(regex)) {
btn.setFont(new Font("粗体",Font.BOLD,30));
btn.setForeground(Color.RED);
}
}
}
三、程序流程图

四、主体代码
package p1;
import java.util.Stack;
import java.util.Objects;
import java.util.Arrays;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

//UI设计
class Calculator1 extends JFrame implements ActionListener {
private JPanel jp_north = new JPanel();
//JPanel是java图形用户界面GUI工具包swing中的面板容器类,包含在javax.swing包中,可以进行嵌套,功能是对窗体中具有相同逻辑功能组件进行组合
private JTextField resultText = new JTextField("0"); //创建一个输入框
private JPanel jp_center = new JPanel();
private String input = "";

public Calculator1() throws HeadlessException{
this.init();
this.addNorthComponent();
this.addCenterComponent();
}
//建立计算器界面布局
public void init() {
this.setTitle("计算器");//界面名称
this.setSize(800,800);//界面大小
this.setLayout(new BorderLayout());//建立布局
this.setResizable(false); //计算器界面被拉伸
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//使用system exit方法退出应用程序
}

//向顶层容器添加组件
public void addNorthComponent()
{
this.resultText.setPreferredSize(new Dimension(700,150));
resultText.setHorizontalAlignment(JTextField.RIGHT);;//文本框内容右对齐
resultText.setEditable(false);//文本框不允许修改结果
resultText.setFont(new Font("隶书",Font.PLAIN,36));
jp_north.add(resultText);
this.add(jp_north,BorderLayout.NORTH);//把顶层界面添加到整体布局的上层
}

//向中间层容器添加组件
public void addCenterComponent()
{
String btn_next = "C()/789*456-123+√0.=";//存储中间按钮的所有字符

this.jp_center.setLayout(new GridLayout(5,4,3,3));//设置中间按钮的行列(5行4列的网格布局)
this.add(jp_center,BorderLayout.CENTER);//中层布局添加到整体布局的中层
this.setVisible(true);
String regex = "[\\+\\-\\*\\/\\√\\(\\)\\.\\=C]";
for(int i = 0;i < 20;i++)
{
String temp = btn_next.substring(i,i+1);
JButton btn = new JButton();
btn.setFont(new Font("宋体",Font.BOLD,30));
btn.addActionListener(this);//每个按钮都注册事件监听器
btn.setText(temp);
btn.setBackground(Color.PINK);
jp_center.add(btn);
if(temp.matches(regex)) {
btn.setFont(new Font("粗体",Font.BOLD,30));
btn.setForeground(Color.RED);
}
}
}
//牛顿迭代法计算开方
//牛顿迭代法算开方
public double sequareRoot (double n) {
if(n < 0) {
return Double.NaN;
}else {
double err = 1e-15;
double root = n;
while(Math.abs(n - root * root) > err) {
root = (n/root + root) / 2;
}
return root;
}
}
//用户按下等号按钮后获取存储用户输入的字符串input作为参数传入此方法,用户输入的运算表达式默认为中缀形式,
//此方法利用双栈法将其转化为计算机可以识别的后缀表达式
//中缀转后缀 3+(5*7)
private String[] houzhui(String infix) {
String s = "";
Stack<String> opStack = new Stack<String>();//第一个栈使操作符栈,对用户输入的操作符进行处理,用于存储运算符
Stack<String> postQueue = new Stack<String>();//第二个使后缀表达式栈,用于存储数字和处理后的操作符
System.out.println("中缀:"+infix.substring(0,infix.length()-1));
for(int i=0;i < infix.length();i++)
{
if("1234567890.".indexOf(infix.charAt(i))>=0){//遇到数字直接进栈
s="";//infix.charat()函数用于定位字符串infix 某个位置的值
for(;i<infix.length() && "0123456789.".indexOf(infix.charAt(i)) >=0;i++) {
s = s + infix.charAt(i);
//当前字符为数字时向后继续获取数字到栈中
}//当前i值的位置是非数字,为了避免跳过对其的处理,进行i--运算
i--;
//同一运算数的所有字符获取到字符串s后压入栈
postQueue.push(s);
//左括号直接压入操作符栈
}else if("(".indexOf(infix.charAt(i))>=0) {
opStack.push(String.valueOf(infix.charAt(i)));
//遇到右括号要将操作符栈的栈顶元素循环出栈直到遇到左括号,同时删除左括号
}else if(")".indexOf(infix.charAt(i))>=0) {
while ( !opStack.peek().equals("(")){
postQueue.push(opStack.pop());
}
opStack.pop();
// while (!opStack.empty())
// postQueue.push(opStack.pop());
}else if("+-*/√".indexOf(infix.charAt(i))>=0)//遇到加减乘除等两位预算操作符
{//先看操作符栈是否为空或者栈顶为左括号 若是如此 则直接入栈
if(opStack.empty() || "(".contains(opStack.peek())) {
opStack.push(String.valueOf(infix.charAt(i)));
}else {//如果不是 要比较栈顶操作符和当前操作符的优先级,栈顶为同级或高级时,让栈顶元素先进入后缀表达式栈,
//直到栈为空或者栈顶为低优先级时当前元素入栈 (*/的优先级要高于+-)
// stack().pop是获取栈顶元素并且弹出 stack().peek()是获取栈顶元素不弹出
//rule 定义了一个操作符的优先级规则 */√要优于+-的运算 即先入栈
boolean rule = ("√*/+-".contains(opStack.peek()) && "+-".indexOf(infix.charAt(i)) >=0)
|| ("√*/".contains(opStack.peek()) && "*/".indexOf(infix.charAt(i)) >=0);
while (!opStack.empty() && rule) {
postQueue.push(opStack.peek());
opStack.pop();
rule = ("√*/+-".contains(opStack.peek()) && "+-".indexOf(infix.charAt(i)) >=0)
|| ("√*/".contains(opStack.peek()) && "*/".indexOf(infix.charAt(i)) >=0);
}
//否则 栈为空或者当前操作符的优先级高于操作符栈顶的元素 则当前操作符入栈
opStack.push(String.valueOf(infix.charAt(i)));
}
}
}
//遍历完中缀表达式后 让此时的操作符栈的栈顶元素依次进入后缀表达式栈
while(!opStack.empty()) {
postQueue.push(opStack.pop());
}
//获取后缀表达式的大小 并使用循环让栈中元素倒序依次填入字符串型数组suffix
String[] suffix = new String[postQueue.size()];
for (int i=postQueue.size() - 1;i>=0;i--) {
suffix[i] = postQueue.pop();
}
//打印后缀表达式的数组
System.out.println("后缀:"+Arrays.toString(suffix.clone()));
return suffix;
}

//转化为后缀表达式,并返回最终结果
//上一个方法已经得到了后缀表达式且其存储在数组suffix中,计算时就是把后缀表达式的每一个字符从数组中一个一个取出
//看它是数字还是运算符,数字直接进入结果栈,运算符判断是单目还是双目运算符,双目运算符弹出结果栈顶两个数字,单目运算符弹出结果栈顶一个数字,计算后再次入栈
  //运算符中,"-","√"为特殊字符,根号一个是除数为零时显示ERROR,一个是调用了牛顿迭代法运算;“-”这里可能是减号,也可能是负号即取反号
    public String Result(String[] suffix)
{
//字符串栈Rsult,数字没有遇到运算符时进入结果栈顶等待出现运算符时弹出运算,运算后结果再次存储到结果栈
double label=0;
Stack<String> Result = new Stack<String>();
for(int i = 0;i < suffix.length; i++) {
//数字直接入栈 取字符串的首位即第0位 用indexof取查找在前面数字串的位置 若能返回一个位置值则该字符为数字
if ("1234567890.".indexOf(suffix[i].charAt(0))>=0)
Result.push(suffix[i]);
else
{//运算符的话 根号和减号单独拿出来算
double n = 0;
if (suffix[i].equals("√"))
{
double z = 0;
if(Double.parseDouble(Result.peek())>=0)
{
z = Double.parseDouble(Result.pop());
n = sequareRoot(z);
}
else label = 1;
}
else if (suffix[i].equals("-"))
{  //后面代码用了suffix[i+1],所以这里要确保i+1不超出后缀数组长度
if (i + 1 < suffix.length)
{
  // 当结果栈顶只有一个元素或者当前运算符后也是运算符时,判断“-”为取反号
               // 取反号则弹出栈顶一个元素进行取反运算 
                        if (Result.size()  == 1 || (Result.size() %2 == 0 && ("+-*/".contains(suffix[i + 1]))))
{
double q = 0;
q = Double.parseDouble(Result.pop());
n = -q;
}//否则 则判断为减号,弹出栈顶两个数相减
else
{
double a = 0, b = 0;
a = Double.parseDouble(Result.pop());
b = Double.parseDouble(Result.pop());
n = b - a;
}
}//i+1超出数组长度说明当前“-”为最后一个运算符
else
{
if (Result.size() == 1 )
{
double q = 0;
q = Double.parseDouble(Result.pop());
n = -q;
System.out.println(n);
}
else {
double a = 0, b = 0;
a = Double.parseDouble(Result.pop());
b = Double.parseDouble(Result.pop());
n = b - a;
}
}
}//判断为一般运算符 则弹出栈顶两个数字进行双目运算
else
{
double x, y = 0;
x = Double.parseDouble(Result.pop());
y = Double.parseDouble(Result.pop());
switch (suffix[i]) {
case "+":
n = x + y;
break;
case "*":
n = x * y;
break;
case "/":
if (x == 0) {//除数为零时 改变标签label的值使其显示输入错误
label = 1;
}
n = y / x;
break;
}
}
          每一次计算的结果循环入栈 作为下一次计算的基数,最后栈顶只保留最终结果
Result.push(String.valueOf(n));
}
} //报错标志
if(label == 1)
return "ERROR!";
return (Result.peek());//返回栈顶的最终结果
}
//事件处理 UI布局时注册了监听器 这里使用监听器获取用户按动的按钮的字符
public void actionPerformed(ActionEvent e) {
String label = e.getActionCommand();
     //每次获取的字符的接到input字符串中
input = input + label;
resultText.setText(input);
     //清零输入框
if(Objects.equals(label, "C")) {
input = "";
resultText.setText("0");
}
//遇到等号即输入结束
if((Objects.equals(label, "="))){
if(input.isEmpty()) return ;
   //调用houzhui方法将中缀表达式转化为后缀
String[] s = houzhui(input);
       //调用Result方法结算最终结果并获取到result字符串中 打印在文本框中
String result = Result(s);
if(result.equals("ERROR!"))
resultText.setText(result);
else
resultText.setText(input + result);
}
}
public static void main(String[] args) {
Calculator1 calculator = new Calculator1();
calculator.setVisible(true);
}
}
五、测试用例

 

标签:实验报告,opStack,String,infix,Result,new,btn
From: https://www.cnblogs.com/shierxixi/p/17760738.html

相关文章

  • SWUST 算法分析与设计 实验报告2
    合并排序实验报告 一、     实验内容及目的实验内容:对合并排序算法进行算法描述、效率分析、实验结果分析。实验目的:深入理解分治法的思想,学习合并排序的排序方法,对合并排序进行算法分析,通过与其他排序算法比较,体会分治思想的优点。分析的指标:在相同数据规模的情况......
  • SWUST 算法分析与设计 实验报告1
    Lockerdoors实验报告 一、     实验内容及目的实验内容:有一组数从1~n。从1开始,访问第i个数和它的倍数。以此类推。当i=n结束时,求有多少个数的访问次数为奇数。实验目的:验证不同的算法,在不同的数据规模的情况下,运行时间的变化情况,绘制成曲线图,比较算法的优劣性。体......
  • 2023夏季《移动软件开发》实验报告:lab01
    一、实验目标学习使用快速启动模板创建小程序的方法;学习不使用模板手动创建小程序的方法。二、实验步骤自动生成小程序首先到微信公众平台官网首页注册账号,进行信息填写登记,使用邮箱激活账号。然后到微信开发者工具官网,根据自己的电脑版本下载微信开发者工具,并进行安......
  • 如何实现Python线性回归模型的实验报告的具体操作步骤
    Python线性回归模型的实验报告简介线性回归是一种常用的机器学习模型,用于预测一个或多个自变量与因变量之间的线性关系。它是一种简单但强大的模型,被广泛应用于各个领域。本实验报告将介绍如何使用Python实现线性回归模型,并进行实验验证其预测能力。数据集首先,我们需要准备一......
  • 实验报告
    https://www.cnblogs.com/yycyhyhf/p/17264251.htmlhttps://www.cnblogs.com/yycyhyhf/p/17285673.htmlhttps://www.cnblogs.com/yycyhyhf/p/17378885.htmlhttps://www.cnblogs.com/yycyhyhf/p/17239138.htmlhttps://www.cnblogs.com/yycyhyhf/p/17327753.html......
  • 实验报告
    密码引擎的设计与实现实验一密码引擎-1-OpenEuler-OpenSSL编译1.下载最新的OpenSSL源码(1.1版本)2.用自己的8位学号建立一个文件夹,cd你的学号,用pwd获得绝对路径3.参考https://www.cnblogs.com/rocedu/p/5087623.html先在Ubuntu中完成OpenSSL编译安装,然后在OpenEuler中......
  • 实验报告
    实验一-密码引擎-加密API一、研究资料API:应用程序接口(API:ApplicationProgramInterface)是一组定义、程序及协议的集合,通过API接口实现计算机软件之间的相互通信。API的一个主要功能是提供通用功能集。程序员通过使用API函数开发应用程序,从而可以避免编写无用程序,以减轻编......
  • 电子公文传输系统实验报告
    目录一.编译与运行页面及代码部分展示二.信息资产的存放与保护保护的信息资产保护形式信息资产的存放三.实现方案方案集合四.根据GMT0054标准实行安全性措施访问控制机密性加密解密五.心得体会一.编译与运行在你的电脑上编译小组项目,提交截图。在你的电脑上运行小组项目,提交......
  • 实验报告
    实验一密码引擎-1-OpenEuler-OpenSSL编译一、任务详情安装Ubuntu和OpenEuler虚拟机下载最新的OpenSSL源码(1.1版本)用自己的8位学号建立一个文件夹,cd你的学号,用pwd获得绝对路径参考 https://www.cnblogs.com/rocedu/p/5087623.html 先在Ubuntu中完成OpenSSL编......
  • 实验报告
    密码引擎部分1-OpenEuler-OpenSSL编译一、下载最新的OpenSSL源码(1.1版本)二、用自己的8位学号建立一个文件夹,cd你的学号,用pwd获得绝对路径三、参考https://www.cnblogs.com/rocedu/p/5087623.html先在Ubuntu中完成OpenSSL编译安装,然后在OpenEuler中重现./config--prefix=......