BUAA_OO第一单元总结性博客作业——表达式

一、程序设计思路  

  在我的三次作业中都采用了类的分层结构,采用逐项匹配,分层求导的思路。

  (一)、

  第一次作业中构建了Polynimial(多项式)类,在类的构造器中就完成了对非法空格的判断并对合法表达式进行删除空格处理。由于第一次作业仅含有带有系数的幂函数与常数项,因而我就没有专门构建针对每一个项的类,而是在本类中就定义了getitem方法,用正则表达式逐项匹配出符合要求的项。在第一次作业中我求导的基本单位为项,在构造正则表达式时我对表达式中可能出现项的类型进行枚举,分别为:(1)系数与指数均有的幂函数,(2)仅有系数的幂函数,(3)仅有指数的幂函数,(4)无指数,无系数的幂函数(x),(5)仅为常数;每一次调用getitem方法时,从表达式开头按顺序寻找符合要求的项,将它抠出来并分别进行求导操作,逐项输出返回至main函数所在类。并在main函数所在类中完成合并同类项、化简输出的操作。

  (二)、

       第二次作业较第一次作业增加了三角函数因子,并且改变了项中因子的构造。在第一次作业中可以视为幂函数的系数的常数因子,在第二次作业中必须作为一个单独的因子来处理。比如在第一次作业中仅会出现3*x^2类型的项,这时其中的系数“3”可以看作是3*x^2幂函数的一部分,与幂函数合并处理,而在第二次作业中可能会出现3*3*x^2这种类型的项,这时若将第一个”3“当作常数因子来处理,将第二个”3“仍然当作幂函数的系数来处理,整个类的构造就显得拖沓、臃肿,因此我将这个项中的两个”3“均当作一个常数因子来处理,即为取消了第一次作业中的带有系数的幂函数因子,在第二次作业中的幂函数仅有x与x^n两种。为了简化第二次作业的设计,在第二次作业中我设计的求导方法的基本单位仍然为项,这也为第三次作业埋下了先天缺陷。同样地,在第二次作业中,我也采用了逐项匹配的思路。我构造了一个针对每一项的正则表达式:

1  String itemregex =
2      "([-+]?)" + "(" +                          // 正负号 用于连接item
3      "(\\*(sin|cos)\\(x\\)\\^[-+]?\\d+)|" +     // 因子5  有指数的正弦函数
4      "(\\*(sin|cos)\\(x\\))|" +                 // 因子4  无指数的正弦函数
5      "(\\*x\\^[-+]?\\d+)|" +                    // 因子3  有指数的幂函                       
6      "(\\*x)|" +                                // 因子2  无指数的幂函数                
7      "(\\*[-+]?\\d+)" +                         // 因子1  带符号整数
8      ")+";

