2023软工作业(一)——计算器

班级班级社区
作业要求软件工程实践第一次作业-CSDN社区
作业目标完成一个具有可视化界面的科学计算器
参考文献Fyne

  目录

作业要求

项目源码地址

作业目标

0. 界面及功能展示

1. PSP表格

2. 解题思路描述

3. 核心代码

4. 设计与实现过程

5. 程序性能改进

6. 单元测试展示

7. 异常处理

8. 心路历程和收获


环境:go 1.20.7   |  windows  |  mingw64

编程工具: VS Code

作业要求

要求链接软件工程实践第一次作业-CSDN社区要求链接

项目源码地址

代码地址

作业目标

完成一个具有可视化界面的科学计算器

0. 界面及功能展示

                软工计算器演示


1. PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划3030
• Estimate• 估计这个任务需要多少时间2015
Development开发300340
• Analysis• 需求分析 (包括学习新技术)6090
• Design Spec• 生成设计文档2020
• Design Review• 设计复审3030
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)1010
• Design• 具体设计3050
• Coding• 具体编码240300
• Code Review• 代码复审6030
• Test• 测试(自我测试,修改代码,提交修改)100100
Reporting报告120100
• Test Repor• 测试报告2020
• Size Measurement• 计算工作量1010
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划90

70

合计900915

2. 解题思路描述

        1. GUI问题

        什么是GUI,如何使用GUI ,通过调用特定的GUI库实现的接口,可以实现GUI的窗口创建,窗口布局,按钮布局,设置函数响应等功能

        2. 计算表达式

        用户输入表达式字符串,程序需要识别该字符串,包括识别数字,运算符等,比较难的是括号匹配问题,这边选择通过堆栈实现括号的匹配问题,但是在三角函数方面这个并不适用,这里选择使用的是递归实现

3. 核心代码

