Python实现结对编程项目

Github (李昆乘)(陈俊豪)

开发流程

PSP2.1

 

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

 60  90

· Estimate

· 估计这个任务需要多少时间

 60  90

Development

开发

 580 1780

· Analysis

· 需求分析 (包括学习新技术)

 100  80

· Design Spec

· 生成设计文档

 50  120

· Design Review

· 设计复审 (和同事审核设计文档)

 50  200

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

 30  30

· Design

· 具体设计

 100  300

· Coding

· 具体编码

 100  350

· Code Review

· 代码复审

 60  550

· Test

· 测试(自我测试,修改代码,提交修改)

 120  150

Reporting

报告

 60  200

· Test Report

· 测试报告

 20  150

· Size Measurement

· 计算工作量

 10  15

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

 30  35

合计

  700  
2070

 

需求实现 :

  • 使用 -n 参数控制生成题目的个数
  • 使用 -r 参数控制题目中的数值
  • 生成的题目中计算过程不能产生负数
  • 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数
  • 每道题目中出现的运算符个数不超过3个
  • 程序一次运行生成的题目不能重复(正在努力中)
  • 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件
  • 程序应能支持一万道题目的生成
  • 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计

程序概述

(因为网页排版问题,图片字体显示比较小, 可以ctrl+鼠标滚轮放大页面显示比例或者右键该图片, 在新窗口中打开该.image, 可以看到最清晰的图片)

 

 项目分工

 

 

 

性能优化

 

该测试是生成一万条数值100内的算式, 用时437ms, 该程序耗时间大部分是生成每一条函数时调用了的随机函数

  •  优化方案

    • 因为randint产生最大系统耗时

    • 全部换用random原始函数, 借用mod取模运算实现range功能, 避免 randint调用过程中的 randrange耗时, 使用X1,X2两个参数辅助优化符号的选择, 具体实现如下图

     

           +   优化Print 文件写入, 用一个字符串out先生成全部算式, 再一次性写入到文件

    •              

优化效果如下

 

 设计实现过程&代码说明

Class Generate

 

 1 class Genera:
 2 
 3     def __init__(self, numbers, range):
 4         'self.numbers 是生成题目总个数, self.range 是数值的范围'
 5         self.numbers = numbers
 6         self.range = range
 7         self.filename = 'Exercises.txt'
 8         self.Fomulas()
 9 
10     def GeneralOneFormula(self):
11         Range = self.range
12         # OperateNumbers = random.randint(1, 3)
13         X1 = int(random.random() * 10000)
14         X2 = int(random.random() * 10000)
15         OperateNumbers = X1 % 3 + 1
16         CountNUmbers = OperateNumbers + 1
17         Ostyle = ['+', '-', '*', '÷']
18 
19         # 生成符号list
20         Operates = []
21         a = 0
22         while (a <= OperateNumbers):
23             # Operates.append(random.choice(Ostyle))
24             if (a == 0):
25                 Operates.append(Ostyle[X1 % 4])
26             if (a == 1):
27                 Operates.append(Ostyle[X2 % 4])
28             if (a == 2):
29                 Operates.append(Ostyle[(X1 + X2) % 4])
30             a += 1
31         # 生成数字list与括号list
32         Counts = []
33         i = CountNUmbers
34         while (i > 0):
35             X = int(random.random() * 10000) % Range + 1
36             if (X % 10 != 1):
37                 term = str(X)
38                 Counts.append(term)
39             else:
40                 term = [str(X), '/', str(int(random.random() * 10000) % Range + 1)]
41                 termT = ''.join(term)
42                 # 此处插入分数化简
43                 Counts.append(termT)
44             i -= 1
45         if ((Operates.count('-') != 0) and (Operates.count('+') != 0) and (
46                 int(random.random() * 10000) % 7 == 1)):  # 假定1/7的括号生成概率
47             leftPosition = int(random.random() * 10000) % OperateNumbers
48             rightPosition = random.randint(leftPosition + 2, OperateNumbers + 1) - 1
49             # rightPosition = int(random.random() * 10000) % OperateNumbers + 1
50             term = '(' + str(Counts[leftPosition])
51             Counts[leftPosition] = term
52             term = str(Counts[rightPosition]) + ')'
53             Counts[rightPosition] = term
54         # 合并符号list 数字括号list
55         FinalList = []
56         j = 0
57         k = 0
58         i = OperateNumbers + CountNUmbers - 1
59         while (i >= 0):
60             if (i % 2 != 1):
61                 FinalList.append(Counts[j])
62                 j += 1
63             else:
64                 FinalList.append(Operates[k])
65                 k += 1
66             i -= 1
67         FinalList = ''.join(FinalList)
68         return FinalList
69 
70     def Fomulas(self):
71         Range = self.range
72         Numbers = self.numbers
73         ' 生成多个Formula并写入文档 '
74         file = open("Exercises.txt", 'a+')
75         out = ""
76         for i in range(1, Numbers + 1):
77             out = out + self.GeneralOneFormula() + '\n'
78         print(out, file=file)
79         file.close()
Class Genera

 Class Answer

 1 class Answer:
 2     '这是用于生成任何题目文件的结果到Answers.txt中的类'
 3 
 4     def __init__(self, FileName):
 5         self.file = FileName
 6         self.OpenAFile()
 7 
 8     def mul_divOperation(self, s):
 9         sub_str = re.search('(\d+\.?\d*[*/]-?\d+\.?\d*)', s)
