北航oo作业第三单元小结

一、梳理JML语言的理论基础

1、jml的注释结构

  jml注释语言的每一行都以@作为开始,若是块注释,则需要在注释块的首尾使用/*@ 与@*/

2、jml的表达式体系

1、原子表达式

  表达式可以看作是一个原子类型,常见的有\result表达式(用于表达某个方法执行后的结果)、\old表达式(用来表示某个对象执行某操作之前的值)。

2、量化表达式

  量化表达式是对给定范围内对象批量操作的表达式,其存在大大简化了jml语言,常见的有\forall表达式(范围内对象,都要满足某要求)、\exists表达式(范围内存在对象,满足某要求)、以及\sum表达式、\product表达式、\max表达式等数学方法的量化表达式

3、集合表达式

  用来在jml规格中构造一个集合(容器),来简化jml规格

4、操作符

  包括算数操作符和逻辑操作符。例如等价操作符<==> 与 ==,其中特别要注意的是<==> 与 ==这样的操作符,优先级是不一样的!

  此外操作符还包括变量引用操作符,例如\nothing指示一个空,\everything指示一个全集。

3、jml的方法规格

  我们使用与学习的方法规格只是最基础的部分,核心内容是三方面:前置条件、后置条件、副作用限定。

1、前置条件

  前置条件使用requires语句来表示,requires P意味着需要确保P的值为真。需要注意的是,requires语句如果多行一起出现,是并列条件,需要全部满足。

2、后置条件

  后置条件使用ensures语句来表示,ensures P意味着需要确保P的值为真。同样,ensures语句如果多行一起出现,是并列条件,需要全部满足。

3、副作用限定

  在方法执行时,为了满足前置条件与后置条件,我们可能会对对象做一些与前置条件和后置条件无关的改动,也就是副作用。有时候需要通过副作用限定来对方法实现进行约束。

  这里,我们可以选择assignble和modifiable。

  assignble 表示可赋值,而 modifiable 则表示可修改。虽然二者有细微的差异,在大部分情况 下,二者可交换使用。

  这两个关键词既可以约束特定的对象,表示更改特定对象。也可以选择约束\nothing 和 \everything这样的关键词,来表示什么都不更改,或是全部更改。

4、其他

  当前置条件与后置条件需要对多个对象进行约束时,可以与用\forall或者\exists表达式结合使用。

  我们使用signals语句,来表示异常处理,语句signals (***Exception e) b_expr 的意思是当 b_expr 为 true 时,方法会抛出括号中给出 的相应异常e。

4、jml的类型规格

  不变式(invariant),语法上定义 invariant P ,其中 invariant 为关 键词, P 为谓词,表示要求在所有可见状态下都必须满足的特性。

  状态变化约束(constraint),用来对前序可见状态和当前可见状态的 关系进行约束。

5、总结

  jml语言是一套逻辑严密的注释语言体系,通过对方法行为的描述,预防bug的出现,并使得检查过程比原先轻松,提高了代码的可维护性。

二、jml应用工具链情况

  首先,是jml语言的编写,可以使用专门的jml编译器。

  jml规格的校验,可以使用openjml。

  JMLUnitNG可以根据规格的实现自动生成TestNG测试样例。

三、部署JMLUnitNG

 1、测试代码

package demo;
public class Demo {
private /*@ spec_public @*/ int[] nodeList;

public Demo(int[] nodeList) {
this.nodeList = new int[16];
}


/*@ requires index >= 0 && index < size();
@ assignable \nothing;
@ ensures \result == nodes[index];
@*/
public /*@pure@*/ int getNode(int index){
if (index >= 0 && index < size()) {
return nodeList[index];
} else {
return 0;
}
}

//@ ensures \result == (nodes.length >= 2);
public /*@pure@*/ boolean isValid(){
boolean ret = false;
if (nodeList.length >= 2) {
ret = true;
}
return ret;
}

public static void main(String[] args){

}


}

