301
社区成员
发帖
与我相关
我的任务
分享第一次作业要求实现表达式的化简。主要采用递归下降法对表达式进行解析,并将表达式转化为多项式之后进行化简。表达式(Expr)由项(Term)相加组成,项由因子(Factor)相乘组成,因子分为三类,分别为表达式类(ExprFactor)、幂函数类(PowFactor)以及常数类(NumFactor)。
首先,构造词法解析器(Lexer),分析读取到的内容属于什么类型。关于类型,我单独设计了一个Token类,储存字符对应的类型和内容。并在句法解析器(Parser)内采用递归下降法把表达式拆分。
表达式解析完毕后,会得到相应的Expr,Term,Factor内容。化简的方式是将其转化为多项式(Poly),并进行多项式的加法和乘法运算。多项式由单项式(Mono)组成。其中,常数因子和幂函数因子可以直接转化为单项式,即a*x^n的形式,可以看作只有一项的多项式,对多项式进行运算即可。
1.其中单项式系数可能会大于Integer类型,因此在设定时要将系数类型定为BigInteger,且创建一个新Num类型时要采用BigInteger num = new Num ();而不是thevalueof(会超时)
2.进行多项式乘法时,可以在初始时加入一个单项“1”

优点:设计Factor接口,将不同类型的因子分类,方便之后的迭代和修改;
缺点:在递归解析时只设计了parserFactor()解析,有些解析的过程重复了,且方法长度有点长;
没有单独设计合并方法,在乘法的过程中直接实现合并,一是方法复杂度较高,二是不便于日后的迭代。




要求在第一次作业的基础上,新增了指数因子(ExpFactor)和函数因子(FunFactor)。
1.指数因子的处理:
我的处理方式是在单项式类中新增了一个HashMap<Factor,Integer>储存指数因子。等于可以将一个单项式视作 a*x^n*exp(Factor)(此处可以是一堆指数的乘积,为了方便在这里没有化简)。
在进行多项式的加法和乘法时,也要对相应内容进行修改。
2.函数因子的处理
分成两个部分,一个是将函数的表达式读入(addFunc),另一部分是讲形参替换成实参(callFunc),最后将表达式看作与作业一中相同的来进行处理。
表达式的读入的话,新增一个工具类(Definer),对表达式进行处理。在类中有两个map分别负责储存函数的定义式以及形参。
在解析表达式时,读入函数的实参,在工具类中将形参替换成实参,最终返回表达式。
3.在第一次作业基础上做出的其他改进:
由于因子类型的增多,容易导致ParserFactor()方法内容较长,且出现很多重复内容(例如解析exp()括号中内容时实际上就是再解析一遍表达式因子),因此将Factor分类解析,缩短了方法长度,也方便修改。

在幂函数不断相乘的过程中,最终多项式中单项式的指数大小可能会超出Integer范围,因此,要将其改为BigInteger。

优点:直接在单项式内添加了exp()内容,可以直接在上次的基础上修改。
缺点:没有设计合并方法,输出性能较差。




在第二次作业的基础上新增了导数因子(DxFactor)。我采用的处理方式是对每种类型分别进行求导。
先对每种Factor求导,再依次对Term求导(链式法则),最后再对Expr求导。
项的求导:
由于项是由不同因子相乘得到,因此在对项求导时,先提出第一个,将后面的所有当作一个整体,对两项进行求导,然后再将后面的项依次展开求导。
1.求导过程中的符号。我设计的是Term中有符号,而由于对Term求导会得到表达式,因此需要修改表达式中每个项的符号。
2.二次求导:我在求导因子中也加入了derive()方法(求导),对第一次求导返回的表达式再求导。

优点:在每一个类中分别添加了derive()方法,方便对每一类的求导方法修改完善。
缺点:方法复杂度较高,特别是二次求导的时候以及项中相乘因子较多时。





刚开始完成作业的时候,由于太久没有接触过相应的内容,感觉任务量还挺大的。在逐步迭代和完善的过程中,也复习了相关的知识,掌握了类的基本构造方法,熟悉了一些基本的语法知识。几次作业下来,感觉难度最大的部分还是实现表达式的化简和架构的优化。虽然最终也完成了任务,但自己的方法相对来说还是比较粗糙,包括在实现多项式相乘的时候,效率较低,指数因子还有部分没有实现合并等等,可能在性能上还需要有改进。也希望自己能够踏踏实实地完成任务,能够在今后取得进步。