10         while sub_str:
11             sub_str = sub_str.group()
12             if sub_str.count('*'):
13                 l_num, r_num = sub_str.split('*')
14                 s = s.replace(sub_str, str(float(l_num) * float(r_num)))
15             else:
16                 l_num, r_num = sub_str.split('/')
17                 s = s.replace(sub_str, str(float(l_num) / float(r_num)))
18             sub_str = re.search('(\d+\.?\d*[*/]\d+\.?\d*)', s)
19         return s
20 
21     def add_minusOperation(self, s):
22         s = '+' + s
23         tmp = re.findall('[+\-]\d+\.?\d*', s)
24         s = str(functools.reduce(lambda x, y: float(x) + float(y), tmp))
25         return s
26 
27     def compute(self, formula):
28         formula = self.mul_divOperation(formula)
29         formula = self.add_minusOperation(formula)
30         return formula
31 
32     def calc(self, formula):
33         """计算程序入口"""
34         if (formula[0] == '(' and formula[len(formula) - 1] == ')'):
35             formula = formula.replace('(', '')
36             formula = formula.replace(')', '')
37         formula = re.sub('[^.()/*÷\-+0-9]', "", formula)  # 清除非算式符号
38         if (formula[1] == '.'):
39             formula = formula.replace(formula[0:2], '')  # 计算含有题目序列号的标准算式
40         has_parenthesise = formula.count('(')
41         while has_parenthesise:
42             sub_parenthesise = re.search('\([^()]*\)', formula)  # 匹配最内层括号
43             if sub_parenthesise:
44                 formula = formula.replace(sub_parenthesise.group(), self.compute(sub_parenthesise.group()[1:-1]))
45             else:
46                 has_parenthesise = False
47         ret = self.compute(formula)
48         return ret
49 
50     def Transfer(self, formula):
51         '这是一个把小数字符串转换成分数的函数'
52         i = formula.find('.')
53         if (i != -1 and formula.find('-') == -1):  # 如果存在小数点,只取小数点后三位
54             e = float(formula[0:i + 4])
55             intE = int(e)
56             term = round(e - intE, 4)  # 小数部分四舍五入
57             if (term == 0): return formula[:i]
58             termD = term * 1000
59             Deno = 1000
60             if (termD % 333 == 0): Deno = 999  # 优化小学生算术题中常出现的1/3
61             while (termD != Deno):  # 求最大公约数以化简
62                 if (Deno > termD): Deno = Deno - termD
63                 if (termD > Deno): termD = termD - Deno
64             term = int(term * 1000 / termD)
65             Deno = int(1000 / termD)
66             if (intE != 0): answers = [str(intE), '\'', str(term), '/', str(Deno)]
67             if (intE == 0): answers = [str(term), '/', str(Deno)]
68             answers = ''.join(answers)
69             return answers
70         else:
71             return formula
72 
73     def OpenAFile(self):
74         fileE = open(self.file, "r+")
75         string = fileE.read()
76         fileE.close()
77         string = string.replace('÷', '/')
78         out = ""
79         for line in string.splitlines():
80             # out = out + self.compute(line) + '\n'
81             out = out.replace('+', '')
82             out = out + self.Transfer(self.calc(line)) + '\n'
83         fileA = open("Answers.txt", "w+")
84         print(out, file=fileA)
85         fileA.close()
Class Answer

