Git地址 | https://github.com/F0urty-Tw0 |
Git用户名 | F0urty-Tw0 |
学号后五位 | 61109 |
博客地址 | https://www.cnblogs.com/FourtyTwo/ |
作业链接 | https://edu.cnblogs.com/campus/xnsy/Autumn2019SoftwareEngineeringFoundation/homework/7590 |
Part 1. 配置环境及工具准备
由于想进一步巩固一下Python,此次作业打算用它来写,原先要求的Visual Studio 2017
的环境配置就由PyCharm
来代替
①PyCharm安装
PyCharm是由JetBrains打造的一款Python IDE,进入官网https://www.jetbrains.com/pycharm/,选择免费的community版本下载即可,如图所示:
安装过程较为简单,未遇到问题,安装完成效果如下:
②Git安装
进入Windows版git下载地址:https://git-scm.com/download/win,一般来说打开即自动开始下载任务,若长时间未开始,可手动点击:
接下来点击安装程序一步步来即可,安装成功后应可以在桌面或文件夹空白处右键下拉菜单中出现Git GUI here以及Git Bash here,如下图所示:
③项目克隆
再安装好Git之后,我们就可以再Github上克隆指定的项目,首先进入本次作业指定的仓库地址:https://github.com/Cherish599/AchaoCalculator,点击右上角的Fork
之后,即可拷贝到自己的同名仓库中。
接下来在我的电脑中任意路径下点击鼠标右键,选择Git Bash here,输入命令行git clone https://github.com/Cherish599/AchaoCalculator.git
,(git clone后的链接即为仓库地址)如下图所示:
之后需要在刚刚的路径下新建一个与Github账户名相同的文件夹,如下图所示:
由于之前通过Github搭建过个人博客,有了安装及使用Git的经验,所以本次作业在Git安装及项目克隆方面没有出现什么问题
Part 2.代码实现
①题目背景:
阿超家里的孩子上小学一年级了,这个暑假老师给家长们布置了一个作业:家长每天要给孩子出一些合理的,但要有些难度的四则运算题目,并且家长要对孩子的作业打分记录。
作为程序员的阿超心想,既然每天都需要出题,那何不做一个可以自动生成小学四则运算题目与解决题目的命令行 “软件”呢。他把老师的话翻译一下,就形成了这个软件的需求:
- 程序接收一个命令行参数 n,然后随机产生 n 道加减乘除(分别使用符号+-*/来表示)练习题,每个数字在 0 和 100 之间,运算符在 2 个 到 3 个之间。
- 由于阿超的孩子才上一年级,并不知道分数。所以软件所出的练习题在运算过程中不得出现非整数,比如不能出现 3÷5+2=2.6 这样的算式。
- 练习题生成好后,将生成的 n 道练习题及其对应的正确答案输出到一个文件 subject.txt 中。
- 当程序接收的参数为4时,以下为一个输出文件示例。
- 13+17-1=29
- 11*15-5=160
- 3+10+4-16=1
- 15÷5+3-2=4
②代码设计思路
根据题目背景,得知设计需求为一个可以自订题目数量的四则运算生成器,且结果只能为整数,还要将所有的算式和正确答案输出到一个文件中。由此,这个题目的关键由三部分构成:表达式的随机生成、表达式的计算、表达式及答案打印输出
(程序由包含表达式生成、打印输出及main()函数的caculator.py文件与包含表达式计算函数的caculate_func.py文件构成)
-
表达式的随机生成思路及代码实现
题目中要求表达式有两到三个运算符,所以在表达式生成之前应先设定在2和3之间的随机数,并通过条件分支语句判断该随机数为2还是3以进行后续的随机表达式生成。
具体代码:
r = random.randint(2, 3)
再确定运算符数量后,声明一个列表“op”,其元素即为["+", "-", "*", "/"]四个运算符(此处乘号与除号未直接采用“×”与“÷”是为了方便后续的计算操作),接着将0到100的随机数存入另一列表以便后续插入。在之后的表达式生成时,先取数值列表中的一个随机值,后通过for in语句将数值循环插入,运算符除插入位置变更外同理。
具体代码:for i in range(0, 3): tmp = random.choice(num) formula_list.append(tmp) for i in [1, 3]: tmp = random.choice(op) formula_list.insert(i, tmp)
该功能完整代码如下:
def new_form(): # 生成新的表达式 op = ["+", "-", "*", "/"] r = random.randint(2, 3) if r == 2: # 通过判断随机生成的r来确定表达式中运算符数量 num = random_num(10) formula_list = [] for i in range(0, 3): tmp = random.choice(num) formula_list.append(tmp) for i in [1, 3]: tmp = random.choice(op) formula_list.insert(i, tmp) elif r == 3: num1 = random_num(10) formula_list = [] for i in range(0, 4): tmp = random.choice(num1) formula_list.append(tmp) for i in [1, 3, 5]: tmp = random.choice(op) formula_list.insert(i, tmp) return formula_list
通过网上查阅,发现python中可以使用flatten()函数或
list(itertools.chain.from_iterable(zip(l,m)))
语句来直接实现列表的交叉合并,这样一来表达式的生成一两条语句便可实现,但我在实际使用过程中出现报错语句:TypeError: zip argument #1 must support iteration
并且一直无法解决(T_T),只能采用上述较为原始的方法。。。 -
表达式的计算思路及代码实现
未避免代码看起来冗长,将实现这一功能的函数写入了另一个文件中,及“calculate_func.py”,并通过
python from calculate_func import gen_list, calculate
语句引入至原文件由于运算符有加减乘除四个,要将表达式的答案计算出来就要考虑到运算符的优先级,原本想着要用栈实现,但在网上搜索的过程中发现也可以利用python的列表加递归实现简单加减乘除的计算。
具体原理为通过new_form()生成的表达式以列表形式存储,之后从左向右计算所有乘除号两边的数值,然后删除运算符及其左右两个数值,再把运算结果保存在第一个数值原先存在的位置,然后第二次循环计算剩下的加减法运行,直至只剩下一个数,即表达式的运算结果,由于不能出现分数,可以让强转int后的结果与原结果比较,若相同则正常返回结果
具体代码如下:def calculate(expression, isFirst=True): i = 0 while i < len(expression) - 3: a = expression[i] # 获取操作数1 b = expression[i + 2] # 获取操作数2 op1 = expression[i + 1] # 获取运算符 result = None if op1 == "*": result = a * b elif op1 == "/": result = a / b elif not isFirst and op1 == "+": result = a + b elif not isFirst and op1 == "-": result = a - b if result is not None: expression[i] = result # 将第一个操作数替换问运算结果 del (expression[i + 1: i + 3]) # 删除运算符号和后一个操作数 else: i += 2 # 下标偏移 if isFirst: return calculate(expression, False) elif int(expression[0]) == expression[0]: return expression[0] # 返回结果
-
表达式打印输出思路及代码实现
由于new_form()函数return了的表达式以列表形式存储,并且乘号与除号还只是用"*"与"/"表示,所以首先要遍历生成好的表达式找到乘号与除号,将其替换为“×”与“÷”。具体可以利用python中的字典来实现。
首先再定义一个字典op_dictm,即
op_dict = {"+": "+", "-": "-", "*": "×", "/": "*"}
,之后通过for in语句遍历表达式列表,若其中存在"*"与"/",则直接替换字典中对应的value,
具体代码:for i in range(length): if form_list[i] in op: form_list[i] = op_dict[form_list[i]]
之后在通过append直接将"="尾插进替换后的表达式。最后,通过
"".join(str(i) for i in form_list)
直接将列表中的元素拼接为字符串再输出。在此之前,若表达式运算结果为整数则调用save_to_file()函数将该表达式存入文件,若不为整数,则再次调用new_form()函数与form_display()函数来重新生成并输出表达式,直至该表达式答案为整数。
具体代码如下:def form_display(form_list): # 将生成的表达式打印 expression = "".join(str(i) for i in form_list) expression1 = gen_list(expression) result = calculate(expression1, isFirst=True) if result: # 若通过calculate()函数计算表达式结果为整数,则进行后续的打印输出 op_dict = {"+": "+", "-": "-", "*": "×", "/": "÷"} op = ["+", "-", "*", "/"] length = len(form_list) for i in range(length): if form_list[i] in op: form_list[i] = op_dict[form_list[i]] # 通过op_dict字典将原有表达式中的乘除运算符替换为"X"与"÷" form_list.append("=") form_list = "".join(str(i) for i in form_list) # 将form_list列表中的元素拼接为字符串输出 print (form_list) save_to_file('subject.txt', form_list+str(result)) else: # 若表达式计算结果不为整数则不打印任何数据 form_list = new_form() form_display(form_list)
-
运行结果
Part 3.测试
①单元测试
首先选中想要进行单元测试的函数,选中后ctrl+shift+T,如图:
选择Create New Test.....,之后再弹出的选项框中勾选test method为:test_new_form
之后在新建的文件中补全对函数进行测试的代码,在已经设定的类中添加测试函数的方法即可。
如图所示:
form_display()函数测试结果如下:
遇到的问题:
在进行单元测试时发现如何测试都不能通过,但自己的程序却能正常执行,网上查阅后发现要通过PyCharm对函数进行测试,必须按照特定的步骤方法,有些单元测试还要另外安装特定的模块,在按照正确的步骤进行后单元测试也可以正常运行
在一开始进行单元测试时,想当然的添加main函数,认为测试和正常的代码执行无异,但尝试了几次且确认测试代码没有问题后测试还是无法正常开始,网上查阅后才发现Pycharm中进行单元测试时不会执行main()函数,在已经设定好的类中添加测试函数的方法即可。
②回归测试
回归测试是指修改了旧代码后,重新进行测试已确认修改没有引入新的错误或导致其他代码产生错误的一种测试方法。具体流程就是在当前版本中对上一版本中测过的功能再重复测试一遍
在本次作业中,我在form_display()中新增了判断语句以在运算结果为整数时执行save_to_file()函数,,为了确定功能添加后没有产生新bug,再次对form_display的打印功能进行了测试。
Part 4.代码提交
首先在git clone下来的文件路径出右键,选择Git bash here
,输入命令行Git status
检查仓库状态,如下图所示:
接着输入命令行Git push
便可以将代码上传至GitHub仓库中,如下图所示:
之后便可以看到GitHub上的仓库中多出了与自己账户名相同的包含程序源码的文件夹,点击自己仓库界面的New Pull Request
:
在弹出的新页面中选择Create pull request
即可成功将作业源码提交,如下图所示:
Part 5.感想
虽然这次作业占比较小,但却也用了较长时间并且查阅网上得资料来完成,并且虽然使用了python来完成,却没有用到面向对象的思想通过创建类来实现程序,深刻体会到了目前能力的不足≡(▔﹏▔)≡。但通过完成这次作业,体会到了软件设计到实现到测试到发布的大体流程,熟悉了各种工具的使用,并且在代码实现过程中也巩固了前段时间自学的Python,走了一遍较为完整的项目设计的流程,由衷地感受到了邹欣老师所推崇的“做中学”的思想(ง •_•)ง。