博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
BUAA-OO 前三次作业“表达式求导" 总结与思考
阅读量:4916 次
发布时间:2019-06-11

本文共 8018 字,大约阅读时间需要 26 分钟。

写在前面

  OO作业已经做了三周,有种“一周蜕一层皮”的感觉,还是挺酸爽的。之前虎头蛇尾的博客经历告诉我,及时发博文总结反思是必要的,很开心能在博客园建一个专属的技术博客。希望这些拙劣的碎碎念可以给自己也给大家提供帮助和启迪,也期待和大家共同交流!

  PS. 讲一个悲伤的故事,这是这个博文的第三稿...前两稿都被博客园和typora无情吞掉了,于是在默写第三遍之后有些话就省去了...希望以后的博客作业能被电脑温柔对待;(

一、需求分析

用JAVA实现多项式的合法性判断和求导运算。

1)多项式是带常数的幂函数之和

2)多项式是带常数的幂函数和基本三角函数的乘积项之和

3)在(2)的基础上,三角函数的自变量可以进行嵌套

二、思路分析

1、基于度量的程序结构分析

类关系图(利用UML Support插件)

第一次

第二次

第三次

 

代码行数统计(利用Statistic插件)

第一次

 

第二次

 

 

第三次

 

代码设计复杂度(利用MetricsReloaded插件)

ev(G)基本复杂度,用来衡量程序非结构化程度

iv(G)模块设计复杂度,用来衡量模块判定结构
v(G)独立路径条数

第一次

 

第二次

 

第三次

2、BUG分析

第一次:

强测中得分 100

互测:由于被 hack 扣分 1 分。这其中包括了 13 个错误,同时hack 他人成功 8 次,得分 18.6 分。

自己错误:1)未判断其他空白字符的不合法性 2)处理第一项前面的符号时,未考虑空白字符\t的情况

他人错误:正则表达式疏漏,特别是对符号和数字间的空格的特殊处理。例如 -   -   5

第二次:

在强测中得分 93.8423

未被hack。hack 他人成功 7 次,得分 0.625 分。

他人错误:正则表达式疏漏,特别是三角函数特殊位置空白字符的判断。例如cos ( x ),      -+x

第三次:

在强测中得分 84.7059

由于被 hack 扣分 2 分。这其中包括了 3 个错误。同时, hack 他人成功 25 次,得分 16.75 分

自己错误:1)某一处合并同类项写挂了,导致求导结果不对 2)乘积项输出时,若为空串应直接return,否则会执行下一条charAt(0)语句导致访问越界。例如:+-sin ((x-x) ) ^ +07

他人错误:同前两次。随机数据都能一串三,也警醒我们自测的重要性。

 

三、知识技能总结

1、正则表达式

(1)正则表达式的组命名:

//正则表达式的组命名        String sin = "(sin\\((?
.*)\\)(\\^" + num + ")?)"; String cos = "(cos\\((?
.*)\\)(\\^" + num + ")?)";//调用 r = Pattern.compile(sin); m = r.matcher(str); if (m.matches()) return isFactor(m.group("quad"));

(2)正则表达式的回溯

java的正则表达式,相当于把我们在C语言中用栈处理表达式运算符的过程封装起来,所以在匹配过程中要考虑到爆栈问题。正则表达式量词匹配模式区别就在于回溯(弹栈)方式的不同。具体如下:

 

1     private String matchProduct(String str) { 2         String num = "[+-]?\\d+"; 3         String pow = "x(\\^" + num + ")?"; 4         String sin = "sin\\(x\\)(\\^" + num + ")?"; 5         String cos = "cos\\(x\\)(\\^" + num + ")?"; 6         String factor = 7                 "(" + num + ")|(" + pow + ")|(" + sin + ")|(" + cos + ")"; 8         String product = 9                 "^[+-]{1,2}(\\d+\\*)?((" + factor + ")\\*)*+(" + factor + ")";10         Pattern r = Pattern.compile(product);11         Matcher m = r.matcher(str);12         if (m.find()) {13             return m.group();14         } else {15             return "";16         }17     }
我觉得比较好的正则匹配写法,用到了占有型匹配

2、Hashmap

 • 第一次作业,直接用指数作为Key即可

 • 第二次作业,需要使用(a,b,c)三元对作为自定义Key