Class Verify

 

 

  1 class Verify:
  2     '这是一个用于修正有负数结果的式子,判断式子是否有重复,以及生成题目序号的类,判断/后面有没有0'
  3 
  4     # 筛选出等式中的符号
  5     def __init__(self, FileName):
  6         self.file = FileName
  7         self.VerifyAFile()
  8 
  9     def VerifyAFile(self):
 10         No = 1
 11         with open(self.file) as r:
 12             lines = r.readlines()
 13         with open('StandExercises.txt', 'w') as w:
 14             for l in lines:
 15                 s = l
 16                 s = s.replace('÷', '/')
 17                 if ((self.math_compute(s) == 1)):
 18                     position = re.search('\Z', l).end()
 19                     l = l.replace(l[position - 1], ' = \n')
 20                     l = str(No) + '. ' + l
 21                     w.write(l)
 22                     No += 1
 23         r.close()
 24         w.close()
 25 
 26     def filt_sym(self, e1_fs):
 27         sym_get = ""
 28         for sym in e1_fs:
 29             if sym == '+' or sym == '-' or sym == '*' or sym == '/':
 30                 sym_get = sym_get + sym
 31         return sym_get
 32 
 33     # 筛选出等式中的数字
 34     def filt_num(self, e1_fn):
 35         num_get = []
 36         num_c = ""
 37         for num in e1_fn:
 38             if num != '+' and num != '-' and num != '*' and num != '/':
 39                 flag = 1
 40                 num_c += num
 41             else:
 42                 flag = 0
 43             if flag == 0:
 44                 num_get = num_get + [float(num_c)]
 45                 num_c = ""
 46         num_get = num_get + [float(num_c)]
 47         return num_get
 48 
 49     # 判断优先级
 50     def judge_pri(self, sym_int):
 51         i = 0
 52         sym_p = []
 53         for sym_jp in sym_int:
 54             if sym_jp == '/':
 55                 sym_p += [40 + i]
 56                 i += 1
 57             elif sym_jp == '*':
 58                 sym_p += [30 + i]
 59                 i += 1
 60             else:
 61                 i += 1
 62         i = 0
 63         for sym_jp in sym_int:
 64             if sym_jp == '-':
 65                 sym_p += [20 + i]
 66                 i += 1
 67             elif sym_jp == '+':
 68                 sym_p += [10 + i]
 69                 i += 1
 70             else:
 71                 i += 1
 72         return sym_p
 73 
 74     # 等式运算计算细节实现
 75     def int_compute(self, num_int, sym_int):
 76         sym_p_int = self.judge_pri(sym_int)
 77         while sym_p_int != []:
 78             sym = int(sym_p_int[0])
 79             if sym >= 40:
 80                 if num_int[sym - 40 + 1] == 0:
 81                     return -1
 82                 num_int[sym - 40] /= num_int[sym - 40 + 1]
 83                 num = num_int[sym - 40: sym - 40 + 1]
 84                 del num_int[sym - 40 + 1: sym - 40 + 2]
 85                 sym_int = sym_int[:sym - 40] + sym_int[sym - 40 + 1:]
 86             elif sym >= 30:
 87                 num_int[sym - 30] *= num_int[sym - 30 + 1]
 88                 num = num_int[sym - 30: sym - 30 + 1]
 89                 del num_int[sym - 30 + 1: sym - 30 + 2]
 90                 sym_int = sym_int[:sym - 30] + sym_int[sym - 30 + 1:]
 91             elif sym >= 20:
 92                 num_int[sym - 20] -= num_int[sym - 20 + 1]
 93                 num = num_int[sym - 20: sym - 20 + 1]
 94                 if num[0] < 0:
 95                     return -1
 96                 del num_int[sym - 20 + 1: sym - 20 + 2]
 97                 sym_int = sym_int[:sym - 20] + sym_int[sym - 20 + 1:]
 98             elif sym >= 10:
 99                 num_int[sym - 10] += num_int[sym - 10 + 1]
100                 num = num_int[sym - 10: sym - 10 + 1]
101                 del num_int[sym - 10 + 1: sym - 10 + 2]
102                 sym_int = sym_int[:sym - 10] + sym_int[sym - 10 + 1:]
103             sym_p_int = self.judge_pri(sym_int)
104         return float(num[0])
105 
106     # 等式运算
107     def compute_c(self, e1):
108         num_int = float()
109         num_int = self.filt_num(e1)
110         sym_int = self.filt_sym(e1)
111         flag = self.int_compute(num_int, sym_int)
112         if flag < 0:
113             return 'f'
114         else:
115             return str(flag)
116 
117     # 将等式中括号里面的等式提取出来
118     def judge_bracket(self, equ_j):
119         left = equ_j.rfind('(')
120         right = equ_j.find(')', left)
121         e1 = equ_j[left + 1:right]
122         c1 = self.compute_c(e1)
123         if c1 == 'f':
124             return False
125         equ_j = equ_j[0:left] + str(c1) + equ_j[(left + len(c1)):]
126         equ_j = equ_j[0: left + len(str(c1))] + equ_j[right + 1:]
127         return equ_j
128 
129     def math_compute(self, equation):
130         equ_m = equation
131         while equ_m.find('(') != -1:
132             if equ_m.find('(') != -1:
133                 equ_m = self.judge_bracket(equ_m)
134                 if not equ_m:
135                     break;
136             else:
137                 break
138         if not equ_m:
139             return 0
140         elif equ_m.find('+') != -1 or equ_m.find('-') != -1 or equ_m.find('*') != -1 or equ_m.find('/') != -1:
141             val = self.compute_c(equ_m)
142             if val == 'f':
143                 return 0
144             else:
145                 return 1
146         else:
147             return 1
Class Verify

Class Judge

Class Judge

 1 class Judge:
 2     '判断Exercises 和 Answers.txt ,并返回处理结果'
 3 
 4     def __init__(self, FileName, FilenameAns):
 5         self.user_file = FileName
 6         self.standAns_file = FilenameAns
 7         self.judge_ans(self.user_file, self.standAns_file)
 8 
 9     def judge_ans(self, user_ans, stand_ans):
10         user_a = open(user_ans, 'r')
11         std_a = open(stand_ans, 'r')
12         i = 0
13         c_sum = []
14         e_sum = []
15         while 1:
16             equa_u = user_a.readline()
17             equa_s = std_a.readline()
18             if not equa_u:
19                 break
20             ind = equa_u.rfind('=')
21             if equa_u[ind + 1:].strip() == equa_s.strip():
22                 i += 1
23                 c_sum += [i]
24             else:
25                 i += 1
26                 e_sum += [i]
27         print("Correct: ", len(c_sum), c_sum)
28         print("Wrong: ", len(e_sum), e_sum)
Class Judge