func equals() func() {return func() {// 切割换行lines := strings.Split(entry.Text, "\n")fmt.Println("lines:", len(lines))// 空表达式不变if len(lines) == 0 || (lines[0] == "" && len(lines) == 1) {entry.Text = ""entry.Refresh()log.Println("empty expression")return}line := lines[len(lines)-1]// 错误切除if len(lines) >= 3 || strings.Contains(entry.Text, "error") {entry.Text = lineif strings.Contains(lines[0], "error") {entry.SetText("")log.Println("continue error calculate")return}entry.Refresh()}// 溢出切除if strings.Contains(line, "Inf") {entry.SetText("error:inf calculate\n")entry.Refresh()log.Println("error:inf calculate")return}// 三角函数处理for {if strings.Contains(line, "sin") || strings.Contains(line, "cos") || strings.Contains(line, "tan") {res, err := Hsin(line)if err != nil {entry.SetText("error:sin or cos calculate expression\n")entry.Refresh()log.Println(err)return}log.Println("--------------------")_, _, trim_exp, err := TrimTec(line)if err != nil {entry.SetText("error:sin or cos trim expression\n")entry.Refresh()log.Println(err)return}log.Println("--------------------")line = strings.Replace(line, trim_exp, fmt.Sprint(float32(res)), 1)log.Println("line 三角函数切割 :", line)} else {break}}line = strings.Trim(line, "+/x")// 次方替换if strings.Contains(line, "^") {line = strings.ReplaceAll(line, "^", "**")}// 到此阶段只剩下基本的表达式,高级的运算符已经处理成数字和基本运算符// 表达式求值expr, err := govaluate.NewEvaluableExpression(line)if err != nil || expr == nil {if strings.Contains(err.Error(), "transition") {entry.Text = fmt.Sprint("transition error")}entry.Text = fmt.Sprint("error:wrong expression\n")entry.Refresh()return}result, err := expr.Evaluate(nil)if err != nil || result == nil {if strings.Contains(err.Error(), "transition") {entry.Text = fmt.Sprint("transition error")}entry.Text = fmt.Sprint("error:unespected error please clear\n")}entry.Text += "=\n"entry.Text += fmt.Sprint(result)entry.Refresh()}
}

递归求三角函数

// 求解表达式的第一个三角函数值
func Hsin(exp string) (float64, error) {restr := ""preindex := 0lastindex := 0pkcnt := 0lkcnt := 0begin := falsesin := falsetan := falserjudge := falseif strings.Contains(exp, "sin") || strings.Contains(exp, "cos") || 
strings.Contains(exp, "tan") {for index, v := range exp {ch := string(v)if begin && ch == "(" {pkcnt++} else if begin && ch == ")" {lkcnt++if lkcnt == pkcnt { //括号匹配lastindex = indexrestr = exp[preindex+3 : lastindex]break}}if ch == "r" {rjudge = true}if (ch == "i" || ch == "o" || ch == "a") && !begin {preindex = indexlog.Println("--------------")begin = true}if ch == "s" && !begin {sin = true} else if ch == "t" && !begin {tan = true}}if pkcnt != lkcnt {log.Println("error expresiion")return 0, errors.New("error expresiion")}exp = restr} else {expr, err := govaluate.NewEvaluableExpression(exp)if err != nil || expr == nil {log.Println("expression:", exp)log.Println("error expresiion1")return 0, err}result, err := expr.Evaluate(nil)if err != nil {log.Println("error expresiion2")return 0, err}log.Println("output:", result.(float64))return result.(float64), nil}log.Println("exp:", exp)result, err := Hsin(exp)if err != nil {return 0, err}if sin {log.Println("sin------:", result)if rjudge {if !judgeValue(result) {return 0, errors.New("error:value error")}return math.Asin(result) * 180 / math.Pi, nil}return math.Sin(result * math.Pi / 180), nil} else if tan {log.Println("tan------:", result)if rjudge {return math.Atan(result) * 180 / math.Pi, nil}return math.Tan(result * math.Pi / 180), nil}if rjudge {if !judgeValue(result) {return 0, errors.New("error:value error")}return math.Acos(result) * 180 / math.Pi, nil}log.Println("cos------:", result)return math.Cos(result * math.Pi / 180), nil
}// 获得第一个cos或者sin表达式
func TrimTec(exp string) (preindex, lastindex int, res string, err error) {begin := falsepkcnt := 0lkcnt := 0rjudge := falsefor index, v := range exp {if string(v) == "r" && !begin {rjudge = true}if (string(v) == "s" || string(v) == "c" || string(v) == "t") && !begin {preindex = indexbegin = true}if begin {if string(v) == "(" {pkcnt++} else if string(v) == ")" {lkcnt++if lkcnt == pkcnt {lastindex = indexlog.Println("preindex:", preindex)log.Println("lastindex:", lastindex)if rjudge {res = exp[preindex-1 : lastindex+1]return}res = exp[preindex : lastindex+1]return}}}}err = errors.New("error:expression error")return
}


4. 设计与实现过程

导入所需的库:

fyne:GUI库,用于创建GUI窗口,编排GUI布局,  自定义图形化界面,内嵌处理函数

math:提供数学计算方法 和 数学常量, 如: Π

创建计算器窗口:

创建一个fyne窗口,设置logo,设置显示位置,窗口大小。

创建显示输入和输出的文本框:

创建一个Entry(文本框)部件的对象,用于显示输入输出。
可以使用entry.SetText("内容")来设置文本框中的内容,或者直接对entry.Text进行操作

每次操作完后需要entry.Refresh刷新,用户才能实时的在文本框中获取最新的内容
设置文本对齐方式、边框大小等属性。

响应函数:

input :input函数用于输入用户在按钮输入的内容如数字,运算符, 支持过滤连续输入运算符
 

equls:equals函数用于处理表达式并返回结果,使用堆栈,递归以及调用库的方式实现表达式的计算,这个方法中不仅计算的细节很多,处理错误的细节也要注意

计算错误时,返回error:错误内容,用户只需要再次输入新的内容则会清空文本框并开启新的输入,或者直接点击清空按钮   c

sign:sign函数用于将传入的数进行正负切换

        

back:back函数用于 删除表达式最后一个字符

其他内置函数:
        Hsin 获得第一个cos或者sin表达式

        TrimTec 获得第一个cos或者sin表达式

        judgeValue 判断反三角函数输入值是否在定义域内

文本框和按钮布局顺序:

包括科学函数按钮、操作符按钮、数字按钮、等号等。
使用循环创建按钮,并为每个按钮绑定相应的事件处理函数。

最后启动窗口即可


5. 程序性能改进


优化方式:使用递归识别并计算表达式中的三角函数值,降低时间复杂度,代码可见3. 核心代码中的Hsin函数

6. 单元测试展示

覆盖率测试结果(这边仅测试主要功能函数,另外的百分之二十九左右为程序的启动函数,例如GUI布局设置,窗口创建之类的代码这边不包含进去,覆盖率实现基本全覆盖)

覆盖率测试代码

func TestCover(t *testing.T) {a := app.New()_ = a.NewWindow("Calculator")entry = widget.NewEntry()entry.MultiLine = trueentry.Resize(fyne.NewSize(150, 150))funcs := equals()// 表达式测试 各种运算符检测 结果检测entry.Text = "1+2+3+2^3+rsin(sin(30))+rtan(tan(30))+rcos(cos(60))"t.Log(entry.Text)funcs()entry.Text = "error:xxx"funcs()entry.Text = "Inf"funcs()entry.Text = "rsin(10)"funcs()entry.Text = "rcos(10)"funcs()// 重复运算符过滤ifunc := input("+")ifunc()ifunc()// 正负反转entry.Text = "3"sfunc := sign()sfunc()entry.Text = "3.3"sfunc()// 退格测试entry.Text = "8^9999"bfunc := back()bfunc()funcs()entry.Text = "\n"bfunc()entry.Text = ""bfunc()t.Log(entry.Text)
}

覆盖率优化经验:增加测试用例,尽可能地将代码的错误都跑过一遍,主要还是看编程人对自身代码的熟悉程度,需要了解每个异常发生原因以及每个分支的进入条件,编写相应的测试用例

单元测试代码

func TestCalculateSC(t *testing.T) {// 1.判断表达式是否有三角函数// 2.有则放入Hsin求得第一个函数的值// 3.然后将值替换原本的三角函数// 4.回到1// 5.没有三角函数了在将整个表达式求解exp := "1+sin(sin(10+110))+rsin(0.5)"for {if strings.Contains(exp, "sin") || strings.Contains(exp, "cos") {res, err := Hsin(exp)if err != nil {t.Log(err)t.Fail()}_, _, trim_exp, err := TrimTec(exp)if err != nil {t.Log(err)t.Fail()}exp = strings.Replace(exp, trim_exp, fmt.Sprint(float32(res)), 1)t.Log("change", exp)} else {break}}}func TestEvaluate(t *testing.T) {expr, err := govaluate.NewEvaluableExpression("1+3%4-1/2+2^3")if err != nil || expr == nil {fmt.Println("err:", err)t.Log("error")t.Fail()}result, err := expr.Evaluate(nil)t.Log("res:", result)
}

单元测试结果

 

7. 异常处理

随着程序的开发,有些细节的错误没有处理会导致程序直接崩溃,闪退

如: 表达式输入错误,返回的值无法表示时返回Inf,输入的值不在数学函数的定义域之内,用户和程序显示内容不同步等错误

  

大部分的错误只需要处理好临界条件即可,在golang 中函数返回错误,然后在函数返回后判断返回的错误是否为空即可进入分支处理,同时有些库函数并不返回错误,因此还是需要好好处理临界的条件判断,如输入值是否在定义域内,我们需要在计算前判断一下输入值的大小,如果不满足则直接返回错误不进行后续的操作

再比如:返回的值无法表示的时候返回INF ,程序在判断用户输入时,需要判断表达式内是否含有INF字符串,存在则返回表达式错误


8. 心路历程和收获


心路历程:先想到的是GUI编程,之前只开发过Web,并没有windows GUI相关经验,然后去百度了解,搜索相关的资料,GUI的用法之类的,然后想到的表达式的处理,这是一个相对比较复杂的过程,我通过自己实现内部函数递归算法和调用库函数的方式实现了表达式的计算,这里面有很多细节需要考虑,不过我认为在这门课中本次任务的目标还是对GUI的使用,毕竟换个环境或者题目这些细节可能又需要重新去考虑

用户界面使用优化:在一开始实现的过程中,总是会遇到感觉很别扭的地方,比如闪退,返回错误显示在文本框还要再次点击清空才能再次输入表达式,我通过自身体验去优化这些地方,使得用户不会出现闪退,以及能够在发生错误后只需点击任一按钮即可清空并开始新的表达式输入

学习收获:收获了关于GUI库:fyne的使用、字符串处理,计算表达式处理,单元测试,和GUI编程经验

总结:了解了GUI编程的相关知识,锻炼了GUI编程能力,
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/120500.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

读取.nrrd和.dcm文件格式医学图片可视化与预处理

nrrd数据格式 MITK默认会将医学图像保存为格式为NRRD的图像,在这个数据格式中包含: 1、一个单个的数据头文件:为科学可视化和医学图像处理准确地表示N维度的栅格信息。 2、既能分开又能合并的图像文件。 nrrd_options输出 {u’dimension’:…

基于Java实现的仓库管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言功能介绍:具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding)有保障的售后福利 代码参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导…

王庆友-架构的本质:如何打造一个有序的系统?

整理自:王庆友-[架构实战案例解析] 我们知道,现在的软件系统越来越复杂,当然相应地,架构的作用也越来越明显。作为开发人员,我们每天都在和架构打交道,在这个过程中,对于架构也经常会产生各种各…

秋招在线人才测评考什么内容?

又是一年招聘季,各大高校都会组织校园招聘,这次我们就来了解一下秋季校园招聘究竟考什么。近些年来校园秋招已经广泛采用在线测评,尤其各行业龙头大厂们,网申、在线测评、小组无领导讨论,一面二面......各类纷杂的面试…

Unity fbx动画压缩自动化

应用场景:Unity WebGl项目,原先的动画压缩后也有20到30M,导致用户进入网页加载太慢。需求是在不对动画精确度造成影响的前提下,影响尽可能地压缩动画。(使用的是Maya导出的人体骨骼动画,在动画压缩方式选择…

电脑技巧:推荐一款桌面增强工具AquaSnap

目录 一、软件介绍 二、功能介绍 2.1 窗口边缘停靠 2.2、 窗口平铺 2.3、 窗口对齐 2.4 窗口自动拉伸 2.5、同时移动多个窗口 2.6 、支持窗口置顶 2.7、 鼠标快捷方式 2.8、 键盘快捷键 三、软件特色 四、软件获取 一、软件介绍 AquaSnap(界面增强软件)是一款功能…

蓝桥杯每日一题2023.9.27

4408. 李白打酒加强版 - AcWing题库 题目描述 题目分析 对于这题我们发现有三个变量,店,花,酒的数量,对于这种范围我们使用DP来进行分析。 dp[i][j][k]我们表示有i个店,j朵花,k单位酒的集合&#xff0c…

Windows 下安装和配置 Redis (详细图文)

目录 下载 Redis安装 Redis配置 Redis修改密码(可选)配置环境变量注册系统服务 Redis 桌面管理工具附:开源项目微服务商城项目前后端分离项目 下载 Redis 访问 Redis 下载地址:https://github.com/tporadowski/redis/releases 下载 Redis 时&#xff0c…

MIPI协议介绍-CPHY

MIPI协议概述 MIPI(Mobile Industry Processor Interface): 是MIPI联盟发起为移动应用处理器制定的开放标准.MIPI接口协议层主要包括CSI和DSI两种,其中CSI主要用于图像输出,如图像传感器等; DSI主要用于图像输入,如屏幕显示器等.对于camera而…

聊一聊JDK21-虚拟线程

目录 前言 Virtual Threads的开始 为什么需要Virtual Threads JDK19 预览版初次出现 JDK21 Virtual Threads的正式发布 Virtual Threads 该怎么使用 简单聊聊Virtual Threads的实现 使用时候的注意事项 本地尝鲜一下JDK21及Virtual Threads 结语 前言 2023年9月19日…

Pygame中监控鼠标动作的方法

在Pygame中监控键盘按键的方法_pygame获取键盘输入-CSDN博客中提到,通过在while True循环中获取队列中事件的方法监控键盘动作。监控鼠标动作的方法与监控键盘动作的方法相同。 相关连接1 队列与事件的相关知识,请参考 Pygame中监控键盘按键的方法_pyg…

设计模式1、单例模式 Singleton

解释说明:确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例 要点如下 有且仅有一个实例 必须自行创建自己的唯一实例 必须给所有其他对象提供这一实例 具体实现要点如下 提供一个 private 构造函数(防止外部调用而构造类的实例…