软工作业3:结对项目-四则运算

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade21-12
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade21-12/homework/13016
这个作业的目标 与队友合作完成项目,实现四则运算算式生成

团队成员:

沈俊杰 3121004710 https://github.com/13ugYellow/13ugYellow/tree/master/结对项目
梁志聪 3121004704

PSP表格:

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

能效分析:

生成四则运算算式的总时间:

答案对比的总时间:

生成四则运算算式的时间占比:



可以看到在creat函数中:

将逆波兰式生成规范二叉树时间占比较大,但这是为了判重的必须步骤,暂时无法优化。

答案对比的时间占比:



其中grade函数占比最大,看在grade函数中:

发现是读取文件的时间占比较大,故无法优化。

设计实现过程:

总体思想:

随机生成中缀表达式——>生成逆波兰表达式——>转化为规范二叉树——>计算结果,结果为负数则舍弃二叉树——>表达式判重——>写入文件

类与函数:

  • class Arith(object)
    • creat(self, problem_number, r) # 生成四则运算和答案在判重后写入文件
    • main(self, arith, argv) # 支持命令行输入参数
  • class BinaryTree(object) # 二叉树
    • out_put_tree(self, tree, s) # 返回二叉树逆波兰形式的字符串
  • class Caculation(object)
    • caulate(self, op, f1, f2) # 计算f1 op f2 的值(op为运算符)
    • max(self, num1, num2) # 比较两分数大小
  • class Check(object) # 判重
    • check_tree(self, tree) # 对二叉树进行判重,若不重复返回True
  • class Compare(object)
    • grade(self, exercise_file, answer_file) # 比较两文件答案并记录
  • class Create(object) # 生成四则运算表达式
    • create_operator(self) # 随机生成运算符
    • create_arith(self, r) # 生成范围在r以内的四则运算表达式
    • proper_fraction(self, list) # 将假分数化为带分数
  • class Fractions(object) # 分数类
  • class CreateTree(object) # 生成规范二叉树
    • toRPN(self, list) # 生成逆波兰表达式
    • createTree(self, suffix) # 将逆波兰式转化为规范化二叉树
    • priority(self, operatorout, operatorin) # 判定优先级

代码说明:

生成四则运算表达式

def create_arith(self, r):
    x = 0
    list = []
    # 随机生成运算符的数量
    operator_num = random.randint(1, 3)
    e1 = Create()
    e2 = Create()
    if operator_num == 1:
        list.append(e1.create_number(r))
        list.append(e2.create_operator())
        list.append(e1.create_number(r))
    elif operator_num == 2:
        start = random.randint(0, 2)
        end = 0
        if start > 0:
            end = start + 1
        for i in range(1, 4):
            if i == start:
                list.append("(")
            list.append(e1.create_number(r))
            if i == end:
                list.append(")")
            list.append(e2.create_operator())
        list.pop()
    elif operator_num == 3:
        start = random.randint(0, 3)
        end = 0
        if start > 0:
            end = start + 1 + random.randint(0, 1)
            if end >= 4:
                end = 4
        for i in range(1, 5):
            if i == start:
                list.append("(")
            list.append(e1.create_number(r))
            if i == end:
                list.append(")")
            list.append(e2.create_operator())
        list.pop()
    else:
        list.append(e1.create_number(r))
        list.append(e2.create_operator())
        list.append(e1.create_number(r))
    return list

生成逆波兰式

def toRPN(self, list):
    right = []
    aStack = []
    position = 0
    while True:
        if self.isOperator(list[position]):
            if list == [] or list[position] == "(":
                aStack.append(list[position])
            else:
                if list[position] == ")":
                    while True:
                        if aStack != [] and aStack[-1] != "(":
                            operator = aStack.pop()
                            right.append(operator)
                        else:
                            if aStack != []:
                                aStack.pop()
                            break
                else:
                    while True:
                        if aStack != [] and self.priority(list[position], aStack[-1]):
                            operator = aStack.pop()
                            if operator != "(":
                                right.append(operator)
                        else:
                            break
                    aStack.append(list[position])
        else:
            right.append(list[position])
        position = position + 1
        if position >= len(list):
            break
    while aStack != []:
        operator = aStack.pop()
        if operator != "(":
            right.append(operator)
    return right

将逆波兰式转换成二叉树并规范化

def createTree(self, suffix):
    stacks = []

    for i in range(0, len(suffix)):
        tree = BinaryTree()
        ob = suffix[i]
        c = Caculation()
        if self.isOperator(ob):
            t2 = BinaryTree()
            t1 = BinaryTree()
            t2 = stacks.pop()
            t1 = stacks.pop()
            if ob == '-' and t1.value <= t2.value:
                return None
            else:
                if self.maxTree(t1, t2):
                    tree.set_date(ob)
                    tree.set_left(t1)
                    tree.set_right(t2)
                    tree.set_value(c.caulate(ob, t1.value, t2.value))
                else:
                    tree.set_date(ob)
                    tree.set_left(t2)
                    tree.set_right(t1)
                    tree.set_value(c.caulate(ob, t1.value, t2.value))
                stacks.append(tree)
        else:
            tree.set_value(ob)
            tree.set_date(ob)
            stacks.append(tree)
    return tree

