301
社区成员
发帖
与我相关
我的任务
分享
利用正则表达式匹配空白符,利用replaceAll()方法删除。
可以利用若干个连续+-分割表达式成若干个子字符串,判断连续正负号的最终性质为+还是为-,分别用单个正负号将子字符串连接在一起。
通过Hashmap,储存函数名与函数表达式的对应,储存函数名和形参的键值对。
private static HashMap<String, String> nameToFunc = new HashMap<>();
private static HashMap<String, ArrayList<String>> nameToPara = new HashMap<>();
public enum Type {
ADD, SUB, MUL, LPAREN, RPAREN, NUM, POW, X, EXP,F,G,H,CM,DX
}
分析表达式包含的所有基本类型。
private ArrayList<Token> tokens = new ArrayList<Token>();
public Lexer(String input) {
for (int pos = 0; pos < input.length();) {
if (input.charAt(pos) == '(' || input.charAt(pos) == ')') {
lexerParen(input.charAt(pos));
pos++;
} else if (input.charAt(pos) == '+' || input.charAt(pos) == '-') {
lexerOp(input.charAt(pos));
pos++;
...
}
}
用于收集表达式的所有信息,便于下一步分析。
1.1.3.1分析总体思路
使用递归下降法,parserExpr()分析表达式,保存项之间的符号并对每一项使用方法parserTerm(),分析项中的每一个因子parserFactor()。
| parserExpr() | 可以调用parserTerm() |
| parserTerm() | 可以调用parserFactor() |
| parserFactor() | 分析幂函数因子、数字因子、自定义函数因子、求导因子、表达式因子,返回值为Factor类,其中可以递归调用parserExpr() |
1.1.3.2替换自定义函数(hw2)
在parser过程中,通过调用parserFuncFctor(),传入实参,替换形参,返回一个字符串,再通过调用parserExpr(),返回表达式因子,这样就完成了自定义函数的替换。
1.1.3.3求导(hw3)
在parser过程中,通过parserDxFactor(),继续parserExpr(),得到一个表达式,再通过多项式求导方法得到一个Poly,再通过Poly类的toExpr(),这样就有了求导后的表达式因子。
private BigInteger exp;
private BigInteger coef;
private Poly expPoly = new Poly();
private int expPow = 0;
private boolean containExp = false;
是多项式的基本单位:
包含toString()方法。
private ArrayList<Mono> monoArrayList = new ArrayList<Mono>();
public String toString() {
StringBuilder sb = new StringBuilder();
if (this.getMonoArrayList().size() == 0) {
return "0";
}
sb.append(this.getMonoArrayList().get(0).toString());
for (int i = 0; i < this.getMonoArrayList().size() - 1; i++) {
if (this.getMonoArrayList().get(i + 1).getCoef() == BigInteger.ZERO) {
continue;
}
sb.append("+");
sb.append(this.getMonoArrayList().get(i + 1).toString());
}
return sb.toString();
}
包含三个运算方法addPoly()、multPoly()、powPoly()来展开,通过derive()进行对Poly的求导。
通过上文的词法分析可以得到表达式的所有信息,以expr保存。经过Expr、Term、Factor类的toPoly()方法来递归获得Poly。
这实际是展开括号的过程。
用于输出。
并无重构
在第一次作业中,显然选择使用课程中的递归下降来解构表达式,而在面对去括号的思考中,我认识到递归的展开或者递归的展开成多项式可以解决嵌套括号,于是打好了地基,这也是三次作业没有重构的根本原因。
这一次作业难度较大,因为多项式的基本结构改变了,我先实现了exp信息的储存,改变了合并同类项的条件。
面对自定义函数,在词法分析时来替换func成表达式因子。
增加了Exp类、FuncFactor类
第三次作业只需同理其他因子,为得到表达式和Poly这个目标来努力。
由于项目含有多项式的确立和各种用来解构表达式的递归函数方法,代码在新增需求时,只需以下步骤:
(1)分析单项式是否有变化,调整单项式性质
(2)分析多项式运算的变化,调整addPoly()、multPoly()、powPoly()、derive()。
(3)分析新增内容是否改变toPoly()。
(4)在递归下降中插入新增需求。
(5)分析输出toString()。
bug主要是在Poly运算前,没有合并同类项,导致计算错误,或者Poly没有合并。
而且没有考虑*后为负数,即面对*-会出现bug
0 (((((((((((x^8)^8)^8)^8)^8)^8)^8)^8)^8)^8)^8)^8
没有考虑幂的范围超出Integer
在多项式运算过程中会产生很多COEF为0的项,对这些无用项运算时,会浪费很多时间,并且产生更多无用的0项。所以在运算前删除为0的mono,可以解决TLE问题。
无
(1)思考边界情况,如0^0、次幂的范围。
(2)分析易错的情况如exp((-x)),检查他人代码是否含有特判。
(3)生成简单的测试用例后,检验他人代码是否正确输出,来确定他人代码没有特别大问题。
及时删除没用的0项。
(1)系数COEF为0则不输出+、-和接下来0*x^a*exp...
(2)coef为1或-1时,不输出1或-1
(3)x^1时只输出x,exp同理
(4)在合并同类项的时候,exp因子判等后,会提出两个exp的次幂在合并前的公因数,大多情况下,是可以减少长度的。
将预处理和自定义函数等单独写静态方法,使得结构性增强。
第一次作业是最难的,0到1最难的就是开始。
面向对象项目的完成,首先需要有清晰的实现框架,先搭建好信息储存和信息分析过程,然后,搭建好所构成的类,通过分析各自类的作用和属性来选择类之间继承、接口等关系,通过方法的调用来实现处理信息的过程,最后完成信息处理结果的优化后的输出。
而整个信息处理过程,最重要的是开始,好的架构地基可以减少需要重构的情况。
经过OO的一单元学习,深刻地认识到了面向对象的魅力,近千行的代码更让我的能力逐渐提升。
第一单元的作业已经是很完美的课设了,但是第一次作业对于没有学过OOPRE的同学很不友好,建议第一次作业实现嵌套括号,这样可以让学生优先考虑递归的使用,而不是走其他不易迭代的弯路。