命令行传参代码

 1 from optparse import OptionParser
 2 
 3 usage = "[<-n> + 数字] 确定题目条数 [<-r> + 数字] 确定数字范围 \n 可选参数: \n <-u> 生成有负数出现的题目 \n [<-a> + (filename)] 回答filename文件的题目 \n [<-j> + (filename)] 批改filename文件的题目"
 4 parser = OptionParser(usage)
 5 parser.print_help()
 6 parser.add_option("-n", action='store', type='int', dest='Numbers', help="生成Numbers条无负数结果的算式,输出文件是StandExercises.txt")
 7 parser.add_option("-r", action='store', type='int', dest='Range', help="指定数字Range范围")
 8 parser.add_option("-u", action='store', type='string', dest='ProExFile', help="生成Numbers条有负数结果的算式,输出文件时Exercises.txt")
 9 parser.add_option("-a", action='store', type='string', dest='AnsFile', help="指定题目文件,并生成答案到Answers.txt")
10 parser.add_option("-j", action='store', type='string', dest='JudgeFile', help="指定用户答案文件,并将其和标准Answers.txt对比")
11 options, args = parser.parse_args()
12 
13 if options.Numbers and options.Range and options.ProExFile:
14     '生成Numbers条有负数结果的算式, 再将其标准化(去除中间过程有负数结果的算式以及/后面有0的非法算式), 输出文件是StandExercises.txt'
15     fileE = Genera(options.Numbers, options.Range)
16     fileStand = Verify(fileE.filename)
17 
18 if options.Numbers and options.Range and options.ProExFile and options.AnsFile:
19     '生成Numbers条有负数结果的算式, 再将其标准化(去除中间过程有负数结果的算式以及/后面有0的非法算式), 输出文件是StandExercises.txt'
20     fileE = Genera(options.Numbers, options.Range)
21     fileStand = Verify(fileE.filename)
22     fileA = Answer(options.AnsFile)
23 
24 if options.AnsFile and not options.Numbers:
25     '回答-a后面的filename题目文件,并输出结果到Answers.txt文件'
26     fileA = Answer(options.AnsFile)
27 
28 if options.ProExFile and options.Numbers and options.Range and not options.AnsFile:
29     '生成Numbers条有负数结果的算式, 生成文件是Exercises.txt'
30     fileE = Genera(options.Numbers, options.Range)
31 
32 if options.JudgeFile and not options.Numbers and not options.Range and not options.ProExFile:
33     '-j 接一个用户的答案文件, 并将其和标准答案文件Answers.txt比较'
34     FileA = Judge(options.JudgeFile, "Answers.txt")
命令行传参

 

  1 import re
  2 import functools
  3 # -*- coding: UTF-8 -*-
  4 import random
  5 from optparse import OptionParser
  6 
  7 usage = "[<-n> + 数字] 确定题目条数 [<-r> + 数字] 确定数字范围 \n 可选参数: \n <-u> 生成有负数出现的题目 \n [<-a> + (filename)] 回答filename文件的题目 \n [<-j> + (filename)] 批改filename文件的题目"
  8 parser = OptionParser(usage)
  9 parser.print_help()
 10 parser.add_option("-n", action='store', type='int', dest='Numbers', help="生成Numbers条无负数结果的算式,输出文件是StandExercises.txt")
 11 parser.add_option("-r", action='store', type='int', dest='Range', help="指定数字Range范围")
 12 parser.add_option("-u", action='store', type='string', dest='ProExFile', help="生成Numbers条有负数结果的算式,输出文件时Exercises.txt")
 13 parser.add_option("-a", action='store', type='string', dest='AnsFile', help="指定题目文件,并生成答案到Answers.txt")
 14 parser.add_option("-j", action='store', type='string', dest='JudgeFile', help="指定用户答案文件,并将其和标准Answers.txt对比")
 15 options, args = parser.parse_args()
 16 
 17 
 18 class Genera:
 19 
 20     def __init__(self, numbers, range):
 21         'self.numbers 是生成题目总个数, self.range 是数值的范围'
 22         self.numbers = numbers
 23         self.range = range
 24         self.filename = 'Exercises.txt'
 25         self.Fomulas()
 26 
 27     def GeneralOneFormula(self):
 28         Range = self.range
 29         # OperateNumbers = random.randint(1, 3)
 30         X1 = int(random.random() * 10000)
 31         X2 = int(random.random() * 10000)
 32         OperateNumbers = X1 % 3 + 1
 33         CountNUmbers = OperateNumbers + 1
 34         Ostyle = ['+', '-', '*', '÷']
 35 
 36         # 生成符号list
 37         Operates = []
 38         a = 0
 39         while (a <= OperateNumbers):
 40             # Operates.append(random.choice(Ostyle))
 41             if (a == 0):
 42                 Operates.append(Ostyle[X1 % 4])
 43             if (a == 1):
 44                 Operates.append(Ostyle[X2 % 4])
 45             if (a == 2):
 46                 Operates.append(Ostyle[(X1 + X2) % 4])
 47             a += 1
 48         # 生成数字list与括号list
 49         Counts = []
 50         i = CountNUmbers
 51         while (i > 0):
 52             X = int(random.random() * 10000) % Range + 1
 53             if (X % 10 != 1):
 54                 term = str(X)
 55                 Counts.append(term)
 56             else:
 57                 term = [str(X), '/', str(int(random.random() * 10000) % Range + 1)]
 58                 termT = ''.join(term)
 59                 # 此处插入分数化简
 60                 Counts.append(termT)
 61             i -= 1
 62         if ((Operates.count('-') != 0) and (Operates.count('+') != 0) and (
 63                 int(random.random() * 10000) % 7 == 1)):  # 假定1/7的括号生成概率
 64             leftPosition = int(random.random() * 10000) % OperateNumbers
 65             rightPosition = random.randint(leftPosition + 2, OperateNumbers + 1) - 1
 66             # rightPosition = int(random.random() * 10000) % OperateNumbers + 1
 67             term = '(' + str(Counts[leftPosition])
 68             Counts[leftPosition] = term
 69             term = str(Counts[rightPosition]) + ')'
 70             Counts[rightPosition] = term
 71         # 合并符号list 数字括号list
 72         FinalList = []
 73         j = 0
 74         k = 0
 75         i = OperateNumbers + CountNUmbers - 1
 76         while (i >= 0):
 77             if (i % 2 != 1):
 78                 FinalList.append(Counts[j])
 79                 j += 1
 80             else:
 81                 FinalList.append(Operates[k])
 82                 k += 1
 83             i -= 1
 84         FinalList = ''.join(FinalList)
 85         return FinalList
 86 
 87     def Fomulas(self):
 88         Range = self.range
 89         Numbers = self.numbers
 90         ' 生成多个Formula并写入文档 '
 91         file = open("Exercises.txt", 'a+')
 92         out = ""
 93         for i in range(1, Numbers + 1):
 94             out = out + self.GeneralOneFormula() + '\n'
 95         print(out, file=file)
 96         file.close()
 97 
 98 
 99 class Answer:
