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.mzph.cn/news/90106.shtml

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

相关文章

Fiddler 抓包八个实用技巧

大家对Fiddler应该不会陌生,但里面有些技巧不见得都会,这里就有八个实用技巧,通过对Fiddler的定制,能提高大家的测试效率。 fiddler抓包教程:一节课教你fiddler抓包在测试领域的四大实战,你一定要学_哔哩哔…

vue event bus 事件总线

vue event bus 事件总线 创建 工程: H:\java_work\java_springboot\vue_study ctrl按住不放 右键 悬着 powershell H:\java_work\java_springboot\js_study\Vue2_3入门到实战-配套资料\01-随堂代码素材\day04\准备代码\08-事件总线-扩展 vue --version vue crea…

微信多账号聊天、多账号管理,轻松拿捏

你是否微信账号太多,很难管理; 是否很难触达精准客户; 是否人力成本不断上升, 公司迫切需要提高工作效率,降低成本。 ...... 针对多个痛点问题, 微信管理系统进行了有针对性的开发和定位。 多账号聚合…

【计算机网络黑皮书】入门必学的基本网络知识

【事先声明】 这是对于中科大的计算机网络的网课的学习笔记,感谢郑烇老师的无偿分享 书籍是《计算机网络(自顶向下方法 第7版)》 需要的可以私信我,无偿分享,课程简介下也有 B站链接 目录 网络核心电路交换端到端之间的…

LeetCode【2251. 花期内花的数目】

给你一个下标从 0 开始的二维整数数组 flowers ,其中 flowers[i] [starti, endi] 表示第 i 朵花的 花期 从 starti 到 endi (都 包含)。同时给你一个下标从 0 开始大小为 n 的整数数组 people ,people[i] 是第 i 个人来看花的时间…

基于SpringBoot的医院管理系统

目录 前言 一、技术栈 二、系统功能介绍 病床信息管理 药房信息管理 个人中心管理 药房信息 病床类别 科室信息管理 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息互联网信息的飞速发展,医院也在创建着属于自己的管理系统。本文介…

uniapp 事件委托失败 获取不到dataset

问题&#xff1a; v-for 多个span ,绑定点击事件 代码:view里包着一个span, <view class"status-list" tap"search"><span class"status-item" v-for"(key,index) in statusList" :key"index" :data-key"k…

【N年测试总结】区块链行业测试特点

一、区块链业务系统简介 转入转出业务&#xff1a;这类业务一般会涉及币的转入和转出&#xff0c;转入的流程一般是用户从第三方钱包往用户在公司的地址转入&#xff0c;系统收到用户的转入操作消息通知后&#xff0c;定时在链上监控该地址相关的交易&#xff0c;通过校验各项…

windows:批处理bat入门

文章目录 什么是BAT常用命令与语法help与/?titlecolormodeechopausecallremset/a/p gotostartifif errorlevel for普通用法for /l 用法for /d用法for /r用法for /f用法in (file)delims和tokensskipeolusebackq 变量扩展变量延迟 setlocalshiftdirrd&#xff08;删除文件夹&…

服务器搭建(TCP套接字)-libevent版(服务端)

Libevent 是一个开源的事件驱动库&#xff0c;用于开发高性能、并发的网络应用程序。它提供了跨平台的事件处理和网络编程功能&#xff0c;具有高性能、可扩展性和可移植性。下面详细讲解 Libevent 的主要组成部分和使用方法。 一、事件基础结构&#xff08;event_base&#x…

【网络协议】Http-下

HTTP常见Header Content-Type: 数据类型(text/html等) Content-Length: Body的长度 Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上; User-Agent: 声明用户的操作系统和浏览器版本信息; referer: 当前页面是从哪个页面跳转过来的; location: 搭配3xx状态…

无需公网IP,实现公网SSH远程登录MacOS【内网穿透】