可以看出,每一项均由以”*“为开头的5种因子构成,但是在每一项的开头那个因子,比如3*3*x^2这个项中的第一个带符号整数因子”3'的前面并没有“*”,这种情况怎么将第一个因子匹配进去呢?

  在这里面我想说明我处理开头因子时的一个小trick。根据指导书上的文法介绍,每一项的开头可能有1个、2个、3个连续的正负号。当开头有三个连续符号时,紧接着的因子必须是常数。在Polynomial类中我设计了一个modifybegin的方法,第一步,当表达式开头无符号,则添上一个正号;当表达式开头有三个符号时,将前两个符号等价处理为一个符号;当表达式开头有两个符号时,若第三个字符是数字,则不处理,若第三个字符不为数字,则将前两个符号合并;当表达式开头只有一个符号,不处理。第二步,在表达式的第一个符号后面塞进去一个”*“。这样,若一个含有两个因子表达式为“+++1*+1,++x*+1,++1*+1,+1*+1,”,第一步处理完为“++1*+1,+x*+1,++1*+1,+1*+1”,第二步处理完为“+*+1*+1,+*x*+1,+*+1*+1,+*1*+1”。我们可以把处理表达式理解为,第一个乘号前面的正负号为连接符,第一个乘号后面的正负号为带符号整数的符号。可以看出,经过处理的表达式,“*”号的个数与因子个数相等,即处理了文法,避免了形似“+++1”类型项的特判,又方便了每个因子的匹配,避免了为第一个因子专门书写正则表达式的繁琐。这个方法我沿用到了第三次作业中。

  匹配到一个项后,就进入Item类中进行求导运算,Item类以a*x^b*sin(x)^c*cos(x)^d形式来存储每一个项,并进行求导运算,返回一个Arraylist<Biginteger[]>类型的二维数组,存储求导后的每一项以及每一项中的abcd值,再以bcd的合并作为key在HashMap中存储每一项,同时进行合并同类项操作,最后转化为String类型输出结果。

  (三)

  第三次作业较第二次作业增加了嵌套因子与表达式因子,虽说增添的因子的类型不多,但这也意味着不能用前两次作业那种投机取巧方法来处理多项式了,于是我在继承前两次作业的Polynomial类与Item类的基础上,花了大量时间在构造因子类上orz。   

  我构造了一个父类Factor,包含一个String类型的值factor与一个基本的方法getFactors(),一共7个子类全部继承自Factor,除常数与符号类外其他类都各自定义了求导方法,两个含有嵌套表达式的类还定义了自己的取值方法getvalue()。

  

Fuhao 存储表达式开头的符号
Const 存储常数因子
PowerF 存储表达式因子
TrigF 存储三角函数因子
TrigNest 存储三角函数嵌套因子的函数名与嵌套的表达式
ExpressionF 存储表达式因子嵌套的表达式
Term 存储嵌套表达式中的每一个项

  同样地,第三次作业我还是采用逐项匹配的思路,只是在第三次作业中我采用了逐因子匹配,在getitem方法中,先一个因子一个因子地匹配,匹配完一个项,返回一个名为itemfactor的Arraylist<Factor>类型数组;若匹配到了嵌套因子开头或表达式因子开头,则重复调用getitem方法,在嵌套因子中继续匹配表达式,直到遇到“)”返回,将返回的Arraylist<Factor>数组储存到ExpressionF类或TrigNest类中的contains数组,再将ExpressionF类或TrigNest类储存到itemfactor中,itemfactor即为这一项包含的所有因子内容,进入Itemdao类求导运算,再逐因子匹配下一个项,直到表达式完成。

        ArrayList<Factor> itemfactor = new ArrayList<>();
        String trigfuncregex = "\\*(sin|cos)\\(x\\)(\\^[-+]?\\d+)?"; //三角函数因子
        String powerfuncregex = "\\*x(\\^[-+]?\\d+)?";               //幂函数因子
        String constantregex = "\\*[-+]?\\d+";                       //带符号整数因子
        String trigfuncnestregex = "\\*(sin|cos)(\\()";              //带嵌套因子的三角函数开头
        String expressionbeginregex = "\\*(\\()";                    //表达式因子开头

   在Item类中,逐项遍历Arraylist<Factor>中的每一个Factor,调用这个Factor的求导方法与其他所有Factor的getvalue()方法,将返回值用“*”连在一起并输出,就是针对当前项的求导结果,若遇到的是一个嵌套因子,则补上左右括号并在内部递归求导,最后返回String类型的求导结果至Polynimial类中,再由Polynomial类返回至main函数输出。

  在对输出的优化处理中,我将输出项含有“0”因子的项直接丢弃,具体实现方法是在求导过程中,若求导后输出结果为0(一般出现在常数求导中),则直接中断这个for循环,不将针对这一项的求导结果合并到result字符串中,这样可以避免后期处理过程中可能出现的判断错误。

二、程序结构分析

  

猜你喜欢

转载自www.cnblogs.com/hkywwr/p/10583858.html
今日推荐