对二叉树进行判重

def check_tree(self, tree):
    if self.check == []:
        self.check.append(tree)
        return True
    else:
        for i in range(len(self.check)):
            if self.check[i] == tree:
                return False
    self.check.append(tree)
    return True

计算相应运算符下两参数的值

def caulate(self, op, f1, f2):
    result = Fractions()
    n1 = int(f1.numerator)
    d1 = int(f1.denominator)
    n2 = int(f2.numerator)
    d2 = int(f2.denominator)
    list = []
    if op == '+':
        re = Fraction(n1, d1) + Fraction(n2, d2)

    elif op == '-':
        re = Fraction(n1, d1) - Fraction(n2, d2)

    elif op == '×':
        re = Fraction(n1, d1) * Fraction(n2, d2)

    else:
        re = Fraction(n1, d1) / Fraction(n2, d2)

    return re

对两个文件中的答案进行比较并记录

def grade(self, reanswer_file, answer_file):
    correct = []
    wrong = []
    co = 0
    wr = 0
    with open(answer_file, 'r', encoding='utf-8') as f1, open(reanswer_file, 'r', encoding='utf-8') as f2:
        answers = f2.readlines()
        line = 0
        for r_answers in f1.readlines():
            if answers[line] == r_answers:
                co += 1
                correct.append(line+1)
            else:
                wr += 1
                wrong.append(line+1)
            line += 1
    with open('Grade.txt', 'w') as f3:
        f3.write(f"Correct: {str(co)} ({', '.join(str(s) for s in correct if s not in [None])})" + '\n')
        f3.write(f"Wrong: {str(wr)} ({', '.join(str(s) for s in wrong if s not in [None])})" + '\n')
    print("文件比较完成")

生成问题和答案在判重后写入文件

def creat(self, problem_number, r):
    creat_pro = Create()
    t = BinaryTree()
    c = Check()
    with open("Exercises.txt", "w", encoding='utf-8') as file1, open("Answer.txt", "w", encoding='utf-8') as file2:
        num = 0
        while num < problem_number:
            arith = creat_pro.create_arith(r)  # 生成四则运算列表
            Ju = Judge()
            al = Ju.toRPN(arith)  # 将列表转换成逆波兰式
            string = creat_pro.to_string(creat_pro.proper_fraction(arith))
            ta = Ju.createTree(al)  # 将逆波兰式生成规范二叉树
            if ta:
                val = str(creat_pro.pop_fracte(ta.value))
                if c.check_tree(t.to_string(ta)):  # 进行判重
                    file1.write("%d. " % (num+1) + string + '\n')
                    file2.write("%d. " % (num+1) + val + '\n')
                    num +=1
    print("四则运算题目生成完毕,数量为%d个" % problem_number)

支持命令行键入参数

# @profile()
def main(self, arith, argv):
    problem_number = None
    num_range = None
    exercise_file = None
    answer_file = None
    try:
        opts, args = getopt.getopt(argv, "n:r:e:a:")
    except getopt.GetoptError:
        print('Error: main.py -n <problem_number> -r <num_range>')
        print('   or: main.py -e <ReAnswer_file>.txt -a <Answer_file>.txt')
        sys.exit(2)

    for opt, arg in opts:
        if opt in("-n"):
            problem_number = int(arg)
        elif opt in ("-r"):
            num_range = int(arg)
        elif opt in("-e"):
            exercise_file = arg
        elif opt in("-a"):
            answer_file = arg
    if problem_number and num_range:
        arith.creat(problem_number, num_range)
    elif exercise_file and answer_file:
        compare = Compare()
        compare.grade('ReAnswer.txt', 'Answer.txt')
    else:
        print('Error: main.py -n <problem_number> -r <num_range>')
        print('   or: main.py -e <ReAnswer_file>.txt -a <Answer_file>.txt')

测试运行:

命令行参数不完整测试:

生成10道题目测试:


对比答案测试:


项目小结:

本次结对项目中,我们二人得益于住在同一个宿舍,有更多的交流时间与分工合作。我们二人的代码能力尚不成熟,在完成项目的过程中编码环节对我们来说是艰难的,只能通过对网上的算法的借鉴和向同学提问学习来完成代码的编写。我们在这次项目中,发现了相比于编写代码,对于团队项目来说更重要的是理清项目的总体流程,这样才能够便于我们分工合作。代码的模块化便可以极大的辅助我们的合作,这次结对项目也使得我们二人的代码风格更加规范,不再是写出一大堆只为了完成一个目的的代码。总体来说,结对项目让我们可以吸收他人的想法,更加开阔自己对于实现项目的视野,使得思路更加清晰,然而这也让我们知道了我们贫瘠的代码水平限制了我们的思想,我们对彼此的建议都认为提升代码底力是我们的当务之急,也希望可以有更多机会可以结对合作相互督促进步。

posted @ 2023-09-28 21:16  13ugYellow  阅读(26)  评论(0编辑  收藏  举报