1 import java.util.Arrays; 2 public class Tuple { 3     private final int x; 4     private final int y; 5     private final int z; 6     public Tuple(int x, int y, int z) { 7         this.x = x; 8         this.y = y; 9         this.z = z;10     }11     public int getX() {12         return x;13     }14     public int getY() {15         return y;16     }17     public int getZ() {18         return z;19     }20 21     @Override22     public boolean equals(Object obj) {23         if (obj == this) {24             return true;25         } else if (obj instanceof Tuple) {26             return (((Tuple) obj).x == this.x)27                 && (((Tuple) obj).y == this.y)28                 && (((Tuple) obj).z == this.z);29         } else {30             return false;31         }32     }33 34     @Override 35     public int hashCode() {36         return Arrays.hashCode(new int[]{x, y, z});37     }38     @Override 39     public String toString() {40         return String.format("(%s, %s, %s)", x, y, x);41     }42 }43 /******************************************************/44 import java.util.HashMap;45 public class Main {46     private static final HashMap
hashMap = new HashMap<>();47 private static void showHashMapValue(Tuple tuple) {48 if (hashMap.containsKey(tuple)) {49 System.out.println(String.format(50 "Value of %s is : %s", tuple.toString(), hashMap.get(tuple)));51 } else {52 System.out.println(String.format(53 "Value of %s not found!", tuple.toString()));54 }55 }56 57 public static void main(String[] args) {58 hashMap.put(new Tuple(1, 2, 3), 1);59 hashMap.put(new Tuple(1, 3, 4), 2);60 hashMap.put(new Tuple(2, 3, 5), 3);61 showHashMapValue(new Tuple(1, 2, 3));62 showHashMapValue(new Tuple(1, 3, 4));63 showHashMapValue(new Tuple(2, 3, 5));64 showHashMapValue(new Tuple(2, 4, 0));65 }66 }

3、考虑合并同类项的设计思路

4、对拍程序

已上传至GitHub

5、继承与接口

首先明确一点:能用接口就不用继承。

静态绑定与动态绑定

静态绑定:在程序执行前已经被绑定,也就是说在编译过程中就已经明确一个对象\类应该调用哪个方法,此时由编译器获取其他连接程序实现。

动态绑定(多态):在程序运行根据具体对象的类型进行绑定,调用对应的方法。

6、递归下降分析

1 /*******************分解表达式************************/ 2 import java.util.ArrayList; 3 public class ExpressionParser { 4     private enum Status { // 表达式解析状态 5         BEGIN, // 开始状态 6         SIGN, // 符号状态 7         TERM, // 项状态 8         END, // 结束状态 9     }10     11     private Status status; // 当前状态信息12     private ArrayList
items;13 14 private final String string;15 private int index;16 17 public ExpressionParser(String string, int index) {18 this.string = string;19 this.index = index;20 this.status = Status.BEGIN;21 }22 23 public Expression parse() {24 // 起初为开始状态25 // 如果读到了+/-,则进入符号状态,否则记录符号为+后进入项状态26 27 // 加减状态28 // 读取+/-,并记录,之后进入项状态29 30 // 项状态31 // 实例化一个新的TermParser,传入字符串以及当前位置32 // 用此TermParser来生成解析出一个Term,进行存储33 // 如果接下来还有+/-,则进入加减状态,否则进入结束状态34 35 // 结束状态36 // 整理获取到的值,并返回37 }38 }39 /**************分解乘积项***********************/40 import java.util.ArrayList;41 public class TermParser {42 private enum Status {43 BEGIN, // 开始状态44 SIGN, // 符号状态45 FACTOR, // 因子状态46 END, // 结束状态47 }48 private Status status;49 50 private ArrayList
factors;51 private final String string;52 private int index;53 54 public TermParser(String string, int index) {55 this.string = string;56 this.index = index;57 this.status = Status.BEGIN;58 }59 public Term parse() {60 // 起初为开始状态61 // 直接进入因子状态62 63 // 符号状态64 // 读取*后,进入因子状态65 66 // 因子状态67 // 因子状态下,实例化一个FactorParser,传入字符串和位置信息68 // 使用该FactorParser来解析一个因子,并进行存储69 // 如果下一个字符为*,则进入符号状态,否则进入结束状态70 71 // 结束状态72 // 整理获取到的值,并返回73 }74 }75 /***************分解因子*******************/76 public class FactorParser {77 private final String string;78 private int index;79 public FactorParser(String string, int index) {80 this.string = string;81 this.index = index;82 }83 public Factor parse() {84 // 解析一个因子85 86 // 如果开头是数字,则连续读取一串数字,实例化Constant对象,返回87 88 // 如果开头是x,则读取x,实例化XFunction对象,返回89 90 // 如果开头是s,则读取sin(x),实例化SinFunction对象,返回91 92 // 如果开头是c,则读取cos(x),实例化CosFunction对象,返回93 }94 }

7、设计模式

1)工厂模式

2)单例模式

3)装饰者模式

(%cyx)

以第三次作业为例:

我们可以用Factor(因子)作为抽象父类

以具体的ExprFactor、Sin、Cos等作为被装饰者

用可以嵌套的Sin、Cos作为装饰者(注意到它们既是被装饰者也是装饰者)

调用公有的方法——求导,实现对复合函数的求导

四、自己的一点思考

1、学习方法

1)即用即学

  上学期报了java一般专业课,最后摸了一份大作业出来。但这学期正式开始用java编程时,却发现自己之前真正学到的只是偏GUI的一些语法,连正则表达式都还不会用。于是开学初想像学C那样通读语言书,却发现大多数java编程入门书晦涩生硬,也缺少有用的例子。现在再回过头来再看语法,便能理解得更加透彻了,因为在编程中摸爬滚打尝试过一些东西后,语言书变成了使用正确性的印证和内部机制的解释,而不是必须系统学习后才能开始编程的必要前提。因为不同环节侧重的理论知识不同,“即用即学”远比“在没真正使用一个东西的时候通过阅读来提前学习”效率要高。

