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

news2024/11/26 13:44:25
班级班级社区
作业要求软件工程实践第一次作业-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 = line
			if 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 := 0
	lastindex := 0
	pkcnt := 0
	lkcnt := 0
	begin := false
	sin := false
	tan := false
	rjudge := false
	if 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 = index
					restr = exp[preindex+3 : lastindex]
					break
				}
			}
			if ch == "r" {
				rjudge = true
			}
			if (ch == "i" || ch == "o" || ch == "a") && !begin {
				preindex = index
				log.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 := false
	pkcnt := 0
	lkcnt := 0
	rjudge := false
	for index, v := range exp {
		if string(v) == "r" && !begin {
			rjudge = true
		}
		if (string(v) == "s" || string(v) == "c" || string(v) == "t") && !begin {
			preindex = index
			begin = true
		}
		if begin {
			if string(v) == "(" {
				pkcnt++
			} else if string(v) == ")" {
				lkcnt++
				if lkcnt == pkcnt {
					lastindex = index
					log.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 = true
	entry.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.coloradmin.cn/o/1049248.html

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

相关文章

向华为学习:制订一份分工明确、能够落地的产品GTM行动计划表

昨天华研荟介绍了新产品上市的GTM的定义、GTM这个岗位(角色)的主要工作以及新产品的GTM要回答好的四个问题(Why、What、How、Where),帮助大家初步理解了让产品上市更成功的GTM流程。 如我昨天在文章中所讲到的&#x…

ACM MM 2023 | 基于去中心化表征的人体姿态估计方法

01. 前言 北京邮电大学与EVOL创新团队共同提出人体姿态估计方法DecenterNet,用于在提高在拥挤场景下人体姿态估计的准确度。该方法引入了一种去中心化的姿势表征方法,使得网络在纠缠区域/拥挤区域中将更加稳健地表达人体姿态。该方法还提出了一个解耦的…

LeetCode【577. 员工奖金】

表:Employee ---------------------- | Column Name | Type | ---------------------- | empId | int | | name | varchar | | supervisor | int | | salary | int | ---------------------- empId 是该表中具有唯一值的列。 该…

BFS专题7 多终点迷宫问题

题目&#xff1a; 样例&#xff1a; 输入 3 3 0 0 0 1 0 0 0 1 0 输出 0 1 2 -1 2 3 -1 -1 4 思路&#xff1a; 单纯的 BFS 迷宫问题&#xff0c;只是标记一下每个点的 step&#xff0c;注意初始化答案数组都为 -1. 代码详解如下&#xff1a; #include <iostream> #…

Windows下使用pybind11教程(python调用C++代码)

1. 下载pybind11 gittub中下载&#xff0c;pybind下载后解压 2. C生成库文件 2.1.VS新建空白工程&#xff0c;工程名随意起 - 2.2更改目标文件名和配置类型 - 2.3更改目标文件拓展名 2.4添加include路径和库路径 包含目录中添加刚刚下载好的pybind的include路径以及pyhon的…

C#的HALCON引擎调用_传入参数输出结果实现流程

1、在Halcon的开发环境里面写处理流程。 此案例使用HALCON自带图片&#xff1a; read_image (Image, printer_chip/printer_chip_01) 读入图片之后&#xff0c;做处理流程&#xff1a; *图像处理流程&#xff1a; *传入图像变量&#xff0c;阈值最小值&#xff0c;最大值。…

软件测试/测试开发丨利用人工智能自动找Bug

点此获取更多相关资料 简介 在程序员编程的过程中&#xff0c;产生Bug是一件平常的事情&#xff0c;以前在编码的过程中提前找出Bug&#xff0c;需要通过单元测试、CodeReview等各种方式。 当今&#xff0c;人工智能技术的发展给软件开发和测试带来了许多机会。利用人工智能…

聊聊并发编程——多线程之AQS

目录 队列同步器&#xff08;AQS&#xff09; 独占锁示例 AQS之同步队列结构 解析AQS实现 队列同步器&#xff08;AQS&#xff09; 队列同步器AbstractQueuedSynchronizer&#xff08;以下简称同步器&#xff09;&#xff0c;是用来构建锁或者其他同步组 件的基础框架&…

TikTok美国市场爆品:美牙仪一周售出3.36万单,GMV近百万刀

最近一周&#xff0c;超店有数洞察到TikTok Shop美国市场出现一款爆火美牙仪&#xff0c;该款商品售价为31.95美金&#xff0c;佣金比率为25%&#xff0c;一周内销量达3.36万单&#xff0c;GMV近94万美金。自今年7月底上架以来在TikTok上关联视频播放量高达140W&#xff0c;属于…

企业长假期间如何应对突发业务需求?提前部署远程控制为上策

没有人想在长假期间加班&#xff0c;包括管理层也是一样的。但客观来说&#xff0c;很多企业的业务在假期中也是不能中断的&#xff0c;如果业务线遇到紧急需要处理的问题&#xff0c;有没有办法不用长途跋涉跑回公司一趟呢&#xff1f;远程控制现在就是很多企业的选择。 时值…

静态住宅代理是什么?为什么要选择它?

静态住宅代理是互联网服务提供商(ISP)分配的住宅ISP代理。正如名称“静态”所指&#xff0c;他的IP永久不会变化。在当今的数字时代&#xff0c;数据安全、隐私和在线访问已变得至关重要&#xff0c;具有无限带宽的静态住宅代理提供了出色的解决方案。下面给大家具体介绍。 一、…

Django实战项目-学习任务系统-需求说明

一&#xff0c;需求说明   在我最近的阅读中&#xff0c;我深深被一些关于智能或系统的小说吸引。这些小说的主角意外获得某种神秘的智能或系统&#xff0c;然后通过完成系统发布的各种任务&#xff0c;逐渐提升自己的知识和能力。即使是普通的屌丝&#xff0c;也能在系统的管…

win10环境mysql8.10免安装版本配置

MySQL :: Download MySQL Community Server 下载免安装包 解压到相应目录。 以管理员身份启动cmd net start mysql 服务无法启动。 运行mysqld --initialize --console初始化 生成临时密码 验证临时密码并登录测试 mysql -u root -p出错 启动 net start mysql 运行phpmya…

Leetcode684. 冗余连接

Every day a Leetcode 题目来源&#xff1a;684. 冗余连接 解法1&#xff1a;并查集 因为需要判断是否两个节点被重复连通&#xff0c;所以我们可以使用并查集来解决此类问题。 代码&#xff1a; /** lc appleetcode.cn id684 langcpp** [684] 冗余连接*/// lc codestart…

PID温度控制器,全球市场总体规模,前17大厂商排名及市场份额

PID温度控制器全球市场总体规模 PID温度控制器是一种常用的温度控制设备&#xff0c;能够通过使用比例、积分和微分控制算法来实现精确的温度调节。它可以监测和调整温度&#xff0c;保持设定的温度稳定。PID代表比例、积分和微分&#xff0c;比例&#xff08;P&#xff09;控…

【Verilog教程】6.7 Verilog流水线

关键词&#xff1a;流水线&#xff0c;乘法器 硬件描述语言的一个突出优点就是指令执行的并行性。多条语句能够在相同时钟周期内并行处理多个信号数据。 但是当数据串行输入时&#xff0c;指令执行的并行性并不能体现出其优势。而且很多时候有些计算并不能在一个或两个时钟周期…

linux权限机制,

目录 用户与组,id,passwd 查看登录用户whomi,who,w 创建用户 useradd 修改用户信息usermod 删除指定用户userdel 组 ​编辑创建修改删除组groupadd groupmod groupdel 权限 ls-l 修改文件所属用户&#xff0c;所属组 chown,chgrp(change group) 修改权限 chmod 默认权…

针对http接口进行测试,使用Jmeter工具实现

前言&#xff1a; 本文主要针对http接口进行测试&#xff0c;使用Jmeter工具实现。 Jmter工具设计之初是用于做性能测试的&#xff0c;它在实现对各种接口的调用方面已经做的比较成熟&#xff0c;因此&#xff0c;本次直接使用Jmeter工具来完成对Http接口的测试。 一、开发接口…

26381-2011 合成纤维丝织坯绸 阅读笔记

声明 本文是学习GB-T 26381-2011 合成纤维丝织坯绸. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了合成纤维丝织坯绸的术语和定义、要求、试验方法、检验规则、包装和标志。 本标准适用于评定各类合成纤维丝织坯绸品质。 2 规…

需求堆积,如何排序产品优先极

面对堆积的产品需求&#xff0c;到底该如何排序优先极呢&#xff1f; 需求排期的目标 在谈具体的排期方法之前&#xff0c;有必要先探讨一下——合理的需求排期应该达到什么的目标呢&#xff1f;如果站在与项目相关的利益人员的角度来看&#xff0c;至少应该使以下四方面的收…