100     '这是用于生成任何题目文件的结果到Answers.txt中的类'
101 
102     def __init__(self, FileName):
103         self.file = FileName
104         self.OpenAFile()
105 
106     def mul_divOperation(self, s):
107         sub_str = re.search('(\d+\.?\d*[*/]-?\d+\.?\d*)', s)
108         while sub_str:
109             sub_str = sub_str.group()
110             if sub_str.count('*'):
111                 l_num, r_num = sub_str.split('*')
112                 s = s.replace(sub_str, str(float(l_num) * float(r_num)))
113             else:
114                 l_num, r_num = sub_str.split('/')
115                 s = s.replace(sub_str, str(float(l_num) / float(r_num)))
116             sub_str = re.search('(\d+\.?\d*[*/]\d+\.?\d*)', s)
117         return s
118 
119     def add_minusOperation(self, s):
120         s = '+' + s
121         tmp = re.findall('[+\-]\d+\.?\d*', s)
122         s = str(functools.reduce(lambda x, y: float(x) + float(y), tmp))
123         return s
124 
125     def compute(self, formula):
126         formula = self.mul_divOperation(formula)
127         formula = self.add_minusOperation(formula)
128         return formula
129 
130     def calc(self, formula):
131         """计算程序入口"""
132         if (formula[0] == '(' and formula[len(formula) - 1] == ')'):
133             formula = formula.replace('(', '')
134             formula = formula.replace(')', '')
135         formula = re.sub('[^.()/*÷\-+0-9]', "", formula)  # 清除非算式符号
136         if (formula[1] == '.'):
137             formula = formula.replace(formula[0:2], '')  # 计算含有题目序列号的标准算式
138         has_parenthesise = formula.count('(')
139         while has_parenthesise:
140             sub_parenthesise = re.search('\([^()]*\)', formula)  # 匹配最内层括号
141             if sub_parenthesise:
142                 formula = formula.replace(sub_parenthesise.group(), self.compute(sub_parenthesise.group()[1:-1]))
143             else:
144                 has_parenthesise = False
145         ret = self.compute(formula)
146         return ret
147 
148     def Transfer(self, formula):
149         '这是一个把小数字符串转换成分数的函数'
150         i = formula.find('.')
151         if (i != -1 and formula.find('-') == -1):  # 如果存在小数点,只取小数点后三位
152             e = float(formula[0:i + 4])
153             intE = int(e)
154             term = round(e - intE, 4)  # 小数部分四舍五入
155             if (term == 0): return formula[:i]
156             termD = term * 1000
157             Deno = 1000
158             if (termD % 333 == 0): Deno = 999  # 优化小学生算术题中常出现的1/3
159             while (termD != Deno):  # 求最大公约数以化简
160                 if (Deno > termD): Deno = Deno - termD
161                 if (termD > Deno): termD = termD - Deno
162             term = int(term * 1000 / termD)
163             Deno = int(1000 / termD)
164             if (intE != 0): answers = [str(intE), '\'', str(term), '/', str(Deno)]
165             if (intE == 0): answers = [str(term), '/', str(Deno)]
166             answers = ''.join(answers)
167             return answers
168         else:
169             return formula
170 
171     def OpenAFile(self):
172         fileE = open(self.file, "r+")
173         string = fileE.read()
174         fileE.close()
175         string = string.replace('÷', '/')
176         out = ""
177         for line in string.splitlines():
178             # out = out + self.compute(line) + '\n'
179             out = out.replace('+', '')
180             out = out + self.Transfer(self.calc(line)) + '\n'
181         fileA = open("Answers.txt", "w+")
182         print(out, file=fileA)
183         fileA.close()
184 
185 
186 class Verify:
187     '这是一个用于修正有负数结果的式子,判断式子是否有重复,以及生成题目序号的类,判断/后面有没有0'
188 
189     # 筛选出等式中的符号
190     def __init__(self, FileName):
191         self.file = FileName
192         self.VerifyAFile()
193 
194     def VerifyAFile(self):
195         No = 1
196         with open(self.file) as r:
197             lines = r.readlines()
198         with open('StandExercises.txt', 'w') as w:
199             for l in lines:
200                 s = l
201                 s = s.replace('÷', '/')
202                 if ((self.math_compute(s) == 1)):
203                     position = re.search('\Z', l).end()
204                     l = l.replace(l[position - 1], ' = \n')
205                     l = str(No) + '. ' + l
206                     w.write(l)
207                     No += 1
208         r.close()
209         w.close()
210 
211     def filt_sym(self, e1_fs):
212         sym_get = ""
213         for sym in e1_fs:
214             if sym == '+' or sym == '-' or sym == '*' or sym == '/':
215                 sym_get = sym_get + sym
216         return sym_get
217 
218     # 筛选出等式中的数字
219     def filt_num(self, e1_fn):
220         num_get = []
221         num_c = ""
222         for num in e1_fn:
223             if num != '+' and num != '-' and num != '*' and num != '/':
224                 flag = 1
225                 num_c += num
226             else:
227                 flag = 0
228             if flag == 0:
229                 num_get = num_get + [float(num_c)]
230                 num_c = ""
231         num_get = num_get + [float(num_c)]
232         return num_get
233 
234     # 判断优先级
235     def judge_pri(self, sym_int):
236         i = 0
237         sym_p = []
238         for sym_jp in sym_int:
239             if sym_jp == '/':
240                 sym_p += [40 + i]
241                 i += 1
242             elif sym_jp == '*':
243                 sym_p += [30 + i]
244                 i += 1
245             else:
246                 i += 1
247         i = 0
248         for sym_jp in sym_int:
249             if sym_jp == '-':
250                 sym_p += [20 + i]
251                 i += 1
252             elif sym_jp == '+':
253                 sym_p += [10 + i]
254                 i += 1
255             else:
256                 i += 1
257         return sym_p
258 
259     # 等式运算计算细节实现
260     def int_compute(self, num_int, sym_int):
261         sym_p_int = self.judge_pri(sym_int)
262         while sym_p_int != []:
263             sym = int(sym_p_int[0])
264             if sym >= 40:
265                 if num_int[sym - 40 + 1] == 0:
266                     return -1
267                 num_int[sym - 40] /= num_int[sym - 40 + 1]
268                 num = num_int[sym - 40: sym - 40 + 1]
269                 del num_int[sym - 40 + 1: sym - 40 + 2]
270                 sym_int = sym_int[:sym - 40] + sym_int[sym - 40 + 1:]
271             elif sym >= 30:
272                 num_int[sym - 30] *= num_int[sym - 30 + 1]
273                 num = num_int[sym - 30: sym - 30 + 1]
274                 del num_int[sym - 30 + 1: sym - 30 + 2]
275                 sym_int = sym_int[:sym - 30] + sym_int[sym - 30 + 1:]
276             elif sym >= 20:
277                 num_int[sym - 20] -= num_int[sym - 20 + 1]
278                 num = num_int[sym - 20: sym - 20 + 1]
279                 if num[0] < 0:
280                     return -1
281                 del num_int[sym - 20 + 1: sym - 20 + 2]
282                 sym_int = sym_int[:sym - 20] + sym_int[sym - 20 + 1:]
283             elif sym >= 10:
284                 num_int[sym - 10] += num_int[sym - 10 + 1]
285                 num = num_int[sym - 10: sym - 10 + 1]
286                 del num_int[sym - 10 + 1: sym - 10 + 2]
287                 sym_int = sym_int[:sym - 10] + sym_int[sym - 10 + 1:]
288             sym_p_int = self.judge_pri(sym_int)
289         return float(num[0])
290 
291     # 等式运算
292     def compute_c(self, e1):
293         num_int = float()
294         num_int = self.filt_num(e1)
295         sym_int = self.filt_sym(e1)
296         flag = self.int_compute(num_int, sym_int)
297         if flag < 0:
298             return 'f'
299         else:
300             return str(flag)
301 
302     # 将等式中括号里面的等式提取出来
303     def judge_bracket(self, equ_j):
304         left = equ_j.rfind('(')
305         right = equ_j.find(')', left)
306         e1 = equ_j[left + 1:right]
307         c1 = self.compute_c(e1)
308         if c1 == 'f':
309             return False
310         equ_j = equ_j[0:left] + str(c1) + equ_j[(left + len(c1)):]
311         equ_j = equ_j[0: left + len(str(c1))] + equ_j[right + 1:]
312         return equ_j
313 
314     def math_compute(self, equation):
315         equ_m = equation
316         while equ_m.find('(') != -1:
317             if equ_m.find('(') != -1:
318                 equ_m = self.judge_bracket(equ_m)
319                 if not equ_m:
320                     break;
321             else:
322                 break
323         if not equ_m:
324             return 0
325         elif equ_m.find('+') != -1 or equ_m.find('-') != -1 or equ_m.find('*') != -1 or equ_m.find('/') != -1:
326             val = self.compute_c(equ_m)
327             if val == 'f':
328                 return 0
329             else:
330                 return 1
331         else:
332             return 1
333 
334 
335 class Judge:
336     '判断Exercises 和 Answers.txt ,并返回处理结果'
337 
338     def __init__(self, FileName, FilenameAns):
339         self.user_file = FileName
340         self.standAns_file = FilenameAns
341         self.judge_ans(self.user_file, self.standAns_file)
342 
343     def judge_ans(self, user_ans, stand_ans):
344         user_a = open(user_ans, 'r')
345         std_a = open(stand_ans, 'r')
346         i = 0
347         c_sum = []
348         e_sum = []
349         while 1:
350             equa_u = user_a.readline()
351             equa_s = std_a.readline()
352             if not equa_u:
353                 break
354             ind = equa_u.rfind('=')
355             if equa_u[ind + 1:].strip() == equa_s.strip():
356                 i += 1
357                 c_sum += [i]
358             else:
359                 i += 1
360                 e_sum += [i]
361         print("Correct: ", len(c_sum), c_sum)
362         print("Wrong: ", len(e_sum), e_sum)
363 
364 
365 if options.Numbers and options.Range and options.ProExFile:
366     '生成Numbers条有负数结果的算式, 再将其标准化(去除中间过程有负数结果的算式以及/后面有0的非法算式), 输出文件是StandExercises.txt'
367     fileE = Genera(options.Numbers, options.Range)
368     fileStand = Verify(fileE.filename)
369 
370 if options.Numbers and options.Range and options.ProExFile and options.AnsFile:
371     '生成Numbers条有负数结果的算式, 再将其标准化(去除中间过程有负数结果的算式以及/后面有0的非法算式), 输出文件是StandExercises.txt'
372     fileE = Genera(options.Numbers, options.Range)
373     fileStand = Verify(fileE.filename)
374     fileA = Answer(options.AnsFile)
375 
376 if options.AnsFile and not options.Numbers:
377     '回答-a后面的filename题目文件,并输出结果到Answers.txt文件'
378     fileA = Answer(options.AnsFile)
379 
380 if options.ProExFile and options.Numbers and options.Range and not options.AnsFile:
381     '生成Numbers条有负数结果的算式, 生成文件是Exercises.txt'
382     fileE = Genera(options.Numbers, options.Range)
383 
384 if options.JudgeFile and not options.Numbers and not options.Range and not options.ProExFile:
385     '-j 接一个用户的答案文件, 并将其和标准答案文件Answers.txt比较'
386     FileA = Judge(options.JudgeFile, "Answers.txt")
全部代码

 

