这个作业属于课程 软件工程
这个作业的要求在哪里 结对项目
这个作业的目标是 实现一个自动生成小学四则运算题目的命令行程序
成员 3118005408 方俊涛 、3118005409 冯宇航

GitHub

文章目录

  • PSP
  • 效能分析
  • 设计实现过程以及重要代码说明
  • 测试运行
  • 项目小结

PSP

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 20
· Estimate · 估计这个任务需要多少时间 30 20
Development 开发 470 455
· Analysis · 需求分析 (包括学习新技术) 150 120
· Design Spec · 生成设计文档 30 20
· Design Review · 设计复审 (和同事审核设计文档) 20 15
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 20 10
· Design · 具体设计 30 20
· Coding · 具体编码 150 130
· Code Review · 代码复审 30 20
·Test · 测试(自我测试,修改代码,提交修改) 40 120
Reporting 报告 75 80
· Test Report · 测试报告 25 50
· Size Measurement · 计算工作量 20 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 20
合计 575 555

效能分析

花费时间:30min

egg :python main.py -r 50 -n 1000

改进前:

通过pycharm自带的效能分析工具来看,我们的程序在creQuestion()上花费的时间是比较多的,原因是该函数在实现生成表达式时,调用了self.isRepeat(expressionList, expression) 进行查重检测,此步奏涉及到大量的遍历二叉树

    def creQuestion(self):"""表达式生成主函数"""expNum = self.expressionNumexpressionList = []i = 0while i < expNum:random_num_operation = random.randint(1,self.operCount)  #运算符的数目is_need_parenteses = random.randint(0, 1)    #是否需要加括号number_of_oprand = random_num_operation + 1  # 操作数比操作符的数目多1exp = []for j in range(random_num_operation + number_of_oprand):if j % 2 == 0:# 随机生成操作数(含分数)exp.append(self.getOperNum()['operStr'])if j > 1 and exp[j - 1] == '÷' and exp[j] == '0':while True:exp[j - 1] = self.generateOperation()if exp[j - 1] == '÷':continueelse:breakelse:# 生成运算符exp.append(self.generateOperation())if j > 3:if exp[j-2] == '÷' :  #为了子表达式为真分数,÷左右又括号除外if exp[j-1] > exp[j-3]:t  = exp[j-1]exp[j - 1] =  exp[j-3]exp[j - 3] = telif exp[j-2] == '-' :if exp[j-1] < exp[j-3]:t  = exp[j-1]exp[j - 1] =  exp[j-3]exp[j - 3] = t# 判断是否要括号if is_need_parenteses and number_of_oprand != 2:expression = " ".join(self.generateParentheses(exp, number_of_oprand))else:expression = " ".join(exp)#判断是否有重复#           if self.expressionNum <= 500:if 1 :if self.isRepeat(expressionList, expression) :continueelse:result = self.calculate(expression)if result == "False" :passelse:expressionList.append(expression)#('第 %d 道题' % int(i + 1))#print(expression)i = i + 1else:result = self.calculate(expression)if result == "False":passelse:expressionList.append(expression)# ('第 %d 道题' % int(i + 1))# print(expression)i = i + 1return expressionList

改进后:

出于软件工程的实用性和需求分析,当需要生成的题目数量超过500时,由于题量较多,时间更显得重要,故判断题量较多时可不查重,改进后时间大大缩短

通过pycharm自带的效能分析工具来看,我们改进后的程序在expression_result()上花费的时间是比较多的,原因在于为了计算答案,多次调用suffixExpression进行中缀表达式转后缀表达式并进行计算。思考无果,暂时没有好算法进行替换


时间缩短为原来的1/37

   def expression_result(self,exp_list):"""求表达式的结果:param exp_list: 表达式列表:return: None"""self.exp_list  =exp_listif os.path.exists('Answer.txt'):   # 清空上一次的答案with open('Answer.txt', 'r+') as file:file.truncate(0)for i, exp in enumerate(self.exp_list):order_str = str(i + 1)suffixExpression  = SuffixExpression(exp)#print("------suffixExpression:{}---------".format(str(suffixExpression.suffixToValue()) ))exp_value = str(suffixExpression.suffixToValue()) + '\n'result = "Answer"+order_str + ': ' + exp_valuewith open('Answer.txt', 'a+', encoding='utf-8') as f:f.write(result)