目录 前言 1. macOS打开远程登录 2. 局域网内测试ssh远程 3. 公网ssh远程连接macOS 3.1 macOS安装配置cpolar 3.2 获取ssh隧道公网地址 3.3 测试公网ssh远程连接macOS 4. 配置公网固定TCP地址 4.1 保留一个固定TCP端口地址 4.2 配置固定TCP端口地址 5. 使用固定TCP端…

定义豪车新理念 远航汽车亮相2023中国(天津)国际汽车展览会

近年来&#xff0c;随着汽车行业竞争持续加剧&#xff0c;老品牌面临积极转型&#xff0c;新势力则经验不足、实力欠佳&#xff0c;到底是难抵市场的风云变幻。在此背景下&#xff0c;有着“老品牌 新势力”双重基因的远航汽车可谓底气十足。作为大运集团携手博世、华为、阿里斑…

JumpServer开源堡垒机与爱可生云树数据库完成兼容性认证

近日&#xff0c;中国领先的开源软件提供商FIT2CLOUD飞致云宣布&#xff0c;JumpServer开源堡垒机已经完成与爱可生云树数据库软件的兼容性认证。经过双方联合测试&#xff0c;云树数据库软件&#xff08;简称&#xff1a;ActionDB&#xff09;V1.0与杭州飞致云信息科技有限公司…

什么是Vue的Vetur插件?它有哪些功能

引言 在现代前端开发中&#xff0c;Vue.js已经成为了一个备受欢迎的JavaScript框架。随着Vue.js的流行&#xff0c;开发人员需要强大的工具来提高他们的生产力和Vue.js项目的质量。Vetur插件是一个为Vue.js开发者提供的强大工具&#xff0c;它不仅提供了丰富的功能&#xff0c…

【超详细】前段开发之详细的Vue3入门教程,特别适合小白系统学习,入门到熟练使用Vue看这一篇就够了!

前言&#xff1a; 这篇文章更加侧重的是Vue3不同于Vue2的知识点&#xff0c;如果学习Vue2请看下面这篇文章 Vue2详细系统入门教程 11.2 Vue3 声明&#xff1a;图片资源来自于黑马程序员公开学习资料 本人在学习当中&#xff0c;详细整理了笔记&#xff0c;供大家参考学习 1…

什么是DOM和DOM操作

什么是DOM&#xff1f; DOM&#xff08;文档对象模型&#xff09;:HTML文档的结构化表示。允许JavaScript访问HTML元素和样式来操作它们。&#xff08;更改文本&#xff0c;HTML属性甚至CSS样式&#xff09; 树结构由HTML加载后自动生成 DOM树结构 这个是一个很简单的HTML代…

实验五 熟悉 Hive 的基本操作

实验环境&#xff1a; 1.操作系统&#xff1a;CentOS 7。 2.Hadoop 版本&#xff1a;3.3.0。 3.Hive 版本&#xff1a;3.1.2。 4.JDK 版本&#xff1a;1.8。 实验内容与完成情况&#xff1a; &#xff08;1&#xff09;创建一个内部表 stocks&#xff0c;字段分隔符为英文逗号…

SkyWalking分布式链路追踪学习

为什么要用分布式链路追踪 实际生产中&#xff0c;面对几十个、甚至成百上千个的微服务实例&#xff0c;如果一旦某个实例发生宕机&#xff0c;如果不能快速定位、提交预警&#xff0c;对实际生产造成的损失无疑是巨大的。所以&#xff0c;要对微服务进行监控、预警&#xff0…

颜色+情感的英语表达还有这些,零基础学英语口语去哪里,柯桥有推荐的吗?

当我们探讨关于"blue"&#xff08;蓝色&#xff09;的多义性时&#xff0c;我们会发现英语中有许多其他词汇也有类似的双关意义。 既可以表示一种颜色或物理属性&#xff0c;又可以代表一种情感或心理状态。 这种现象在语言中很常见&#xff0c;反映了语言的丰富性和…