概述:
面向对象第三单元的任务是根据JML规格,继承相应的接口,实现相应的类。其中涉及一系列的算法以及数据结构的选择问题,高效的算法和合适的数据结构能够减少CPU时间、运行时间和运行内存,使得程序得到优化。
一、梳理JML语言的理论基础、应用工具链情况
1、理论基础
Java建模语言是用于对Java程序进行规格化设计的一种表示语言。在Java代码中增加了一些符号,这些符号用来标识一个方法是干什么的,却并不关心它的实现。
JML规格大致包含以下内容:
public model instance JMLObjectBag elementsInQueue;(模型域)
public normal_behavior(方法正常功能)
public exceptional_behavior(方法异常功能)
requires(前置条件)
ensures(后置条件)
assignable(副作用范围限定)
JML表达式(\result \old() \forall \exists \nothing\everything <=!=> <==> ==>等)
invariant(不变式)
constraint(状态变化约束)
2、应用工具链
openjml测试JML语言的正确性
Junit4生成模板,构造测试样例,单独测试每个函数是否符合JML
JMLUnitNG自动生成测试样例
二、部署SMTSolver,至少选择3个主要方法来尝试进行验证,报告结果
环境为IDEA+openjml插件+openjml,对第9次作业的MyPathContainer.java进行测试,第一次运行结果如下:
Start OpenJML/ESC with file MyPathContainer.java C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:66: 错误: A \old token with no label may not be present in a requires clause @ requires path != null && path.isValid() && \old(containsPath(path)); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:82: 错误: A \old token with no label may not be present in a requires clause @ requires \old(containsPathId(pathId)); ^2 个错误 |
报告了两个规格的错误,修改之后再次运行,报了更多的错误......如下:
Start OpenJML/ESC with file MyPathContainer.java 错误: A catastrophic JML internal error occurred. Please report the bug with as much information as you can. Reason: Mismatch in number of arguments in accumulateTypeInstantiationsC:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:54: 警告: The prover cannot establish an assertion (ArithmeticOperationRange) in method addPath: overflow in int sum id++; ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:57: 警告: The prover cannot establish an assertion (ArithmeticOperationRange) in method addPath: overflow in int sum sizeall++; ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:49: 警告: The prover cannot establish an assertion (ExceptionalPostcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:58: 注: ) in method addPath if (path == null || !path.isValid()) { ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:58: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:49: 注: @ normal_behavior ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:52: 警告: The prover cannot establish an assertion (Postcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:49: 注: ) in method addPath return pathMap.get(path); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:49: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:52: 注: @ ensures (pidList.length == pList.length); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:52: 警告: The prover cannot establish an assertion (Postcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:50: 注: ) in method addPath return pathMap.get(path); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:50: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:52: 注: @ ensures (\exists int i; 0 <= i && i < pList.length; pList[i] == path && ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:52: 警告: The prover cannot establish an assertion (Postcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:55: 注: ) in method addPath return pathMap.get(path); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:55: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:52: 注: @ ensures (\forall int i; 0 <= i && i < \old(pList.length); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:49: 警告: The prover cannot establish an assertion (ExceptionalPostcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:46: 注: ) in method addPath if (path == null || !path.isValid()) { ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:46: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:49: 注: /*@ normal_behavior ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:25: 警告: The prover cannot establish an assertion (Postcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:12: 注: ) in method containsPath return pathMap.containsKey(path); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:12: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:25: 注: @ ensures \result == (\exists int i; 0 <= i && i < pList.length; ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:29: 警告: The prover cannot establish an assertion (Postcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:17: 注: ) in method containsPathId return idMap.containsKey(pathId); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:17: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:29: 注: /*@ ensures \result == (\exists int i; 0 <= i && i < pidList.length; ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:98: 警告: The prover cannot establish an assertion (Postcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:94: 注: ) in method getDistinctNodeCount return sizedis; ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:94: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:98: 注: /*@ ensures (\exists int[] arr; (\forall int i, j; 0 <= i && i < j && j < arr.length; arr[i] != arr[j]); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:33: 警告: The prover cannot establish an assertion (ExceptionalPostcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:22: 注: ) in method getPathById if (!containsPathId(pathId)) { ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:22: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:33: 注: /*@ public normal_behavior ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:33: 警告: The prover cannot establish an assertion (ExceptionList: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:30: 注: ) in method getPathById if (!containsPathId(pathId)) { ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:30: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:33: 注: @ signals_only PathIdNotFoundException; ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:36: 警告: The prover cannot establish an assertion (Postcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:25: 注: ) in method getPathById return idMap.get(pathId); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:25: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:36: 注: @ ensures (pidList.length == pList.length) && (\exists int i; 0 <= i && i < pList.length; pidList[i] == pathId && \result == pList[i]); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:42: 警告: The prover cannot establish an assertion (ExceptionalPostcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:40: 注: ) in method getPathId throw new PathNotFoundException(path); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:40: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:42: 注: @ signals (PathNotFoundException e) path == null; ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:44: 警告: The prover cannot establish an assertion (Postcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:37: 注: ) in method getPathId return pathMap.get(path); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:37: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:44: 注: @ ensures (pidList.length == pList.length) && (\exists int i; 0 <= i && i < pList.length; pList[i].equals(path) && pidList[i] == \result); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:44: 警告: The prover cannot establish an assertion (Postcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:39: 注: ) in method getPathId return pathMap.get(path); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:39: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:44: 注: @ public exceptional_behavior ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:41: 警告: The prover cannot establish an assertion (ExceptionalPostcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:34: 注: ) in method getPathId if (path == null || !path.isValid() || !containsPath(path)) { ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:34: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:41: 注: /*@ public normal_behavior ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:65: 警告: The prover cannot establish an assertion (ExceptionalPostcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:75: 注: ) in method removePath throw new PathNotFoundException(path); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:75: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:65: 注: @ signals (PathNotFoundException e) path == null; ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:64: 警告: The prover cannot establish an assertion (ExceptionalPostcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:65: 注: ) in method removePath if (path == null || !path.isValid() || !containsPath(path)) { ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:65: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:64: 注: /*@ public normal_behavior ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:77: 警告: The prover cannot establish an assertion (ExceptionalPostcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:81: 注: ) in method removePathById if (!containsPathId(pathId)) { ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:81: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:77: 注: /*@ public normal_behavior ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:78: 警告: The prover cannot establish an assertion (ExceptionalPostcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:90: 注: ) in method removePathById throw new PathIdNotFoundException(pathId); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:90: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:78: 注: @ signals (PathIdNotFoundException e) !containsPathId(pathId); ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:21: 警告: The prover cannot establish an assertion (Postcondition: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:7: 注: ) in method size return sizeall; ^C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\PathContainer.java:7: 警告: Associated declaration: C:\Users\Administrator\Desktop\HW09\HW9\src\com\oocourse\specs1\models\MyPathContainer.java:21: 注: //@ ensures \result == pList.length; ^1 个错误42 个警告 |
前两个错误是整数溢出。
三、部署JMLUnitNG/JMLUnit,针对Graph接口的实现自动生成测试用例,并针对规格对生成的测试用例和数据进行简要分析
对compare函数进行测试,函数内容如下图:
生成的测试文件如下图:
测试过程及运行结果如下图:
发现自动生成的测试样例是通过判断大数据和零来验证结果的。
四、按照作业梳理自己的架构设计,并特别分析迭代器中对架构的重构
1、第9次作业架构
MyPath.java中用ArrayList和HashSet双容器来存一条路径上的节点,根据下标查找用ArrayList,根据nodeid查找用HashSet。
MyPathContainer.java中用了两个HashMap分别将Path映射到Pathid和将Pathid映射到Path,增删Path时同时对两个容器操作。寻找不同节点个数时,将Path中所有的node都放到一个HashSet中,计算该HashSet的大小。
2、第10次作业架构
MyGraph.java中用HashMap<Edge, Integer>来保存每个边出现的次数,其中Edge是一个class,包含(int fromNode,int toNode)代表边上的两个端点。用floyd算法计算出每两个点之间的最短路径,用HashMap<Edge, Integer>存放结果,代表每两个点之间的最短路径,每次增删节点时重新计算一遍。
3、第11次作业架构
MyRailwaySystem.java中用HashMap将nodeid映射到[0,120),之后根据四个不同的问题分别跑floyd算法。
最短路径问题,将有边的两个点之间初始化为边的长度,其余的点之间初始化为-1,跑floyd算法,将结果存放在二维数组中。
最小换乘问题,将每条路径上的每两个点之间初始化为1,其余的点之间初始化为-1,跑floyd算法,将结果存放在二维数组中。
最低票价问题,将每条路径上的每两点之间初始化为二者之间的最短路径+2,其余的点之间初始化为-1,跑floyd算法,将结果存放在二维数组中。
最小不满意度问题,将每条路径上的每两点之间初始化为二者之间的最小不满意度+32,其余的点之间初始化为-1,跑floyd算法,将结果存放在二维数组中。
连通块的个数用并查集来求。
每次增删path重新计算一遍。
五、按照作业分析代码实现的bug和修复情况
代码的正确性上来讲是没有bug的,但是对于第10次作业,强测中出现了大量的CPU超时。经过分析,超时的原因是,我的代码中实现了一个Edge类(其中包含(int fromNode,int toNode)),类中存了这条边的两个端点,程序中大量的new Edge导致了时间和空间的损失,特别是当nodeid特别大的时候,超时明显。
为了修复这个bug,我应该将nodeid映射到[0,200),并采用别的数据结构例如静态数组来存放边。
六、阐述对规格撰写和理解上的心得体会
规格的撰写是一个需要大量的思考的过程,应该将每种情况都考虑到,从而保证正确性。
而规格的解读加代码实现的过程,是一个思维转换的过程,因为规格中的数据结构并不一定是你代码实现的数据结构。规格中的数据结构是为了说明正确性方便,代码中的数据结构要考虑正确性+效率。