设计实现过程以及重要代码说明

本次设计程序我们采取了面向对象的方法进行编程,对于整个程序进行分析,我们以生成题目、计算答案、校对答案三个功能把它分为了三个模块构造了四个类,其中BinaryTree类主要服务于Product类实现生成题目的功能

第一个类: BinaryTree类

该类有两个功能:生成二叉树和检查两颗二叉树是否相同

生成二叉树: 是将传入的后缀表达式列表用二叉树进行存储

检查两颗二叉树是否相同:实现的方法是基于递归的方法进行遍历结点

本类是为了Product类的def isRepeat(self, express_set, expression)函数服务的,在Product类为了避免生成重复的表达式,采用了基于二叉树的形式进行查重!

第二个类: SuffixExpression类

该类的功能如下:

* 将中缀表达式转化为后缀表达式(def toSuffix(self))
* 计算后缀表达式的值( def suffixToValue(self):)

其中将中缀表达式转化为后缀表达式的实现:

def toSuffix(self):"""中缀表达式转为后缀表达式:self.exp : 中缀表达式列表:return: result列表"""if not self.exp: return []ops_rule = {'+': 1,'-': 1,'×': 2,'÷': 2,}suffix_stack = []  # 后缀表达式结果ops_stack = []  # 操作符栈infix = self.exp.split(' ')   # 将表达式分割得到单词# print(infix)for item in infix:if item in ['+', '-', '×', '÷']:  # 遇到运算符while len(ops_stack) >= 0:if len(ops_stack) == 0:ops_stack.append(item)breakop = ops_stack.pop()if op == '(' or ops_rule[item] > ops_rule[op]:ops_stack.append(op)ops_stack.append(item)breakelse:suffix_stack.append(op)elif item == '(':  # 左括号直接入栈ops_stack.append(item)elif item == ')':  # 右括号while len(ops_stack) > 0:op = ops_stack.pop()if op == "(":        # 一直搜索到出现“(”为止breakelse:suffix_stack.append(op)else:suffix_stack.append(item)  # 数值直接入栈while len(ops_stack) > 0:suffix_stack.append(ops_stack.pop())self.re =suffix_stackreturn suffix_stack

