关于第一次作业表达式求导总结

基于度量的程序结构分析

本分析使用的是基于IDEA上的的插件Metrics Reloaded

对于其分析项目的含义做一个简单解释

  1. ev(G):即Essentail Complexity,用来表示一个方法的结构化程度,范围在([1,v(G)])之间,值越大则程序的结构越“病态”,其计算过程和图的“缩点”有关。

  2. iv(G):即Design Complexity,用来表示一个方法和他所调用的其他方法的紧密程度,范围也在([1,v(G)])之间,值越大联系越紧密。

  3. v(G):即循环复杂度,可以理解为穷尽程序流程每一条路径所需要的试验次数。

    对于类,有OCavgWMC两个项目,分别代表类的方法的平均循环复杂度和总循环复杂度。

作业一

类,方法的结构:

度量:

derivation.DriMult.main(String[]) 1.0 2.0 2.0
derivation.Mult.ismatch(Matcher) 6.0 1.0 6.0
derivation.Mult.loadMult(String) 6.0 9.0 16.0
derivation.Mult.mergeadd(String,String) 3.0 3.0 3.0
derivation.Mult.show(ArrayList) 3.0 6.0 8.0
derivation.Unit.show() 2.0 8.0 13.0
Average 1.9375 2.5625 3.75

第一次作业并不困难,由于刚开始接触java和面向对象并且对正则的使用不熟,在字符匹配这一部分使用了面向过程的思想,导致效率低下。

在这次作业中,我第一次尝试用了两个对象,分为单项式和多项式,在多项式中用Arraylist管理。整个程序比较简单,但是扩展十分困难。

作业二

类,方法的结构:

![image-20190322172753774]

度量:

work.Main.main(String[]) 1.0 1.0 1.0
work.Ploy.addmap(Unit) 4.0 3.0 4.0
work.Ploy.ismatch(String) 3.0 3.0 6.0
work.Ploy.Ploy(Scanner) 1.0 5.0 6.0
work.Ploy.tostring() 3.0 5.0 6.0
work.Unit.tostring() 2.0 14.0 14.0
work.Unit.Unit(String) 1.0 10.0 10.0
Average 1.5 3.0 3.4444444444444446

我的作业二继承了作业一的思维,并没有对象化到因子级别,而是在上一个单项式的类中增加了sin(x)和cos(x)两个项的次数。这样的好处在与层次简单,并且求导使只需要考虑固定的三种情况。

缺点是扩展性不够。并且由于单项式这个类比较复杂,构造的添加类方法的复杂度高。同时我当时的构造函数是输入字符串,所以我的构造函数也十分复杂。

作业三

类,方法的结构:

度量:

ev iv v
my.Cos.Cos(String) 1.0 9.0 10.0
my.Mult.init(String) 1.0 9.0 11.0
my.Sin.Sin(String) 1.0 9.0 10.0
my.Unit.add(MultPattern) 7.0 11.0 11.0
my.Unit.init(String) 3.0 8.0 18.0
Average 1.5138888888888888 2.111111111111111 2.7222222222222223

可以看出复杂度最高的函数为

my.Cos.Cos(String)(sin)
my.Mult.init(String) (Unit)
my.Unit.add(MultPattern)

这是因为我采用的是字符串输入构造器,导致构造器的设计比较复杂。

bug分析

作业1:

完全通过了强侧,但在互测过程中被发现无法检测fu的bug,正常情况下遇到这类不合规字符应该输出WF,但是我的正则表达式中的s会吸收这类字符。

作业2:

不能处理符号开头的项,如“-cos(X)”。这个bug导致我在强侧中被扣了很多分。原因是我是先判断合法之后就去匹配项中的sin,cos等元素,但在匹配替换完后忘记了剩下的“-”。

由于指导书中写了三角函数的保留字不能分割。我错误的理解为三角函数的保留子是sin、cos、(x),对于sin( x)就会判断为WF。

作业3:

由于我解析的表达式的思路是将裁切过的字符串输入到构造函数中的。这就需要判断一个字符串代表的是什么格式的表达式。而我在三角函数内部判断数字常量因子时直接判断字符串是否是数字。这就导致我的程序无法识别“sin(-1)”这样的输入。

我对于表达的处理一直是先判断合法性,再去掉空格和连续正负符号。然而我在处理连续正负符号时仅仅考虑了把两个符号化成一个,但是事实上会出现“-+-1*x”这样的情况,在我的程序中就会被处理为“- - 1”而不是我期待的“+1”。导致我之后的处理出错。

程序设计的问题

由于第三次代码量大且较复杂所以主要还是分析第三次作业。

  1. 没有采取使用模型化的解析策略,而是靠对括号进行切分。导致这部分代码极其复杂,扩展性低且易于出错。
  2. 直接使用构造函数来解析字符串。导致代码复杂,维护性低。
  3. 没有完全分离开静态数据和动态数据,导致使用了过多的clone,公测时出现了超时。

发现别人bug的策略

由于自动生成并测试bug的方法很多大佬都介绍了,这里主要分享手动找bug的心得

  1. 特别生僻的点,如sin(-1),我在提交之前自己测试了很多点,但并没有想到这个情况。最后也在这里出了bug。
  2. 简单的容易被忽略的旧点,我第二次作业的bug在输入x-x时就会错误,但是当时并没有检查到这个问题。后来我把这个测试点测试同组的玩家,发现了不少惊喜。

Applying Creational Pattern

由于我把一个乘积的单项式中的每个因子都放在一个Arraylist中,不得不引入一个全新的统一的类。这个时候我就想到了使用接口。

package my;

import java.math.BigInteger;
import java.util.ArrayList;

public interface MultPattern {

    public ArrayList<Unit> units = new ArrayList<>();

    public Unit derive();

    public String tostring();

    public boolean equals(MultPattern mp);

    public BigInteger getValue();

    public BigInteger getIndex();

    public void addIndex(BigInteger b);

    public void multValue(BigInteger newvalue);

    public MultPattern clone();

    public MultPattern getMp();
}

我把因子包括嵌套因子都implements这个接口。于是就可以统一的管理这些不同的类,即在不知道类别的情况下调用其方法。

认识感想

在这三次作业中我最大的收获是感受到了面向对象和面向过程中巨大的思维差别。个人感觉面向过程更需要编程者抽象化具体问题,比如c语言中会用到很多指针。这些部分需要较高的构造成本,且难以扩展。而面向对象更倾向于关注问题本身的样子,编程者无需高度抽象其算法,只需要对不同对象关系层次有一个清晰的认识。

原文地址:https://www.cnblogs.com/buaa17373407hjh/p/10604827.html