测试运行: 

 

 

实现判断函数是否重复思路

 

  • 具体实现

 

    def hash(self,string):
        # 获取算式符数目
        fuhao = re.finditer('[+\-*÷]', string)
        i = -1
        for match in fuhao:
            i = i + 1
        # 获取算式结果
        s = string.replace(' ', '')
        t = re.search('=', s).end()
        s = int(s[t:])
        return (s % 10) + (i * 10)        # s%10表示算式结果模10, i*10表示符号位, 如22则表示三个符号且算式结果个位数是2 11则表示二个符号且算式结果个位数是1
算哈希值
  1 #!/usr/bin/python
  2 # -*- coding: utf-8 -*-
  3 import re
  4 num = 30
  5 
  6 
  7 # 一个数据节点
  8 class Node(object):
  9     def __init__(self, data):
 10         self.data = data
 11         self.next_node = None
 12 
 13     def set_next(self, node):
 14         self.next_node = node
 15 
 16     def get_next(self):
 17         return self.next_node
 18 
 19     def get_data(self):
 20         return self.data
 21 
 22     def data_equals(self, data):
 23         return self.data == data
 24 
 25 
 26 class HashTable(object):
 27     def __init__(self):
 28         self.value = [None] * num
 29 
 30     def hash(self,string):
 31         # 获取算式符数目
 32         fuhao = re.finditer('[+\-*÷]', string)
 33         i = -1
 34         for match in fuhao:
 35             i = i + 1
 36         # 获取算式结果
 37         s = string.replace(' ', '')
 38         t = re.search('=', s).end()
 39         s = int(s[t:])
 40         return (s % 10) + (i * 10)        # s%10表示算式结果模10, i*10表示符号位, 如22则表示三个符号且算式结果个位数是2 11则表示二个符号且算式结果个位数是1
 41 
 42     def insert(self, data):
 43         # if self.search(data):
 44         #     return True
 45 
 46         i = self.hash(data)
 47         node = Node(data)
 48         if self.value[i] is None:
 49             self.value[i] = node
 50             return True
 51         else:
 52             head = self.value[i]
 53             while head.get_next() is not None:
 54                 head = head.get_next()
 55             head.set_next(node)
 56             return True
 57 
 58     def search(self, data):
 59         i = self.hash(data)
 60         if self.value[i] is None:
 61             return False
 62         else:
 63             head = self.value[i]
 64             while head and not head.data_equals(data):
 65                 head = head.get_next()
 66             if head:
 67                 return head
 68             else:
 69                 return False
 70 
 71     def delete(self, data):
 72         if self.search(data):
 73             i = self.hash(data)
 74             if self.value[i].data_equals(data):
 75                 self.value[i] = self.value[i].get_next()
 76             else:
 77                 head = self.value[i]
 78                 while not head.get_next().data_equals(data):
 79                     head = head.get_next()
 80                 head.set_next(head.get_next().get_next())
 81             return True
 82         else:
 83             return False
 84 
 85     def echo(self):
 86         i = 0
 87         for head in self.value:
 88             print (str(i) + ':\t'),
 89             if head is None:
 90                 print (None),
 91             else:
 92                 while head is not None:
 93                     print (str(head.get_data()) + ' ->'),
 94                     head = head.get_next()
 95                 print (None),
 96             print ('')
 97             i += 1
 98         print("")
 99 