2、测试方法

  首先,尽管已经有很多前人感谢过伦佬了,但我还是要在这里郑重地再感谢一次伦佬。

3、测试结果

[TestNG] Running:
Command line suite

Passed: racEnabled()
Passed: constructor Demo()
Passed: <<demo.Demo@1a8f2434>>.getNode(-2147483648)
Passed: <<demo.Demo@5f5a92bb>>.getNode(0)
Passed: <<demo.Demo@376b9c22>>.getNode(2147483647)
Passed: <<demo.Demo@54768a01>>.isValid()
Passed: static main(null)
Passed: static main({})

===============================================
Command line suite
Total tests run: 8, Failures: 0, Skips: 0
===============================================

4、测试思考

  这个自动化测试emmmm,实际上,只是测试了几个边界条件,似乎正能起到一个锦上添花的作用。

四、架构设计

1、第一次作业

  在pathcontainer中,我的选择是构造三个hashmap,一个用path作为key,pathid作为value。另一个用pathid作为key,path作为value。(这里需要在mypath类中重构path对象的hashcode方法)。这两个hashmap实现了path的快速查找和改动(总有一个hashmap可以用key查找,查找出来的value作为另一个hashmap的key)。第三个hashmap,使用node作为key,node的数目作为value,用来,存储当前容器内的节点数量。

  在path中,除了本身的节点数组外,我还构造了一个hashmap,用于判断path中是否含有某node。

2、第二次作业

  本次作业要求,在第一次作业的基础上,加入了判断两个节点是否联通,两个点是否能构成一条边,以及得到两个节点间的最短路径。

  我并没有对第一次的构造进行改动,而是在第一次作业的基础上又构造了两个静态数组,一个hashmap,和一个arraylist,其中hashmap是保存节点与映射节点的关系(我将图内的节点映射到了0到nodesize内),具体做法是,先检查arraylist如果arraylist不为空,从arraylist内取出一个可映射的值,与当前节点映射。否则,当前映射id与当前节点映射并加一。当一个节点,不存在图中时,将其从hashmap中删除,并将对应的映射值,存入arraylist中。

  两个静态数组都是大小为nodesize的二维数组,(节点查找使用映射值)一个存储的是当前图内的点的联通情况(权重为1),另一个存储的是对联通性矩阵进行floyd运算后的矩阵。

3、第三次作业

  本次作业,对nodesize要求更改,并且添加了新的需求,联通块数量计算,最少票价计算,最少换乘次数计算,最少不满意度计算。

  首先,nodesize的更改,由于我没有使用magic number(上一次作业的教训),直接改动nodesize即可。

  此外,分析发现,新的需求中,联通块计算可以通过并查集实现,我增加了并查集方面的方法。

  而剩下的三个需求,其实可以和第二次作业中的最短路径一起归纳为图的权重问题。我对第二次作业的floyd方法进行了重构,重构成了一个templatefloyd(c++课中的模板类思想),并又构建了5个二维矩阵,用来存储不同需求下的图的权重矩阵和floyd运算后的结果矩阵。

五、bug与修复

1、第一次作业

  未发现bug。

2、第二次作业

  本次作业中,我忽略了得到最短路径方法的jml规格中的描述:当fromnodeid == tonodeid时,最短路径为0 ,导致了大面积的wa。

3、第三次作业

  未发现bug。

六、心得体会

  首先,我深刻理解了这一单元最开始时,老师在课上反复强调的,只要严格按照jml来实现,就不会有bug。自己的bug也是没有完全按照jml的约束实现。

  然而,在jml的实现中,仅仅做到实现jml也是不足够的。我的第三次作业的强测有一个点过的特别危险,35s的时间limit,我花了34.6779s跑完了程序。诚然,我严格按照jml约束实现了,但我没有考虑到性能的优化。在jml的实现中,还要注意性能的优化。

 

posted @ 2019-05-21 23:38  溪阶砧  阅读(202)  评论(0编辑  收藏  举报