OO_BLOG1_简单表达式求导问题总结
作业1-1 包含简单幂函数的多项式导函数的求解
I. 基于度量的程序结构分析
1)程序结构与基本度量统计图
2)分析
本人的第一次作业的程序实现逻辑十分简单,但是OOP的色彩并不强烈,程序耦合度过高。
- Homewk类: judge():输入合法性判断; process(),getTerm():输入处理; output():输出处理; main():主函数;
- Term类: division():将Term识别为二元数组; derivation():实现Term的求导;
II. 程序BUG分析
1)自身错误
本次测试,强测测试中有三个数据测试未通过,报错信息均为“Your answer has format error”。错误分析如下:
STDOUT | ANSWER |
---|---|
**-8642051***x-51x998x-40x-57x^-91 | -8642051 |
-85070591730234615875067023894796828672x-9223372036854775810*x-82 +85070591730234615911960512042215931910***x9223372036854775810***x-100x-70*x-29*x^-30 |
-85070591730234615875067023894796828672x^-9223372036854775810+ 85070591730234615911960512042215931910x^9223372036854775810 |
*第三个数据点过长,既为同质错误,故暂不列出
观察错误输出与标准输出,可以发现,程序输出时,在正确结果之后冗余了一串字符。经过分析,发现冗余的字符串来自系数为0的项,于是去检测output()函数,最终找出bug:输出判断逻辑错误——系数是否为0的判断与其他输出处理的逻辑应为包含关系,原程序错写为并列关系。
2)他人错误
过长数据爆栈
e.g. +x+x+x+x...+x {500, }
原因:输入匹配时使用一个大正则表达式暴力匹配;未用BigInteger等进行运算。
解决方法:运用拆分匹配或者字符自动机;使用大整数的处理机制。
部分非法输入无法识别
e.g. \f, \v, 空输入 (文件输入)
原因:未对这些特殊情况进行处理。
解决方法:输入时进行空输入和非法字符的检验;利用try-catch提高程序的鲁棒性。
III. 程序测试策略
1)白盒测试
- 第一次作业的代码量较小,进行白盒测试的代价相对较小,且效果更好。
- 白盒测试可以增强阅读理解代码的能力。
- 我的阅读顺序是先观察程序的Diagram,然后从main函数开始,由数据输入到输入判断,再到输入处理,最后到数据输出,一步步探索同学们的代码。
2)黑盒测试
- 相信大多数同学在自己编写程序时,或在与同学朋友们交谈的过程中,对可能出错的数据点有了一些初步的把握,在互测阶段,可以利用这些头脑风暴的结果进行测试,这种测试往往具有较强的针对性;
- 周围优秀的同学们应用各种工具编写了对拍器与评测机,在评测时应用这些可以大大节省掉重复的机械试错带来的时间消耗。
IV. 关于程序优化的思考
1)结构优化
- 将现有Term类、Homewk类拆分成Term类、Handler类和Main类:Term类包含系数、幂以及求导的方法,Handler类包含输入判断和字符串处理的方法;Main类里包含有main函数,负责将输入、处理与输出串联起来。
2)输出优化
- 当输出的结果中的项有多个,且有正有负,则正项应先输出。
作业1-2 包含简单幂函数和简单正余弦函数的导函数的求解(不支持嵌套规则)
I. 基于度量的程序结构分析
1)程序结构与基本度量统计图
2)分析
本次作业,我的基本思路是将每一项转化为 \(a·x^b·sin^c(x)·cos^d(x)\) 的标准格式,按照求导公式计算出结果。本题所用求导公式如下,
\(df/dx = a·[ b·x^{b-1}·sin^c(x)·cos^d(x)+c·x^b·sin^{c-1}(x)·cos^{d+1}(x)-d·x^b·sin^{c+1}(x)·cos^{d-1}(x)]\)
- Item类定义了“标准项”,即符合通项公式 \(a·x^b·sin^c(x)·cos^d(x)\) 的一个项,可以实现项的求导;
- Term类定义了”普通项“(它是Item类的子类),每一个项由一个或多个因子按照乘法规则构成,可以实现将Term类型的对象转化为Item类的对象的方法;
- Factor类定义了”因子“,可以实现因子的求导;
- Main类里包含有main函数,负责将输入、处理与输出串联起来。
II. 程序BUG分析
1)自身错误
本次测试,互测有三个测试点未通过,报错信息均为”Your answer has format error“。错误分析如下,
STDIN | STDOUT | ANSWER |
---|---|---|
sin( x ) | WRONG FORMAT! | cos(x) |
(space)1 | WRONG FORMAT! | 0 |
x*sin(x ) | WRONG FORMAT! | sin(x)+x*cos(x) |
观察程序输入、输出与正确答案可知,输入判断格式有问题。经过输入判断正则表达式的简单修改,即得到正确答案。
2)他人错误
部分非法输入字符无法识别
e.g. (space)+++1, \f, \v
原因:未对这些特殊情况进行处理。
解决方法:输入时进行非法字符的检验;利用try-catch提高程序的鲁棒性;正确理解实验指导书所规定的正确规范。
求导计算错误
e.g. 优化输出时将幂为0的整个项全部删去,使结果出错。
原因:优化中考虑疏忽,导致最终结果出错。
解决方法:谨慎优化,大力测试。
III. 程序测试策略
1)白盒测试
- 本次作业代码量略有提升,由于出现三角函数合并的优化,容易出现错误,检查代码时可以有所侧重。
2)黑盒测试
- 由于本次作业程序输出可能较为复杂,肉眼很难判断正误,自动化评测显得必不可少。在此鸣谢伯格提供的评测代码,大大降低了测试的复杂度。
- 自动化测试带来方便,但是也有弊端,即评测得到的错误多为同质,但由于输入输出往往较长,肉眼依旧难辨。为解决这一问题,我采取了观察+人工修改重测的方法,效率较低但是最终结果可靠。
IV. 关于程序优化的思考
1)结构优化
- 将数据输入以及数据判断与处理从Main类里剥离出来,形成一个新的InputHandler类。
2)输出优化
- 仔细阅读了何岱岚同学的优化思路后,其”化成三元组+贪心&随机合并“的策略虽未能完全领会,但依然受益匪浅。
作业1-3 包含简单幂函数和简单正余弦函数的导函数的求解(支持嵌套规则)
I. 基于度量的程序结构分析
1)程序结构与基本度量统计图
2)分析
本次作业是OO课程第一单元的重头戏。下面对本次程序进行分析。
Main类:包含有main函数,负责将输入、处理与输出串联起来;
InputHandler类:可实现输入合法性检测,输入处理;
Hander类:分析输入的表达式字符串,将分析结果储存到动态数组中,具体分析算法详见下文;
Item类:定义了上述动态数组元素的类型,包括权重(weight)和内容(core);
FormalueTree类:根据Handler形成的动态数组,用双栈法生成表达式树;
Node类:定义了树节点以及树节点的求导(derivate)以及取字符串(getValue)的方法;
FactorStd接口 + Cnt类&Cos类&Sin类&Pow类:分别实现常数因子、三角函数型因子、幂函数因子的求导;
Algorithms类:统一上述四种因子求导的实现;
StdFmt类:定义了程序所需的正则表达式;
下面是Handler类中分析表达式字符串的算法。
-
下面是程序main函数的代码,展示了程序的基本逻辑。
public static void main(String[] args) {
try {
/** 输入判断与输入处理 */
Scanner in = new Scanner(System.in);
if (!in.hasNextLine()) {
throw new RuntimeException("EMPTY INPUT!");
}
FormalueTree formalueTree = new FormalueTree();
String input = in.nextLine();
InputHandler inputHandler = new InputHandler(input);
if (!inputHandler.getIsLegal()) {
throw new RuntimeException("ILLEGAL INPUT!");
}
/** 字符串分析 */
Handler handler = new Handler(inputHandler.getInput());
ArrayList<Item> items = handler.analyze();
/** 构建表达式树 */
Node root = formalueTree.creatTree(items);
/** 求导并输出结果 */
System.out.println(root.deriv());
} catch (RuntimeException e) {
System.out.print("WRONG FORMAT!\n" + e.getMessage());
}
}
II. 程序BUG分析
1)自身错误
本次测试,强测点有4个未通过,互测点有13个未通过,数目令人触目惊心,但是仔细分析以后,发现错误点有二,现分析如下,
STDIN | STDOUT | ANSWER |
---|---|---|
(((((((((((((((((((( + 2*x )))))))))))))))))))) | WRONG FORMAT! | 2 |
sin(((x-x)*x^+1+++1)) | WRONG FORMAT! | 0 |
1*+1 | WRONG FORMAT! | 0 |
sin(x)*-2 | WRONG FORMAT! | 0 |
*表中测试点已覆盖所有错误,其余测试点暂不列出
前两个测试点暴露的错误为,拆括号的嵌套时括号检测错误:
public Boolean searchBra() {
Matcher m = StdFmt.BRACEL.matcher(poly);
if (m.lookingAt()) {
int flag = 1;
// for (int i = 2; i < poly.length(); i++) { <——— 错误所在 !!
for (int i = 1; i < poly.length(); i++) { // <——— 正确结果 !!
if (poly.charAt(i) == '(') {
flag += 1;
} else if (poly.charAt(i) == ')') {
flag -= 1;
}
if (flag == 0) {
/**
实现子串提取,递归拆除嵌套
*/
return true;
}
}
}
return false;
}
后两个测试点暴露的错误为进行”树上求导“时,递归终止条件疏漏了带符号整数这一种因子:
public String deriv() {
switch (this.data) {
case "+":
case "-":
return "(" + this.rchild.deriv() + ")" +
this.data + "(" + this.lchild.deriv() + ")";
case "*":
return "(" + this.rchild.deriv() + ")*(" +
this.lchild.getValue() + ")+(" +
this.rchild.getValue() + ")*(" +
this.lchild.deriv() + ")";
case "@":
return "(" + this.rchild.deriv() + ")*(" +
this.lchild.deriv() + ")";
default:
Matcher m = StdFmt.FACTOR.matcher(this.data);
/**
终止条件的格式判断应为
public static final Pattern FACTOR =
Pattern.compile("((x(\\^[+-]?\\d+)?)|(sin)|(cos)|([+-]?\\d+))");
错写为
public static final Pattern FACTOR =
Pattern.compile("((x(\\^[+-]?\\d+)?)|(sin)|(cos)|(\\d+))");
故当乘号(*)后含带符号整数,会抛出异常”Derivte Fault!“,程序出错。
*/
if (m.lookingAt()) {
Algorithms algorithms = new Algorithms(this.data);
return algorithms.factorDeriv();
} else {
throw new RuntimeException("Derivte Fault!!");
}
}
}
2)他人错误
某些错误输入无法识别
e.g. sin( - 1)
输出中某些因子缺少括号导致结果出错
e.g. 负整数无括号,导致运算顺序出错
多重嵌套爆栈
e.g. ((((((((((((((((((((((((((x))))))))))))))))))))))))))
原因:递归层次太深等。
解决方法:输入判断要慎重,选取恰当算法;递归优化时要注意等。
正确输入错判
e.g. sin(x)^+12, x^-2
III. 程序测试策略
- 本次作业程序结构较为复杂,反而可以通过分析,想出大量的不同方面的测试用例,在实际应用中,也颇有效用,如对表达式因子识别不够全面(尤其是首项为+1或-1的特殊情况),多层嵌套,带幂的三角函数型因子的处理和计算等等;
- 本次程序测试的输出较长,适宜用评测程序进行数据比照;
- 用评测程序评测时,出现大量的同质错误,我采取“修补+测试”的方法,提高最终测试结果的准确性。
IV. 关于程序优化的思考
1)结构优化
- 反思:本次程序基本上是面向过程的编码,大多数类的效果基本等同于一个函数,类之间的耦合度非常高。
- 首先是输入判断的优化,本次我的输入判断其实并不是在一开始就判断好的,而是在之后计算和处理的过程中,通过不断地抛出异常来降低程序错误的概率,程序鲁棒性十分之低。拟采用课程组提供的字符自动机的方法进行优化改造
- 后面的优化,没有想出来较好的方案,希望能够在学习助教的总结PPT以及优秀同学们的代码后,有所启发。
2)输出优化
改造Node类:在lchild(Node)与rchild(Node)之外增加father(Node),使父子节点双向链接。
输出处理:将原输出构建成表达式树,遍历节点,如果节点为运算符,则查询它的子节点(getValue),根据一定规则“清理树枝”,最后getValue()得到清爽输出。规则示例如下,
作业1-4 面向对象课程第一单元学习感想
I. 对自己的程序要狠一点, 对自己的程序要狠一点, 对自己的程序要狠一点;
II. 编写代码前的构思十分重要,但是如果真的毫无头绪,不妨动手小试,灵感也许会不期而至;
III. 面向对象的思维方式不是一蹴而就的,课堂之外需要多读代码,多做练习;
IV. 要提高自己新旧知识的融会贯通能力,复习与预习双轨并行;
V. 要调整好自己的心态,不要过于畏惧,更不要懈怠!
最后,衷心感谢为这门课程辛苦付出的老师和助教。
OO_BLOG1_简单表达式求导问题总结的更多相关文章
- OO Unit 1 表达式求导
OO Unit 1 表达式求导 面向对象学习小结 前言 本博主要内容目录: 基于度量来分析⾃己的程序结构 缺点反思 重构想法 关于BUG 自己程序出现过的BUG 分析⾃己发现别人程序bug所采⽤的策略 ...
- BUAA-OO-第一单元表达式求导作业总结
figure:first-child { margin-top: -20px; } #write ol, #write ul { position: relative; } img { max-wid ...
- 2019年北航OO第1单元(表达式求导)总结
2019年北航OO第1单元(表达式求导)总结 1 基于度量的程序结构分析 量化指标及分析 以下是三次作业的量化指标统计: 关于图中指标在这里简要介绍一下: ev(G):基本复杂度,用来衡量程序非结构化 ...
- 2020 OO 第一单元总结 表达式求导
title: BUAA-OO 第一单元总结 date: 2020-03-19 20:53:41 tags: OO categories: 学习 OO第一单元通过三次递进式的作业让我们实现表达式求导,在 ...
- OO_Unit1_表达式求导总结
OO_Unit1_表达式求导总结 OO的第一单元主要是围绕表达式求导这一问题布置了3个子任务,并在程序的鲁棒性与模型的复杂度上逐渐升级,从而帮助我们更好地提升面向对象的编程能力.事实也证明,通过这 ...
- 面向对象第一单元总结:Java实现表达式求导
面向对象第一单元总结:Java实现表达式求导 题目要求 输入一个表达式:包含x,x**2,sin(),cos(),等形式,对x求导并输出结果 例:\(x+x**2+-2*x**2*(sin(x**2+ ...
- oo第一次博客-三次表达式求导的总结与反思
一.问题回顾与基本设计思路 三次作业依次是多项式表达式求导,多项式.三角函数混合求导,基于三角函数和多项式的嵌套表达式求导. 第一次作业想法很简单,根据指导书,我们可以发现表达式是由各个项与项之间的运 ...
- OO_JAVA_表达式求导_单元总结
OO_JAVA_表达式求导_单元总结 这里引用个链接,是我写的另一份博客,讲的是设计层面的问题,下面主要是对自己代码的单元总结. 程序分析 (1)基于度量来分析自己的程序结构 第一次作业 程序结构大致 ...
- OO_JAVA_表达式求导
OO_JAVA_表达式求导_第一弹 ---------------------------------------------------表达式提取部分 词法分析 首先,每一个表达式内部都存在不可 ...
随机推荐
- 关于Django字段类型中 blank和null的区别
blank 设置为True时,字段可以为空.设置为False时,字段是必须填写的.字符型字段CharField和TextField是用空字符串来存储空值的. 如果为True,字段允许为空,默认不允许. ...
- cmd 创建用户,并授权管理员权限就可以远程登陆了
创建账号 net user 用户名 密码 /add //注意空格 授权管理员权限 net localgroup Administrators 用户名 /add // ...
- ABP入门系列(4)——创建应用服务
ABP入门系列目录--学习Abp框架之实操演练 一.解释下应用服务层 应用服务用于将领域(业务)逻辑暴露给展现层.展现层通过传入DTO(数据传输对象)参数来调用应用服务,而应用服务通过领域对象来执行相 ...
- emWin仪表界面设计,含uCOS-III和FreeRTOS两个版本
第7期:多功能仪表界面设计配套例子:V6-912_STemWin提高篇实验_多功能仪表界面设计(uCOS-III)V6-913_STemWin提高篇实验_多功能仪表界面设计(FreeRTOS) 例程下 ...
- SQL Server索引碎片整理实际操作记录
SQL Server 版本是 2008 R2. 查询数据库索引碎片情况的 SQL 语句(来源): SELECT OBJECT_NAME(ind.OBJECT_ID) AS TableName, ind ...
- [Swift]LeetCode609. 在系统中查找重复文件 | Find Duplicate File in System
Given a list of directory info including directory path, and all the files with contents in this dir ...
- [Swift]LeetCode837. 新21点 | New 21 Game
Alice plays the following game, loosely based on the card game "21". Alice starts with 0 p ...
- Vue中实现一个无限加载列表
参考 https://www.jianshu.com/p/0a3aebd63a14 一个需要判断的地方就是加载中再次触发滚动的时候,不要获取数据. <!DOCTYPE html> < ...
- 7.Git分支-分支简介、分支创建、分支切换
1.分支简介 几乎所有的版本控制系统都支持某种形式的分支.使用分支意味着可以把你的工作从开发主线上分离开来,以免影响开发主线.Git的分支是其必杀技,它相对于其它版本控制系统来说,具有难以置信的轻量性 ...
- PyCharm无法激活
如果你激活软件遇到问题 (Pycharm.GoLand.idea.phpstorm.webstorm.sublime.ultraEdit.win10等等) 比如: 激活框提示Key is invali ...
OO Unit 1 表达式求导 面向对象学习小结 前言 本博主要内容目录: 基于度量来分析⾃己的程序结构 缺点反思 重构想法 关于BUG 自己程序出现过的BUG 分析⾃己发现别人程序bug所采⽤的策略 ...
figure:first-child { margin-top: -20px; } #write ol, #write ul { position: relative; } img { max-wid ...
2019年北航OO第1单元(表达式求导)总结 1 基于度量的程序结构分析 量化指标及分析 以下是三次作业的量化指标统计: 关于图中指标在这里简要介绍一下: ev(G):基本复杂度,用来衡量程序非结构化 ...
title: BUAA-OO 第一单元总结 date: 2020-03-19 20:53:41 tags: OO categories: 学习 OO第一单元通过三次递进式的作业让我们实现表达式求导,在 ...
OO_Unit1_表达式求导总结 OO的第一单元主要是围绕表达式求导这一问题布置了3个子任务,并在程序的鲁棒性与模型的复杂度上逐渐升级,从而帮助我们更好地提升面向对象的编程能力.事实也证明,通过这 ...
面向对象第一单元总结:Java实现表达式求导 题目要求 输入一个表达式:包含x,x**2,sin(),cos(),等形式,对x求导并输出结果 例:\(x+x**2+-2*x**2*(sin(x**2+ ...
一.问题回顾与基本设计思路 三次作业依次是多项式表达式求导,多项式.三角函数混合求导,基于三角函数和多项式的嵌套表达式求导. 第一次作业想法很简单,根据指导书,我们可以发现表达式是由各个项与项之间的运 ...
OO_JAVA_表达式求导_单元总结 这里引用个链接,是我写的另一份博客,讲的是设计层面的问题,下面主要是对自己代码的单元总结. 程序分析 (1)基于度量来分析自己的程序结构 第一次作业 程序结构大致 ...
OO_JAVA_表达式求导_第一弹 ---------------------------------------------------表达式提取部分 词法分析 首先,每一个表达式内部都存在不可 ...
blank 设置为True时,字段可以为空.设置为False时,字段是必须填写的.字符型字段CharField和TextField是用空字符串来存储空值的. 如果为True,字段允许为空,默认不允许. ...
创建账号 net user 用户名 密码 /add //注意空格 授权管理员权限 net localgroup Administrators 用户名 /add // ...
ABP入门系列目录--学习Abp框架之实操演练 一.解释下应用服务层 应用服务用于将领域(业务)逻辑暴露给展现层.展现层通过传入DTO(数据传输对象)参数来调用应用服务,而应用服务通过领域对象来执行相 ...
第7期:多功能仪表界面设计配套例子:V6-912_STemWin提高篇实验_多功能仪表界面设计(uCOS-III)V6-913_STemWin提高篇实验_多功能仪表界面设计(FreeRTOS) 例程下 ...
SQL Server 版本是 2008 R2. 查询数据库索引碎片情况的 SQL 语句(来源): SELECT OBJECT_NAME(ind.OBJECT_ID) AS TableName, ind ...
Given a list of directory info including directory path, and all the files with contents in this dir ...
Alice plays the following game, loosely based on the card game "21". Alice starts with 0 p ...
参考 https://www.jianshu.com/p/0a3aebd63a14 一个需要判断的地方就是加载中再次触发滚动的时候,不要获取数据. <!DOCTYPE html> < ...
1.分支简介 几乎所有的版本控制系统都支持某种形式的分支.使用分支意味着可以把你的工作从开发主线上分离开来,以免影响开发主线.Git的分支是其必杀技,它相对于其它版本控制系统来说,具有难以置信的轻量性 ...
如果你激活软件遇到问题 (Pycharm.GoLand.idea.phpstorm.webstorm.sublime.ultraEdit.win10等等) 比如: 激活框提示Key is invali ...