BUAA-OO-Unit 1: 表达式展开

BUAA-OO-Unit 1: 表达式展开


第一单元的主题是对表达式进行括号展开,主要训练目标是体会层次化设计的思想的应用和工程实现。本单元一共有三次作业,难度递增,从包含含加、减、乘、乘方以及括号的单变量多项式展开,依次加入指数函数、自定义函数和求导运算。以下将对这三次作业逐个进行分析并总结。

第一次作业

本次作业需要完成的任务为:读入一个包含加、减、乘、乘方以及括号(其中括号的深度至多为 1 )的

单变量表达式,输出恒等变形展开所有括号后的表达式。

UML类图

本次作业代码的UML类图如下:

在这里插入图片描述
从图中可以看出,所建的类大致可以分为解析类、储存类和运算类,具体每个类的设计考虑见下。

架构设计

在分析题目和参考训练题后,我将本次作业分为预处理、解析、储存、展开与优化等五个任务。

字符串预处理

由于输入的字符串中会含有空格、连续的加减号等,需要进行预处理以方便之后的操作。在Preprocessor类中:

  • 删除所有空格和制表符

  • 通过遍历合并连续的加减号

  • 将连续的加号替换为单个加号,并移除表达式开头的加号

最后输出一个字符串交给负责解析的类。

表达式解析:递归下降

根据递归下降的思想,我们要自上而下建立语法树,进行词法和语法的分析。

Lexer

这一部分是词法分析器,主要负责将经过预处理后的字符串分解成一系列有意义的单元,称为“token”。这个过程是解析器的第一步。在第一次作业中token 包含+, -, *, (, ), ^, x以及数字。

Lexer中的核心公共方法是next(),通过if-else语句识别数字和特定的单字符token。通过peek()方法可以访问当前的token。该方法使得外部代码能够在不改变Lexer状态的情况下检查当前token。

Parser

这一部分是语法分析器,使用Lexer实例来逐个读取输入字符串中的token,然后根据这些token构建表达式(Expr)、项(Term)和因子(Factor)对象。仿照训练题的代码,Parser类由以下三个主要方法组成:

  • parseExpr():解析整个表达式,包含一个或多个项。这个方法循环调用parseTerm(),直到遍历完所有的token或遇到右括号)为止,表示表达式的结束。加减号并没有在此处处理,而是在parseTerm中进行处理,作为项的正负号。
  • parseTerm():解析单个项,由一个或多个因子组成(通过乘号连接)。这个方法会处理加减号、识别乘号,并根据这些信息构建Term对象。通过循环调用parseFactor()方法,同时进行系数的相乘和指数的相加,最终在Exprlist中增加一个Term
  • parseFactor():解析单个因子,因子可能是数字、变量、带括号的表达式或变量的指数形式。这个方法根据当前token的类型决定如何构建因子对象,并处理指数。遇到带括号的表达式则调用parseExpr递归地解析。
表达式展开

经过以上处理后,得到的Expr中含有一个装有TermList, 而Term由两个List组成,分别装表达式因子和其他因子。其他因子在parseTerm部分以及进行计算,得到两个数字分别代表系数和x的指数,即:
F ( x ) = ∑ i a i x b i F(x) = \sum_{i} a_i x^{b_i} F(x)=iaixbi
基于此,我新建了Operator类和MulExpr类进行处理。在MulExpr中我构建了两个公开方法mulExprmulTerm,可以读入两个表达式/项,返回其相乘的结果。在Operator类遍历ExprTerm列表,取出exprFactor列表不为空的项,对其调用表达式乘法和项乘法将括号展开,最终返回一个不带括号的表达式。

其中需要注意的是,为了提高程序的性能,处理更复杂的表达式展开,需要在mulExpr之后调用合并同类项的方法,即下面的优化。

结果优化与输出

为了缩短最终输出的字符串的长度,需要对展开后的表达式进行合并同类项,即将x的指数相同的项的系数相加。这里我用了HashMap来储存指数作为key,系数作为value,具体实现的代码如下:

HashMap<Integer, BigInteger> sites = new HashMap<Integer, BigInteger>();
        for (int i = 0; i < numTerm; i++) {
   
            Term term = input.getTerm(i);
            BigInteger coe = term.getCoe().getValue();
            int exp = term.getExp().getExp();

            if (sites.containsKey(exp)) {
   
                BigInteger newValue = sites.get(exp).add(coe);
                BigInteger value = sites.replace(exp, newValue);
            } else {
   
                sites.put(exp,coe);
;           }
        }

之后输出时需要依次重写 TermExprtoString方法,同时在其中需要进行一些特判以进一步缩短输出长度,包括:

  • 系数为0的项不输出,如果最后字符串为空则输出0;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值