Soft-tasking (III): twinning project - four arithmetic generator (Python implementation)

1. Github address and project members


 

2. PSP table:


 

PSP2.1

Personal Software Process Stages

Estimated time consuming (minutes)

The actual time-consuming (minutes)

Planning

plan

 20

 20

· Estimate

• Estimate how much time this task requires

 20

 20 

Development

Develop

 1560

 1490

· Analysis

· Needs analysis (including learning new technologies)

 70

 60

· Design Spec

Generate design documents

 60

 60

· Design Review

· Design Review (and his colleagues reviewed the design documents)

 80

 90

· Coding Standard

· Code specifications (development of appropriate norms for the current development)

 20

 20

· Design

· Specific design

 80

 90 

· Coding

· Specific coding

 1000

 905

· Code Review

· Code Review

 120

 120

· Test

· Test (self-test, modify the code, submit modifications)

 130

 145

Reporting

report

 150

 150

· Test Report

· testing report

 60

 70

· Size Measurement

· Computing workload

 30

 30

· Postmortem & Process Improvement Plan

· Hindsight, and propose process improvement plan

 60

 50

total

 

 1730

 1660

 

3. Effectiveness Analysis


Input: -r is 10, -n 10000 (numbers and denominator of the fraction within the range of 10, entitled Number 10000)

Before optimization: The total running time of 3.002s

Analysis and optimization process:

  • Observing the performance analysis it was found that the program takes place randomly generated operands and calculation
  • Addition also found redundant computations , while fraction IO write score module and a larger percentage of time.

Therefore, the optimization process mainly from the above aspects.

After optimization: The total running time of 1.727s

 

Figure 1: before optimization Performance Analysis

Figure 2: Analysis of the optimized performance

Added: In the process of optimizing the effectiveness, we went through a discussion (tangled ing), found another room for optimization, is in the process of randomly generated, you can rest assure that will not generate the same formula (ie complex requirements, (1 + 2) + 3 and 3 + (1 + 2) does not also produce), then re-check the final expression becomes a tool of our evidence! Finally, we wrote a test (to test whether there will be repeat title), were tested 1W track title, track title repeat title and 100W when produced, the result of repeated thrown exception is 0.

 

4. design and implementation process


Design ideas and panel discussions:

  After reading the title and demand analysis, we will group the project is divided into three parts, one to generate expression and re-check; the second is to calculate and save the topic and answer; and finally recording user input answer and score.

  Argparse eligible by the user input module parameters -n, -r parameters setting operation range, and whether a title number of generated negative like.

  Early discussions, we envisage the use of filling simple random numbers and operators to generate a arithmetic expressions, and a detection method to check the answer to this inconsistency weight, but less efficient, do not facilitate subsequent expansion. Therefore, we have been discussed and decided to use a binary tree randomly generated expressions . Provided inside the class structure of the node Node expression, arithmetic binary tree Tree class generating layer by layer containing operands and operators (operator parent node, the leaf node is the number of operations as in FIG. 3). By imparting operator precedence op_priority matching operation process of addition, subtraction, and binding infix operators generate a corresponding priority. After passing through the postfix expression to generate expression re-check, to detect whether the repeated calculation formula (rechecking ideas reference links: https: //www.cnblogs.com/wxrqforever/p/8679118.html).

 

Figure 3: The expression binary tree

 

 

  Subsequently, in conjunction with the priority calculation result of the operator, by FileUtils class document and save the results to exercise.txt answer.txt document, finally obtaining performance grade after the answer in the console.

 

As well as the method to call between classes to explain:

 Class Description:

  • calc_cmd.py: the main program category, other classes call the run.

  • calc_error.py: exception class, some anomalies in the definition of the entire operation, and generating generated

  • calc_util.py: computing class, used to calculate the formula, and returns the associated anomalies, scored and collected answers

  • expre_tree.py: generating method defined in formula binary tree nodes and class attributes and

  • FileUtils.py: file type, for storing the title exercise.txt, answer.txt, grade.txt file

  • FormatUtils.py: generating hydrocarbons such as expression (postfix expression and re-check), and converts the fraction into a form proper fraction

  • unit_text.py: class test

 

5. Code Description


The main function of the method of the main class, each class by calling parameters submit

 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

 

6. 测试运行


测试生成前缀、后缀和查重表达式:

测试生成运算式

测试答题情况及保存文件

 

7. 项目总结


   本次结对编程收获颇多,"1 + 1 > 2"怕说的就是结对编程了。两个人一起分析题目考虑的情况比单个人的多,因此本次项目我们的算法也经得测试。分工合作,加快开发效率的同时也减少了更多的潜在错误。

  本次项目采用的是命令行窗口,因此相对普通,代码方面争取在下次练习中运用更多python的特性和语法糖以提升程序性能。

Guess you like

Origin www.cnblogs.com/jinhuai/p/11617293.html