100 #
101 # 1. 4÷86 = 12
102 # 2. 63÷16÷46*56 = 23
103 # 3. 99+38-59 = 12
104 # 4. 74-50 = 23
105 # 5. 52+47+34+4 = 24
106 # 6. 49*99 = 3
107 # 7. 4÷20 = 4
108 # 8. 1/86*78 = 5
109 
110 
111 if __name__ == '__main__':
112     hashTable = HashTable()
113     hashTable.insert("4÷86 = 12")
114     hashTable.insert("4÷86 = 12")
115     hashTable.insert("2. 63÷16÷46*56 = 23")
116     hashTable.insert("74-50 = 23")
117     hashTable.insert("8. 1/86*78 = 5")
118     hashTable.echo()
119     # hashTable.delete(11)
120     hashTable.echo()
构建哈希表

 

合作编程日志

2018-09-18 20:47:12 实现生成并写入文件,相关代码上传到Github

2018-09-20 22:11:29

beng le !  

 2018-09-23 10:33:53

继续调整思路。已经用正则表达式对字符串进行分割运算实现了多个符号存在时候的运算优先级顺序,但是在生成输出结果的时候遇到困难,解决方法是从Exercises.txt里面依次读取每一行,并对每一行进行运算后得出一个关于结果的标准CONTEXT字符串,再把CONTEXT字符串一次性写入Answers.txt中。此种做法1.可以计算任意符合要求的.txt文件,2. 利用CONTEXT一次性写入,极大优化了程序

 2018-09-26 23:39:33

