标签:cal 开发 题目 not 重复 tee remove 分析 主函数
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
20 |
20 |
· Estimate |
· 估计这个任务需要多少时间 |
20 |
20 |
Development |
开发 |
1560 |
1490 |
· Analysis |
· 需求分析 (包括学习新技术) |
70 |
60 |
· Design Spec |
· 生成设计文档 |
60 |
60 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
80 |
90 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
20 |
20 |
· Design |
· 具体设计 |
80 |
90 |
· Coding |
· 具体编码 |
1000 |
905 |
· Code Review |
· 代码复审 |
120 |
120 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
130 |
145 |
Reporting |
报告 |
150 |
150 |
· Test Report |
· 测试报告 |
60 |
70 |
· Size Measurement |
· 计算工作量 |
30 |
30 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
60 |
50 |
合计 |
|
1730 |
1660 |
图1:优化前性能分析
图2:优化后性能分析
1.10000道题目 2.100W道题目
在阅读题目和分析需求之后,我们组将此次的项目分为三个部分,一是生成运算式并查重;二是计算以及保存题目及答案;最后是记录用户输入答案并且评分。
通过argparse模块获用户输入参数-n,-r等参数设置来运算范围、生成题目数目和是否产生负数等。
前期讨论过程中,我们设想使用简单随机填充数字和操作符来生成算术表达式,以及检测答案不一致这种方法来查重,但是效率较低,也不方便后续做扩展。因此,我们又经过了讨论,决定使用二叉树随机生成运算式。在结点Node类里面设置表达式的结构,二叉树Tree类逐层生成含操作符和操作数的运算树(父节点为操作符,叶子节点为操作数,如下图3)。通过赋予运算符优先级op_priority匹配加减乘除的运算过程,并且结合操作符优先级生成对应的中缀表达式。之后通过后缀表达式来生成查重表达式,来检测运算式是否重复(查重思路参考链接:https://www.cnblogs.com/wxrqforever/p/8679118.html)。
图3:表达式二叉树
接着,结合操作符优先级计算结果,通过FileUtils类将结果保存到exercise.txt文档以及answer.txt文档,最后在控制台中进行答题之后获取成绩grade。
1 parser = argparse.ArgumentParser(description="四则运算")
2 parser.add_argument(‘-n‘, dest=‘number‘, type=int, default=1, help=‘number of generated questions‘)
3 parser.add_argument(‘-r‘, dest=‘range‘, type=int, default=10, help=‘range of values‘)
4 parser.add_argument(‘-e‘, dest=‘exercise‘, type=str, help=‘formula expression file‘)
5 parser.add_argument(‘-a‘, dest=‘answer‘, type=str, help=‘answer expression file‘)
6 parser.add_argument(‘-g‘, dest=‘grade‘, type=str, help=‘grade file‘)
7 parser.add_argument(‘-m‘, dest=‘minus‘, default=False, action=‘store_true‘,
8 help=‘produce formulas with negative numbers‘)
9 args = parser.parse_args()
10
11
12 if __name__ == ‘__main__‘:
13 if args.range is None:
14 print("请输入‘-r‘参数控制题目中数值(自然数、真分数和真分数分母)的范围")
15 if args.exercise is None:
16 args.exercise = os.path.join(os.getcwd(), ‘Exercises.txt‘)
17 if args.answer is None:
18 args.answer = os.path.join(os.getcwd(), ‘Answer.txt‘)
19 if args.grade is None:
20 args.grade = os.path.join(os.getcwd(), ‘Grade.txt‘)
21 print("欢迎进入答题模式......(输入‘exit‘可退出程序)")
22 t = Tree()
23 u_answer = list() # 用户答案
24 formula, s_answer = t.generate_formula(args.range, args.number, args.minus) # 随机生成表达式
25 FileUtils.write_file(formula, s_answer, args.exercise, args.answer) # 保存题目文件
26 for i in range(args.number):
27 print(formula[i], end=‘‘)
28 answer = input() # 获取用户输入的答案
29 if answer == ‘exit‘:
30 print(‘退出程序成功!‘)
31 sys.exit()
32 u_answer.append(answer)
33 correct, wrong = CalculatorUtils.grading(u_answer, s_answer) # 统计答题结果
34 print("答题结果:")
35 print(correct)
36 print(wrong)
37 FileUtils.write_grade_file(args.grade, correct, wrong) # 保存答题结果
class NegativeError(Exception): """自定义表达式不为负数的异常类""" def __init__(self): super(NegativeError, self).__init__() # 初始化父类 def __str__(self): return class DifficultError(Exception): """自定义分母不能超过某个值的异常类""" def __init__(self): super(DifficultError, self).__init__() # 初始化父类 def __str__(self): return class DuplicateError(Exception): """自定义异常类""" def __init__(self): super(DuplicateError, self).__init__() # 初始化父类 def __str__(self): return
def generate_formula(self, num_range, number, negative):
"""随机生成式子"""
num = 0
degree = random.randrange(3, 4) # 随机设置操作数的个数
while num < number:
empty_node = [self.root]
for _ in range(degree):
‘‘‘生成操作符号节点‘‘‘
node = random.choice(empty_node)
empty_node.remove(node)
node.operator = random.choices(self.op_list, cum_weights=self.op_weight)[0]
# node.operator = random.choices(self.op_list)[0]
node.type = 2
# 每生成一个操作符号节点,生成两个空节点
node.left = Node()
node.right = Node()
empty_node.append(node.left)
empty_node.append(node.right)
for node in empty_node:
‘‘‘将所有空结点变为数字结点‘‘‘
node.type = 1
# 设置真分数的比重 1为整数 0为分数
num_type = random.choices(self.type_list, self.num_weight)[0]
if num_type == 1:
# 生成一个整数
node.number = random.randint(1, num_range)
else:
# 生成一个真分数
node.number = Fraction(random.randint(1, num_range), random.randint(1, num_range))
try:
# self.root.show_node() # 获取生成的二叉树结构
self.root.get_answer(negative) # 计算答案
if self.root.number.denominator > 99: # 分母超过99抛出异常
raise DifficultError()
self.pre_formula = self.root.get_formula() # 获取前缀表达式
self.post_formula = FormatUtils.get_result_formula(self.pre_formula) # 获取后缀表达式
self.check_formula = FormatUtils.get_check_formula(self.post_formula) # 获取查重表达式
# 进行查重
if not Tree.duplicate_check(self.check_formula, self.result_formula):
# 返回false 则表明没有重复
self.result_formula.append(self.check_formula)
else:
raise DuplicateError
output = FormatUtils.standard_output(self.pre_formula) # 格式化前缀表达式
if isinstance(self.root.number, Fraction):
answer = FormatUtils.standard_format(self.root.number) # 格式化答案
else:
answer = self.root.number
# print(output, answer)
self.formula.append(output)
self.answer.append(answer)
except ZeroDivisionError:
# print("除数为零,删除该式子")
continue
except NegativeError:
# print("出现负数,删除该式子")
continue
except DifficultError:
# print("题目较难,删除该式子")
continue
except DuplicateError:
# print("题目重复,删除该式子")
continue
else:
num += 1
return self.formula, self.answer
1 class CalculatorUtils: 2 3 @staticmethod 4 def eval_formula(operator, a, b, negative=False): 5 """计算简单的加减乘除, 同时抛出不符合题目要求的异常""" 6 answer = 0 7 if operator == "+": 8 answer = a + b 9 elif operator == "-": 10 if a < b and negative is False: 11 raise NegativeError() # 抛出结果为负数的异常对象 12 else: 13 answer = a - b 14 elif operator == "*": 15 answer = a * b 16 elif operator == "/": 17 if b > 99: 18 raise DifficultError() # 抛出题目较难的异常对象(分母大于100) 19 else: 20 answer = a / b 21 # 如果答案为浮点数,则转换为分数形式 22 if isinstance(answer, float): 23 answer = Fraction(a) / Fraction(b) 24 return answer 25 26 @staticmethod 27 def get_answer(formula_list, negative): 28 """计算后缀表达式的结果""" 29 num_list = list() 30 for i in range(len(formula_list)): 31 if isinstance(formula_list[i], int) or isinstance(formula_list[i], Fraction): 32 num_list.append(formula_list[i]) 33 else: 34 b = num_list.pop() 35 a = num_list.pop() 36 res = CalculatorUtils.eval_formula(formula_list[i], a, b, negative) 37 num_list.append(res) 38 return num_list.pop() 39 40 @staticmethod 41 def grading(user_ans, ans_list): 42 """评分,同时返回要求的评分输出格式""" 43 correct = list() 44 wrong = list() 45 length = len(user_ans) 46 for i, u, ans in zip(range(1, length + 1), user_ans, ans_list): 47 if u == ans: 48 correct.append(i) 49 else: 50 wrong.append(i) 51 return correct, wrong
在经过了整个项目的洗礼之后,作为项目实现的其中一员,整个项目下来学习到了新的知识,新的语言结构以及数据结构,大佬带着小弟闯荡四方,我出谋划策(瞎说了很多想法),贡献了策略,代码量大部分都是进怀写的,我负责的是一些检测和修改,整个项目下来,我发现了很多知识点上的漏洞,但是也学习到了很多东西,比如说进怀使用的这种二叉树的结构,使我对二叉树有了新的理解,以及对于中缀表达式和后缀表达式的理解也更加深刻。无他,就想猛的夸赞一下队友,太给力了。我们队的这次项目,选择的算法简直无敌,太给劲了。本次项目的缺点也许就是没有一个比较完整的GUI界面,使用的是命令行窗口,但是麻雀虽小,算法和数据结构都不小。但是前期开发经验的不足也是我们的代码冗余度更高,特别是我的原因对需求的分析有一些失误,但是后面知道了自己的错误并且及时修改了意见,希望下次能更好的完成任务,并且能多写写代码。感谢队友!
标签:cal 开发 题目 not 重复 tee remove 分析 主函数
原文地址:https://www.cnblogs.com/jezing/p/11689225.html