这个作业属于哪个课程 | 计科国际班 |
---|---|
这个作业的要求 | 作业要求 |
参与人员 | 姓名 | 学号 |
---|---|---|
1 | 李颂豪 | 3119009436 |
2 | 赵铭骏 | 3119009448 |
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 15 |
Estimate | 估计这个任务需要多少时间 | 25 | 15 |
Development | 开发 | 1080 | 670 |
Analysis | 需求分析 (包括学习新技术) | 240 | 200 |
Design Spec | 生成设计文档 | 20 | 30 |
Design Review | 设计复审 | 30 | 30 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 0 |
Design | 具体设计 | 60 | 100 |
Coding | 具体编码 | 450 | 360 |
Code Review | 代码复审 | 20 | 100 |
Test | 测试(自我测试,修改代码,提交修改) | 15 | 50 |
Reporting | 报告 | 50 | 190 |
Test Report | 测试报告 | 20 | 100 |
Size Measurement | 计算工作量 | 15 | 30 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 15 | 60 |
Sum up | 合计 | 1460 | 875 |
设计实现过程
1.对算式的操作和运算
储存真分数的结构和储存一条算式所用到的类的结构
由于涉及真分数运算,并且网络上没有合适的包表达真分数,所以自定义了真分数的加减乘除运算、分数的约分、对真分数的值的计算、对算式的值的计算
2.生成表达式
此处逻辑为:以两个数字和一个运算符号为基本单位,生成若干个基本单位组合在一起形成一条算式,生成多条算式并返回结果
3.将表达式转换为逆波兰式
- 中缀表达式
人类最熟悉的一种表达式1+2,(1+2)3,3+42+4等等都是中缀表示法。对于人们来说,也是最直观的一种求值方式,先算括号里的,
然后算乘除,最后算加减,但是,计算机处理中缀表达式却并不方便,因为没有一种简单的数据结构可以方便从一个表达式中间抽出
一部分算完结果,再放进去,然后继续后面的计算(链表也许可以,但是,代价也是不菲)。
*前缀表达式
由前缀表达式求出结果有下面两种思路:
1.从左至右扫描表达式,如果一个操作符后面跟着两个操作数时,则计算,然后将结果作为操作数替换(这个操作符和两个操作数),
重复此步骤,直至所有操作符处理完毕。如-+AB-CDEF,扫描到-CD时,会计算C-D=C',表达式变成:-+ABC'EF
继续扫描到BC',计算BC'=B',表达式变成:-+AB'*EF,继续+AB',依此类推。
2.由1.知,要多遍扫描表达式,并且需要将3个字符替换成1个,比较繁锁,我们可以用一个栈S2来实现计算,扫描从右往左进行,
如果扫描到操作数,则压进S2,如果扫描到操作符,则从S2弹出两个操作数进行相应的操作,并将结果压进S2(S2的个数出2个进1个),
当扫描结束后,S2的栈顶就是表达式结果。
*后缀表达式
后缀表达式又叫做逆波兰式。它是由相应的语法树的后序遍历的结果得到的。如上图的后缀表达式为:
A B C D - * + E F * -
由前缀表达式求出结果十分方便,只需要用一个栈实现:
我们可以用一个栈S2来实现计算,扫描从左往右进行,如果扫描到操作数,则压进S2,如果扫描到操作符,则从S2弹出两个操作数
进行相应的操作,并将结果压进S2(S2的个数出2个进1个),当扫描结束后,S2的栈顶就是表达式结果。后缀表达式和前缀表达式看
起来就像一对逆过程,实际上并不是这样子,因为字符读取的时候都是从左往右的,所以,前缀表达式往往需要用两个栈来计算,
其中一个栈用来预处理:将字符串倒序压进栈中。
以上是我们在网上查阅资料得出。原文链接:https://blog.csdn.net/linraise/article/details/20459751
将生成的式子转为逆波兰式
`class Check(object):
# 生成逆波兰式
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.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 isOperator(self, operator):
if operator == "+" or operator == "-" or operator == "×" or operator == "÷" or operator == "(" or operator == ")":
return True
else:
return False
# 当两颗树value值相等时判定优先级
def priority(self, operatorout, operatorin):
m = -1
n = -1
addop = [["+", "-", "×", "÷", "(", ")"], ["+", "-", "×", "÷", "(", ")"]]
first = [[1, 1, 2, 2, 2, 0], [1, 1, 2, 2, 2, 0],
[1, 1, 1, 1, 2, 0], [1, 1, 1, 1, 2, 0],
[2, 2, 2, 2, 2, 0], [2, 2, 2, 2, 2, 2]]
for i in range(6):
if operatorin == addop[0][i]:
m = i
for i in range(6):
if operatorout == addop[1][i]:
n = i
if m == -1 and n == -1:
return False
elif m == -1 and n != -1:
return False
elif m != -1 and n == -1:
return True
elif first[m][n] == 1:
return True
else:
return False
原本没有判断优先级,会导致查重不成功,准确率不高,参考了GitHub上的大神的代码之后,设置一个优先级会使查重成功。
# 判断左右子树
def maxTree(self, t1, t2):
c = Caculation.Caculation()
max = c.max(t1.value, t2.value) # 比较两个树value值大小
if max == 1:
return True
elif max == 2:
return False
elif self.priority(t1.date, t2.date): # 如果两个树的value值相等,则判定优先级
if t1.left == None or t2.left == None:
return True
max = c.max(t1.left.value, t2.left.value)
if max == 1:
return True
elif max == 2:
return False
else:
return True
return False`
**流程图
运行截图
效能分析
代码覆盖率100%
测试运行
主函数单元测试
随机算式模块的单元测试
闪光点
李颂豪总结:我认为结对编程是一个小型的小组合作过程,每个人找到自己的长处,比如此次作业,由于我编程习惯使用思维导图先对功能进行大致的描述,所以我的长处是编程逻辑,我就负责主函数逻辑部分的编写和与GUI界面的连结。
赵铭骏总结:我认为两个人合作编程还是十分高效的,一个人负责某一部分的模块,一个人负责其他剩下的模块。这对于一个比较大的程序的编写是十分有帮助的。唯一的问题就是两个人在进行模块对接的时候需要将函数名和变量名改成一样,有点麻烦。