小组讨论和分析,全部重写算式生成函数,以及重写判断负数的算法

 2018-09-28 16:04:17 

在已有功能函数的基础上全部重写。因为团队合作中各种函数传参和处理没有讨论好。并且感谢俊豪同学及时提出问题,我已经写出的函数存在很多缺漏,以及有部分函数未按题目要求编写。/哭泣

合作心得:

昆乘: 合作过程中还是有非常非常多问题, 首先因为大家有选修课, 共同编程的时间并不多,其次是软件工程项目的时间计划没有做, 前面我已经实现了生成和计算然后就丢下了, 最后两天要提交了的时候才发现原来还有很多很多功能没实现. 然后最后赶进度, 函数还有很多很多值得改善的地方都没有做好. 两人小组的时间分配实在做得太差. 没有约定好哪个函数在哪个时间要编写出来, 哪个类什么时候结合, 哪个函数如何测试和优化 都没有计划出来. 因为没有提前合作构思和安排好各个类各个函数的接口, 导致所有函数有两次重写. 很多合作 和 时间计划相关的事情值得反思. 最大的收获明确使用各个类与功能并包装函数, 花了几个小时进行了性能优化, 觉得性能优化很有意思! 虽然还有一些函数还没优化. 编程能力还需要继续提升,算法和数据结构等也要重温. 

俊豪: 合作一起同做一个项目比起一个人独自做相比较来说还更累一点,然而,对于存在问题的查找也更加深入。整个过程当中,有过争执,对于分数答案的一个生成两个人有不同的想法,昆乘想用正则表达式,而我觉得才用公倍数乘以有除号存在等式,而最终,因为我的算法有很大的bug,最终才用他的;除了争执,还有过讨论,一开始昆乘尝试一个人写生成函数时,在两人还没开始讨论的情况下,算法考虑不周全,比如,没有生成括号。最后两个人通过一起讨论,对需求进行一步步分析,最终得到一个比较完整周全的方案。整个过程下来,让我感觉到了两个人一起结对编程的不容易,合作本来是为了更好的编程,而不合理的合作却又会使得编程更加困难复杂。讨论沟通是整个过程中最为重要的事,否则会两人极其容易产生分歧,产生分歧时沟通更是需要,不然项目将很难做下去。

总的来说这是一次太匆忙的项目:

 

posted @ 2018-09-18 07:52  李learning  阅读(607)  评论(1)    收藏  举报