该代码的思路解释:
我们实现中缀表达式转化为后缀表达式的思路:
预处理: 由于题目要求数字与运算符之间要有空格隔开,所以转化过程中需先清除空格
遍历中缀表达式:

  • 如果遇到操作数,我们就直接将其存入后缀表达式栈suffix_stack[]
  • 如果遇到操作符,则我们将其放入到操作符栈ops_stack中,遇到左括号时我们也将其放入操作符栈ops_stack中
  • 如果遇到一个右括号,则将操作符栈ops_stack元素弹出,将弹出的操作符存入后缀表达式栈suffix_stack直到遇到左括号为止。注意,左括号只弹出并不存入后缀表达式栈suffix_stack。
  • 如果遇到任何其他的操作符,如(“+”, “*”,“(”)等,从操作符栈ops_stack中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到操作符栈ops_stack中。有一点需要注意,只有在遇到" ) “的情况下我们才弹出” ( “,其他情况我们都不会弹出” ( "。
  • 如果我们读到了输入的中缀表达式的末尾,则将ops_stack栈中所有元素依次弹出。

第三个类:Product类

功能是生成要求的四则运算式子。

对于实现生成式子功能,我们把式子的各个要素分别构建方法进行生成,式子可能包含的要素分别为:整数、分数、括号、运算符。

其中分数的生成是较为复杂的,因为题目要求生成的都是真分数,所以在生成分数后我们有构建了一个方法把假分数转换为带分数

 def DecToStr(self,operArray):#分数转化为带分数字符串operNum1 = operArray[0]operNum2 = operArray[1]if operNum2 == 1:return operNum1if(operNum1 > operNum2):temp = int(operNum1/operNum2)operNum1 -= (temp*operNum2)return str(temp) + "'" + str(operNum1) + "/" + str(operNum2)else:return str(operNum1) + "/" + str(operNum2)

此外,我们的函数isRepeat(self, express_set, expression)实现了查重,避免生成运算本质相同的式子(egg: 3+2+4 与4+(3+2),像这样就是重复的一种情形 ),不过此算法的时间复杂度较大。

    def isRepeat(self, express_set, expression):"""判断重复方法:paramexpress_set: 表达式集合expression: 生成的表达式:return: True or False"""suffixExpression = SuffixExpression(expression)target_exp_suffix = suffixExpression.re   # 后缀表达式列表binaryTree = BinaryTree()target_exp_binary_tree = binaryTree.generateBinaryTree(target_exp_suffix)for item in express_set:suffixExpression2 = SuffixExpression(item)source_exp_suffix = suffixExpression2.resource_exp_binary_tree = binaryTree.generateBinaryTree(source_exp_suffix)if binaryTree.treeIsSame(target_exp_binary_tree) == binaryTree.treeIsSame(source_exp_binary_tree):return Truereturn False

第四个类:Answer类

功能是:

  1. 调用SuffixExpression类进行计算得出答案文档
  2. 校对提交的答案

由于需要对两个文档的答案进行校对,所以也有可能出现文档输入不正确的结果,所以设置了两个异常处理,防止打开文件出现问题导致程序出错

    def check_answer(exercisefile, answerfile):"""校对答案:paramexercisefile: 练习题文件answerfile: 答案文件:return: None"""wrong_num = 0correct_num = 0exercise_answer = []correct_list = []  # 正确题目序号wrong_list = []  # 错误题目序号try:with open(exercisefile, 'r', encoding='utf-8') as f:for line in f:# 匹配出正则表达式exp_str = re.findall(r'\d+: (.*) = \n', line)if exp_str:exp = exp_str[0]else:continuep  = SuffixExpression(exp)exp_value = str(p.suffixToValue())exercise_answer.append(exp_value)except IOError:print('please check if the path is correct')# 判断表达式列表是否为空try:with open(answerfile, 'r', encoding='utf-8') as f:for i, line in enumerate(f):ans_str = re.findall(r'\d+: (.*)\n', line)# 容错if ans_str:ans = ans_str[0]else:continue# 判断是否正确if ans == exercise_answer[i]:correct_num += 1correct_list.append(i + 1)else:wrong_num += 1wrong_list.append(i + 1)with open('Grade.txt', 'w+', encoding='utf-8') as f:correct_str = 'Correct: ' + str(correct_num) + ' ' + str(correct_list) + '\n'wrong_str = 'Wrong: ' + str(wrong_num) + ' ' + str(wrong_list)f.write(correct_str)f.write(wrong_str)except IOError:print('please check if the path is correct')

在读取文件时,采用正则表达式获取所需要的信息:

ans_str = re.findall(r'\d+: (.*)\n', line)exp_str = re.findall(r'\d+: (.*) = \n', line)

测试运行






项目小结

冯宇航

对于结对项目的实操是给我们团队编程的一次训练,团队合作可能成员实力有强有弱,这时候能力较强的可能要付出多一点教导能力较弱的成员,两人一个学习一个复习,共同进步。

方俊涛

本次结对编程利用python采用了面向对象的思想,进行分模块分类实现。使用到的数据结构有:二叉树,列表,栈。用到的技术有中缀表达式转化为后缀表达式,二叉树判定重复等等。合作方面:开发前二人进行较多的讨论,通过事先调研、二人讨论确定本次开发所需要的技术,并 确定各自任务和合作内容。两人合作可以发现各自无法发现的bug,并互相促进更进进度。结对编程重要的还是交流与互助!

结对项目——自动生成小学四则运算题目的命令行程序(基于Python)相关推荐

  1. myapp——自动生成小学四则运算题目的命令行程序(侯国鑫 谢嘉帆)

    1.Github项目地址 https://github.com/baiyexing/myapp.git 2.功能要求 题目:实现一个自动生成小学四则运算题目的命令行程序 功能(已全部实现) 使用 -n ...

  2. 结对项目:自动生成小学四则运算题目程序

    ================= 这个作业属于哪个课程 软件工程 作业要求 作业要求 Github Github链接 小队成员 这个作业的目标 熟悉结对编程,实现自动生成小学四则运算题目程序,对给定 ...

  3. 自动生成小学四则运算题目的程序.心得体会

    http://t.cn/RAS67B0 源代码 #include<stdio.h> #include<stdlib.h> #include<time.h> main ...

  4. 计算式二级python_python实现自动生成小学四则运算题目(软工第二次项目作业)...

    前言 软件工程 作业要求 作业目标 结对编程:代码实现.性能分析.异常处理说明.记录PSP表格 代码见: github 个人信息:朱育清 3118005437 信安二班 我的partner 个人信息: ...

  5. 自动生成小学四则运算题目

    c++语言 思想:首先用rand产生随机数,for循环确定出题的数量,然后根据两个随机数相加除以4的余数来确定随机产生运算符号. 代码: #include<iostream> using ...

  6. java实现加减乘除运算符随机生成十道题并判断对错_简单小程序——产生三十道小学四则运算题目...

    题目要求程序可以生成三十道小学四则运算题目. 因为要随机生成题目,则需要产生随机数,因此我上网搜索了生成随机数的方法,选择了使用Random类得到规定范围内的随机数.因为一个运算需要三个元素,两个参与 ...

  7. 用Swashbuckle给ASP.NET Core的项目自动生成Swagger的API帮助文档

    Swagger是一个描述RESTful的Web API的规范和框架.如果使用ASP.NET的话,可以用Swashbuckle来自动生成Swagger,具体参考如何使 WebAPI 自动生成漂亮又实用在 ...

  8. 实现生成小学四则运算练习题

    实现小学四则运算练习题 1.题目要求 本次作业要求两个人合作完成,驾驶员和导航员角色自定,鼓励大家在工作期间角色随时互换,这里会布置两个题目,请各组成员根据自己的爱好任选一题. 题目1: 我们在刚开始 ...

  9. Go 项目自动生成接口文档

    CSDN 中文章不一定能及时更新,欢迎关注我的博客查看最新版本:许盛的博客 背景 如何让后端同学愉快地写接口文档,是个老大难问题. 使用 GraphQL 当接口标准,倒是省了接口文档的问题,连前端代码 ...

最新文章

  1. pytorch lstm crf 代码理解 重点
  2. 获取直播连接[.m3u8]
  3. 基于交换芯片的五元组的PCL规则过滤功能
  4. 锤子剪刀布pat-1018
  5. c语言:malloc函数的简介
  6. 低欲望社会有多可怕?仅94万!日本去年新生人口数创历史新低,空房子如瘟疫般蔓延...
  7. JavaMail基本使用
  8. Linux命令之目录和文件操作
  9. Netty 的 内存池 是如何实现的
  10. IT部领导总结:不想被淘汰,看看快速做报表的技巧,甚至能养老
  11. 数组字符串那些经典算法:最大子序列和,最长递增子序列,最长公共子串,最长公共子序列,字符串编辑距离,最长不重复子串,最长回文子串 (转)...
  12. 擎标|CMMI 5认证对软件企业有什么好处?
  13. 想练字要怎么选择字体?
  14. 数据分析达到精准营销的路径分析
  15. 计算机技术数字影音,技能大赛数字影音后期制作技术赛项规程.pdf
  16. Python【王者荣耀】全英雄无水印皮肤
  17. 万兴剪刀手去水印教程_万兴神剪手怎么去水印 去除logo水印方法
  18. CSS+DIV设计导航条源代码
  19. ipguard客户端如何卸载_关于卸载Oracle步骤和相关SQL的学习
  20. 第05章 深度卷积神经网络模型

热门文章

  1. Bootstarting Spring -- Spring 风云再起
  2. linux感染十字符病毒,十字符病毒,杀不死的小强,一次云服务器沦陷实录
  3. enq: TX - index contention
  4. autoit3转换php,AutoIt3 脚本函数用法中文说明
  5. wpf 窗口最大化,最小化,关闭,拖动,双击放大缩小窗口基本事件
  6. android期末大作业,Android代做,毕业设计,SQlLite数据库增删改查
  7. 点击弹出窗口,在窗口中播放视频文…
  8. 3D向量叉乘的理解和记忆
  9. TRIZ创新方法——ARIZ算法
  10. ntelliJ IDEA详细安装步骤