2)从算法竞赛看工程作业

  算法竞赛追求巧妙的思维、代码短小精悍易编写,遇到特殊的边界条件直接特判来莽,总之快速AC是王道。这种写程序的策略有点像在“贪心”。

  而工程思维可能更像是“动态规划”,追求“全局最优”,而非“当前最优”。“不行的话我就在这添一添,那里删一删”的想法在这时变得很危险,乱搞和特判的补丁只会让程序看起来极其恶心。设计上的全面考虑和可扩展性在此时显得更加有智慧。

  但是,这两者有共同的要求:

1)同样注重代码实现能力,即在规定时间内完成特定功能的“硬实力”。写大作业就像长跑,配速低并不意味着我们可以在长距离长时限下允许思维怠惰。

2)对bug“零”容忍。虽说“世界上不存在没有bug的程序”,但“只要出错就爆零”的紧迫感,如果迁移到工程上或许会敦促我们把作业完成得更好。

2、调试方法

1)边写边调试

从HW1到HW3,我的强测成绩逐渐变形(笑容逐渐消失),其实用来构思的时间逐渐变长,写代码时间逐渐变长,也感到压力逐渐增大。

这些年自己写过的代码量应该有5w+,却面对上千行、面向对象的代码debug时常感到崩溃。我认为做出按照层次来编写程序会有效缓解这一问题:

输入处理(合法性检查)——>求导运算——>输出处理——>合并同类项——>输出优化(指数、系数、符号)——>进阶优化(三角函数)

每个模块编程完成后,顺手debug,既能减少工作量,又能减轻心理压力。

2)构造测试数据

在编码前就尽可能充分考虑不同数据的处理方式,而不是在周二写完代码开始debug的时候才开始造数据。

在编写代码前能够考虑得约细致,编写代码的过程就越顺利,debug的时间也会减少。

但是,对于我们初学者来说,很多实现细节在缺乏足够的经验时确实很难提前考虑到,踩坑和磨练大概是必经之路。

3)小黄鸭调试法

大概这就是“肉眼debug”的精髓。在完成程序后先前前后后捋一遍,就可以大幅度减少在输出调试和单步调试中的许多脑残、手残错误。

其实在思路卡壳不知道该怎么做的时候也可以尝试这个方法,跟身边的人讲一讲思路,就算他们无法给出可行的解决方案,自己也会对问题更加明晰。

所以欢迎大家来和我唠嗑!说不定可以互相启迪> < ~

毕竟哪个程序员都不想每天只对着二次元纸片人和小黄鸭自言自语:))

转载于:https://www.cnblogs.com/Ryan0v0/p/10601429.html

你可能感兴趣的文章
c++虚函数注意事项
查看>>
翻译qmake文档(四) Building Common Project Types
查看>>
【转】nginx 服务器安装及配置文件详解
查看>>
oracle 函数用法
查看>>
.Net中的水晶报表
查看>>
Android按钮的四种点击事件
查看>>
【Datastage】函数大全
查看>>
读书笔记三
查看>>
Qt解决中文乱码
查看>>
【语言处理与Python】2.1获取文本语料库
查看>>
048-PHP定义常量
查看>>
##管家婆项目(一)
查看>>
LeetCode-64. 最小路径和
查看>>
Python Homework 001
查看>>
Hadoop安装教程_集群/分布式配置
查看>>
CMD和AMD区别的概括
查看>>
数位dp
查看>>
先来个Label吧
查看>>
【转载】树状数组进阶
查看>>
go if 判断 完成随机分数的评级
查看>>