作业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+
85070591730234615911960512042215931910
x^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_简单表达式求导问题总结的更多相关文章

  1. OO Unit 1 表达式求导

    OO Unit 1 表达式求导 面向对象学习小结 前言 本博主要内容目录: 基于度量来分析⾃己的程序结构 缺点反思 重构想法 关于BUG 自己程序出现过的BUG 分析⾃己发现别人程序bug所采⽤的策略 ...

  2. BUAA-OO-第一单元表达式求导作业总结

    figure:first-child { margin-top: -20px; } #write ol, #write ul { position: relative; } img { max-wid ...

  3. 2019年北航OO第1单元(表达式求导)总结

    2019年北航OO第1单元(表达式求导)总结 1 基于度量的程序结构分析 量化指标及分析 以下是三次作业的量化指标统计: 关于图中指标在这里简要介绍一下: ev(G):基本复杂度,用来衡量程序非结构化 ...

  4. 2020 OO 第一单元总结 表达式求导

    title: BUAA-OO 第一单元总结 date: 2020-03-19 20:53:41 tags: OO categories: 学习 OO第一单元通过三次递进式的作业让我们实现表达式求导,在 ...

  5. OO_Unit1_表达式求导总结

    OO_Unit1_表达式求导总结   OO的第一单元主要是围绕表达式求导这一问题布置了3个子任务,并在程序的鲁棒性与模型的复杂度上逐渐升级,从而帮助我们更好地提升面向对象的编程能力.事实也证明,通过这 ...

  6. 面向对象第一单元总结:Java实现表达式求导

    面向对象第一单元总结:Java实现表达式求导 题目要求 输入一个表达式:包含x,x**2,sin(),cos(),等形式,对x求导并输出结果 例:\(x+x**2+-2*x**2*(sin(x**2+ ...

  7. oo第一次博客-三次表达式求导的总结与反思

    一.问题回顾与基本设计思路 三次作业依次是多项式表达式求导,多项式.三角函数混合求导,基于三角函数和多项式的嵌套表达式求导. 第一次作业想法很简单,根据指导书,我们可以发现表达式是由各个项与项之间的运 ...

  8. OO_JAVA_表达式求导_单元总结

    OO_JAVA_表达式求导_单元总结 这里引用个链接,是我写的另一份博客,讲的是设计层面的问题,下面主要是对自己代码的单元总结. 程序分析 (1)基于度量来分析自己的程序结构 第一次作业 程序结构大致 ...

  9. OO_JAVA_表达式求导

    OO_JAVA_表达式求导_第一弹 ---------------------------------------------------表达式提取部分 词法分析 ​ 首先,每一个表达式内部都存在不可 ...

随机推荐

  1. 关于Django字段类型中 blank和null的区别

    blank 设置为True时,字段可以为空.设置为False时,字段是必须填写的.字符型字段CharField和TextField是用空字符串来存储空值的. 如果为True,字段允许为空,默认不允许. ...

  2. cmd 创建用户,并授权管理员权限就可以远程登陆了

    创建账号 net user 用户名 密码 /add     //注意空格 授权管理员权限 net localgroup Administrators 用户名 /add              // ...

  3. ABP入门系列(4)——创建应用服务

    ABP入门系列目录--学习Abp框架之实操演练 一.解释下应用服务层 应用服务用于将领域(业务)逻辑暴露给展现层.展现层通过传入DTO(数据传输对象)参数来调用应用服务,而应用服务通过领域对象来执行相 ...

  4. emWin仪表界面设计,含uCOS-III和FreeRTOS两个版本

    第7期:多功能仪表界面设计配套例子:V6-912_STemWin提高篇实验_多功能仪表界面设计(uCOS-III)V6-913_STemWin提高篇实验_多功能仪表界面设计(FreeRTOS) 例程下 ...

  5. SQL Server索引碎片整理实际操作记录

    SQL Server 版本是 2008 R2. 查询数据库索引碎片情况的 SQL 语句(来源): SELECT OBJECT_NAME(ind.OBJECT_ID) AS TableName, ind ...

  6. [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 ...

  7. [Swift]LeetCode837. 新21点 | New 21 Game

    Alice plays the following game, loosely based on the card game "21". Alice starts with 0 p ...

  8. Vue中实现一个无限加载列表

    参考 https://www.jianshu.com/p/0a3aebd63a14 一个需要判断的地方就是加载中再次触发滚动的时候,不要获取数据. <!DOCTYPE html> < ...

  9. 7.Git分支-分支简介、分支创建、分支切换

    1.分支简介 几乎所有的版本控制系统都支持某种形式的分支.使用分支意味着可以把你的工作从开发主线上分离开来,以免影响开发主线.Git的分支是其必杀技,它相对于其它版本控制系统来说,具有难以置信的轻量性 ...

  10. PyCharm无法激活

    如果你激活软件遇到问题 (Pycharm.GoLand.idea.phpstorm.webstorm.sublime.ultraEdit.win10等等) 比如: 激活框提示Key is invali ...