OO博客作业1:多项式求导作业总结
第1次作业
要求:
简单多项式导函数的求解,仅包含幂函数和常数
- 带符号整数
- 幂函数
- 项
- 变量项
- 常数项
- 表达式 由加法和减法运算符连接若干项组成
- 空白字符
此外,值得注意的几点是:
- 带符号整数内不允许包含空白字符。
- 幂函数、项、表达式,在不与上一条矛盾的前提下,可以在任意位置包含任意数量的>空白字符。
- 如果某表达式存在不同的解释方式,则只要有任意一条解释中是合法的,该表达式即>为合法。
思路分析:
第一次作业相对来说比较简单,我是先用一个大的正则表达式匹配整个表达式的正确性,然后在逐步提取每一项的内容。
我用了两个arraylist存储每一项的a和b,及每一项都可以写成a*x^b的形式
然后直接运用求导公式便可以得到结果
随后再考虑一些简单的同类项合并的情况,即可得到最简单的求导结果了
类图:
由于没能摆脱学习c语言时面向过程的思维,我在第一次作业中将所有函数均放在了main函数中。这也是因为我在假期预习的工作做得不够成分,今后将引以为戒,锻炼相应的能力。
我在一个主类的一个方法中,完成了对表达式形式合法性的判断+对表达式的核心内容的提取+对表达式求导+将求导的结果输出出来
因为第一次作业还相对比较简单的缘故,我这样子勉强还能够完成任务,但是日后工作量提升之后,我必须改掉这种错误的变成习惯,养成面向对象的思维。
度量分析:
Bug:
1.使用了\s,(匹配任何空白字符,包括空格、制表符、换页符等。与 [ f v] 等效。)
但题目要求:空白字符在本次作业中,空白字符包含且仅包含`<space>`和` `
所以当夹杂着f等的时候,应当输出WRONG FORMAT!,但是我的程序会默认他们是正确的空白字符,从而输出值、
改法:将正则表达式中所有的 \s 改为 [ ]
2.在*后加+,将正则表达式的匹配模式由贪婪模式改为独占模式,防止被爆栈
第2次作业
要求:
第二次作业基本上是在第一次作业的基础上进行的,增加了三角函数的处理,以及一些额外的格式规定。
- 三角函数 sin(x)或cos(x)(在本次作业中,括号内仅为x)
- 一般形式 类似于幂函数,由sin(x)和指数组成,指数为一个带符号整数,如:sin(x) ^ +2。
- 省略形式 当指数为1的时候,可以采用省略形式,省略指数部分,如:sin(x)。
- 常数因子 包含一个带符号整数,如:233。
- 项
- 一般形式 由乘法运算符连接若干因子组成,如:2 * x ^ 2 * 3 * x ^ -2、sin(x) * cos(x) * x。
此外,值得注意的几点是:
- 三角函数的保留字内不允许包含空白字符,即sin和cos内不可以含有空白字符。
- 未知数包含且仅包含小写的x。
思路分析:
第2次作业整体上和第1次作业的思路类似,因为每一项最终都可以写成:index*x^a*sin(x)^b*cos(x)^c的形式,所以我们只需要提取出多项式中的每一项对应的4个关键系数即可,也即:index,a,b,c,然后运用经验求导公式即可得到答案
类图:
在主类中构建一个poly多项式类的实例,然后这个多项式又是由很多个polyitem类的实例而组成的,也即表达式有很多个项组成。
规模分析:
Bug:
1.
求导函数public ArrayList<Polyitem> diff() 中,在循环中增加如下语句:
if (list.get(i).coef.toString().equals("0")) {
continue;
}
在最后增加如下语句:
if (templist.size() == 0) {
return null;
} else {
return templist;
}
在输出函数public void printResult(ArrayList<Polyitem> templist)的开头,新增如下语句:
if (templist == null) {
System.out.println("0");
System.exit(0);
}
原因:
防止出现1000、sin(x)-sin(x) 这样的输入(他们会在构造函数中被存储到list中)
在求导函数中,不会对这些数据进行实际的运算,所以全程对diff中的templist没有改动,templist.size保持为0,此时把return一个null给输出函数
这样的null list传入到输出函数,要对其进行判断,若满足条件,直接输出0即可
2.
在
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
中间增加语句:
if(!sc.hasNextLine()){
System.out.println("WRONG FORMAT!");
System.exit(0);
}
即主函数中如图所示:
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
if(!sc.hasNextLine()){
System.out.println("WRONG FORMAT!");
System.exit(0);
}
String str = sc.nextLine();
Poly p1 = new Poly();
p1.checkFormat(str);
p1= new Poly(str);
p1.printResult(p1.diff());
}
这是为了防止空输入,程序没有相应,报异常
相应题目要求:空串不属于合法的表达式(空输入)
第3次作业:
要求:
在第二次作业的基础上加入了因子嵌套,其余规则保持不变。
- 表达式因子 将在表达式的相关设定中进行详细介绍。不过,表达式因子不支持幂运算。
- 嵌套因子 本次作业将支持因子嵌套在三角函数因子里面,即一个因子作为另一个三角函数因子的自变量,例如sin(x^2),cos(sin(x))以及sin(sin(cos(cos(x^2))))^2等。但是不允许出现指数为变量的情况,指数依然只能是带符号整数,例如sin(x) ^ sin(x)是不合法的,因为指数不是自变量。也不允许幂函数的自变量为除了x之外的因子,例如1926^0817是不合法的,因为幂函数的自变量只能为x。
- 项
- 一般形式 由乘法运算符连接若干任意因子组成,如:x * cos(x) * x,sin(x
^ 2) * cos(sin(x)) ^ 2 * x ^ -2等。
- 项内因子不仅仅是同类因子
- 一般形式 由乘法运算符连接若干任意因子组成,如:x * cos(x) * x,sin(x
^ 2) * cos(sin(x)) ^ 2 * x ^ -2等。
- 表达式 由加法和减法运算符等若干项组成,如: (-1
+ x ^ 233)* sin(x^2) ^ 06 - cos(sin(x)) * 3 * sin((x))。此外,在第一项之前,可以带一个正号或者负> 号,如:- -1
+ x ^ 233、+
-2 + x ^ 1926。此处有几点注意:
- 表达式因子,表达式可以作为因子,其定义为被一对小括号包裹起来的表达式,即我们允许诸如(x * cos(x)) 这种式子的出现
- 空串不属于合法的表达式
- 空白字符 在本次作业中,空白字符包含且仅包含<space>和 。其他的除了上述会用到的字符之外,均属于非法字符。
此外,值得注意的几点是:
- 表达式因子可以递归嵌套,例如sin( ( x^2 + sin((1 + 3*x)) ) )
- 为了方便评测机评测,我们限制的指数的绝对值不超过$10^4$,超过此范围需要输出 WRONG FORMAT!
- (重大更新) 对于类似sin((x-x))^-1的输入,会存在除0的情况,对于评测是非常难处理的,故对指导书进行补充。为了保证向下兼容,我们限制所有的指数必须严格大于0,这一条作为基本限制,任何测试数据不得违背该限制,不需要对此输出WRONG FORMAT!。此外,我们将0^0一概定义为1(即c标准库和python中的定义)
思路分析:
这一次的作业不能够像上两次作业一样进行整个表达式的正则表达式的书写,不能够直接判断表达式的格式合法性。所以要进行逐个逐个因子的提取和计算与存储,这就有许多细节要注意,不如说正负号的数量与存储问题。
类图:
其中在求导的时候,在qd函数中会递归调用其自身
度量分析:
Bug:
1.sin 越界
2.-x -(x) -sin(x) -sin(x*2) 忘了给基本情况输出时加正负号了
3.sin(+x) sin(-x) 判断是否为一个因子的时候 会认为这是一个因子 所以增加判断: 在首位出现了正负号 那么就是错的
4.sin((2*x)) 存的时候 会存成 sin(x)fg2*x,求导的时候又会求sin(2x)的导数,改动方法:求导的时,改求sin((2*x))的导数
总结与收获:
在假期没有好好体会面向对象思想的精髓,所以这三次作业也只能算是勉勉强强,在时间的同时也体会到了好的代码风格的重要性,以及面向对象的思维在解决问题时的方便性。
同时代码中每一个类应该满足以下原则:
单一责任原则:就一个类而言,应该只专注于做一件事和仅有一个引起它变化的原因。所谓职责,我们可以理解为功能,就是设计的这个类功能应该只有一个,而不是两个或更多。也可以理解为引用变化的原因,当你发现有两个变化会要求我们修改这个类,那么你就要考虑拆分这个类了。
低耦合原则:耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。代码的任何一个部分应该减少对其他区域代码的依赖关系。尽量不要使用共享参数。低耦合往往是完美结构系统和优秀设计的标志。
隐藏实现细节:把必要的展示给对方,而把隐私的部分隐藏起来,当其他功能部分发生变化时,能够尽可能降低对其他组件的影响。java通过访问权限修饰符来达到这个目的,有时也用内部类来隐藏实现细节。
迪米特法则:迪米特法则又叫做最少知识原则(Least Knowledge Principle或简写为LKP),就是说,一个对象应当对其他对象有尽可能少的了解,对象与对象之间应使用尽可能少的方法来关联,避免千丝万缕的关系。
避免过早优化:很多人写代码的时候,一下手就拼命往性能优化的方向进行代码设计,往往容易失去对总体性能指标的把握,在不适宜的地点和时间进行不必要的工作,而忽略了可读性、移植性、安全性、内聚性等等真正需要下大力气的地方。如果过早优化,也会限制各模块或是结构的功能,不利于后期扩展。
代码重用原则:重用代码能提高代码的可读性,缩短开发时间。在设计开始之前就应该思考哪些代码可以进行重用,在开发过程中也要将代码编写的可以重用。
关注点分离:如果一个问题能分解为独立且较小的问题,就是相对较易解决的。问题太过于复杂,要解决问题需要关注的点太多,而程序员的能力是有限的,不能同时关注于问题的各个方面。
拥抱改变:积极面对变化,因为变化是永恒的,为了能够更好地接受改变而不是抱怨,银锭要让代码易于重构和扩展。