ソフトタスク(III):双晶プロジェクト - 四則ジェネレータ(Python実装)

1. Githubのアドレスとプロジェクトメンバー


 

2. PSPテーブル:


 

PSP2.1

パーソナルソフトウェアプロセス段階

推定時間がかかる(分)

実際の時間がかかる(分)

プランニング

計画

 20

 20

・見積り

•このタスクが必要と推定どのくらいの時間

 20

 20 

開発

開発

 1560

 1490

・分析

・分析(新しい技術を学ぶ含む)が必要

 70

 60

・デザインスペック

設計ドキュメントの生成

 60

 60

・デザインレビュー

・デザインレビュー(と彼の同僚は、設計文書を見直し)

 80

 90

・コーディング標準

・コードの仕様(現在の開発のための適切な規範の開発)

 20

 20

・ 設計

・具体的な設計

 80

 90 

・コーディング

・具体的なコーディング

 1000年

 905

・コードレビュー

・コードレビュー

 120

 120

・テスト

・テスト(セルフテスト、コードを変更し、変更を提出)

 130

 145

報告

レポート

 150

 150

・ 試験報告書

・テストレポート

 60

 70

・サイズ測定

・コンピューティングのワークロード

 30

 30

・死後&プロセス改善計画

・後知恵、およびプロセス改善計画を提案します

 60

 50

トータル

 

 1730

 1660

 

3.効果分析


入力:-rは(と題し、数万の数字と10の範囲内の分数の分母)10、-n 10000

総走行時間:最適化前3.002s

分析と最適化プロセス:

  • それはプログラムが行われることが判明したパフォーマンス分析観察ランダムに生成されたオペランド計算を
  • さらにまた、見つかった冗長な計算を、分数IO書き込みスコアモジュールと時間の大きな割合つつ。

上記態様から主としてそのため、最適化プロセス。

最適化後:総走行時間1.727s

 

図1:最適化のパフォーマンス分析の前に

図2:最適化されたパフォーマンスの分析

追加:有効性を最適化するプロセスでは、我々は、(INGのもつれ)の議論を経て、最適化のための別の部屋を発見し、ランダムに生成するプロセスである、あなたはそれが同じ式(すなわち複雑な要件、(1を生成しません保証する休むことができます+ 2)+ 3,3 +(1 + 2)も生成されません)、そして最後の式を再確認するには、私たちの証拠のツールになります!最後に、我々は(リピートタイトルがあるかどうかをテストする)テストを書いた、1Wのトラックタイトル、トラックタイトルリピートタイトルと100W生産された場合、繰り返しスローされた例外の結果が0で試験しました。

 

4.設計と実装プロセス


アイデアやパネルディスカッションを設計します。

  タイトルと需要分析を読んだ後、私たちはグループのプロジェクトは、式を生成し、再チェックするために、3つの部分、1に分けられます。そして最終的にはユーザー入力の回答とスコアを記録し、2番目は計算し、話題を保存して答えることです。

  操作範囲を設定-rパラメータ、モジュールパラメータが-Nユーザ入力によって対象Argparseなどを発生する負のタイトル番号かどうか。

  初期の議論は、我々は算術式を生成するために、単純な乱数と演算子を埋めるの使用を想定し、検出方法は、この矛盾の重みに答えを確認するために、あまり効率的で、その後の拡張を容易にしないでください。したがって、我々が議論して使用することが決定されたバイナリツリーランダムに生成された表現をオペランドと演算子を含む層によってノードnode発現、算術バイナリツリーツリークラス発生層のクラス構造体の内部に設けられた(オペレータの親ノードをリーフノードは、図のように操作回数である。3)。加算、減算の演算処理を一致オペレータ優先op_priorityを付与し、そして結合することによって、挿入演算子は、対応する優先度を生成します。再チェック表現を生成するために後置表現を通過した後、繰り返し計算式(ます。https://www.cnblogs.com/wxrqforever/p/8679118.html再チェックアイデアはリンクを参照)かどうかを検出します。

 

図3:表現バイナリーツリー

 

 

  その後、オペレータの優先度計算結果と併せてのfileutilsクラスの文書によると、最終的には、コンソールで答えた後、パフォーマンスのグレードを取得し、answer.txt文書をexercise.txtし、結果を保存します。

 

説明するために、クラス間に呼び出すのと同様の方法:

 クラス説明:

  • calc_cmd.py:メインプログラムのカテゴリは、他のクラスは実行を呼び出します。

  • calc_error.py:例外クラス、全体の動作の定義にいくつかの異常、及び生成発生

  • calc_util.py:計算クラスは、式を計算するために使用され、関連する異常を返し、得点及び回答を収集します

  • expre_tree.py:生成式バイナリツリーノードに規定された方法とクラス属性と

  • FileUtils.py:ファイルタイプ、タイトルexercise.txtを格納するため、answer.txt、grade.txtファイル

  • FormatUtils.py:このような式(後置表現及び再チェック)などの炭化水素を生成し、フォーム適切な画分に分画を変換

  • unit_text.py:クラスのテスト

 

5.コード説明


パラメータが提出呼び出すことで、メインクラス、各クラスのメソッドの主な機能

 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的特性和语法糖以提升程序性能。

おすすめ

転載: www.cnblogs.com/jinhuai/p/11617293.html