码迷,mamicode.com
首页 > 其他好文 > 详细

BUAA_OO_2020_Unit3_Overview

时间:2020-05-19 22:39:41      阅读:56      评论:0      收藏:0      [点我收藏+]

标签:inf   not   junit   instance   shm   还需要   布尔   mlu   code   

JML理论梳理与工具链分析

JML作为一种行为接口规格语言,可以较为准确地对Java程序的行为进行描述。然而在本人使用过程中,由于其工具链的功能的极不完善,大多数的代码编写及测试还是依靠人力完成的,虽然它具有较高的严谨性,但使用体验并不是很好。

JML的注释结构

JML以javadoc注释的方式表示规格,每行以@起头。行注释为//@annotation,块注释为/* @ annotation @ */

规格变量的声明分为静态与实例两种,声明方式分别为//@public static model non_null int[] elements//@public instance model non_null int[] elements

方法规格的声明主要有三个部分:

  • \requires子句:定义方法的前置条件,是调用者承诺满足的条件。
  • assignable子句:限定方法的副作用范围。assignable \nothing的方法不修改规格变量,因此为/*@pure@*/方法。
  • ensures子句:定义方法的后置条件,是被调用者承诺满足的条件。

JML原子表达式

  • \result表达式:表示一个非void方法执行的返回值。
  • \old(expr)表达式:表示表达式expr在执行方法前的取值。
  • \not_assigned(x, y, ...)表达式:表示括号内的变量在执行中未被赋值。若被赋值则该表达式为false。
  • \not_modified(x, y, ...)表达式:与\not_assigned类似,表示取值未发生变化。
  • \nonnullelements(container)表达式:表示container中不含有null对象。等价于断言: container != null && (\foall int i; 0 <= i && i < container.length; container[i] != null) 
  • \type(expr)表达式:等同于java.lang.class
  • \typeof(expr)表达式:返回准确类型。

JML量化表达式

  • \forall表达式:全称量词修饰的表达式。例如 (\forall int i,j; 0 <= i && i < j && j < 10; a[i] < a[j]) 
  • \exists表达式:存在量词修饰的表达式。例如 (\exists int i; 0 <= i && i < 10; a[i] < 0) 
  • \sum表达式:指定范围内求和表达式。例如 (\sum int i; 0 <= i && i < 5; i) ,这个表达式的值为10。
  • \product表达式:指定范围内连乘表达式。例如  (\product int i; 0 < i && i < 5; i)  ,这个表达式的值为24。
  • \max表达式:最大值表达式。例如 (\max int i; 0 <= i && i < 5; i)  这个表达式的值为4。
  • \min表达式:最小值表达式。例如  (\min int i; 0 <= i && i < 5; i)  这个表达式的值0。
  • \num_of表达式:指定范围内满足条件元素个数表达式。 (\num_of T x; R(x);P(x))  表示R(x)范围内满足P(x)条件的x元素的个数。

JML集合表达式

集合表达式的形式为 new ST {T x|R(x)&&P(x)} ,其中ST是构造的容器,T是数据类型,R是范围约束,P是取值约束。

操作符

  • 子类型关系操作符:E1<:E2,若E1是E2的子类或E1与E2同类则为真,否则为假。
  • 等价关系操作符:expr1<==>expr2expr1<=!=>expr2,两端为布尔表达式,表示两者等价或不等价。
  • 推理操作符:expr1==>expr2,其取值等价于expr1→expr2
  • 变量引用操作符:\nothing表示空集,\everything表示全集

方法规格

除了已经提到的三个方法规格外,还有signals与signals_only,用于exceptional behavior的处理。

  • signals子句:signals (***Exception e) b_expr,意为当b_expr为true时方法抛出异常e。
  • signals_only子句:后接异常类型,表示满足条件时直接抛出异常。

工具链使用情况

openjml、SMT solver、JMLUnit等,使用时总体的感受是网络上关于JML工具链的资料实在不够翔实,并且这些工具本身在功能上也不够完善。

基于SMT Solver的程序验证

>>>TODO>>>

应用JMLUnit自动化生成测试用例

>>>TODO>>>

作业架构分析

由于官方包中接口的限定,本次作业在架构设计上比较单一,只要设计相应类并实现相应接口即可。主要的创新空间在于具体方法的实现上。由于本单元作业为增量开发,此处仅给出最后一次作业的UML图。

技术图片

除了规定中要实现的各个类外,我还编写了MyRelation类用来管理关系、MyBalance类用来管理存款、UnionFindSet并查集类用来优化查找速度。

在实现细节方面,在第一次作业中我使用了ArrayList来存储各个量,但到第二次作业时迫于10w指令的大计算量,我改用了以id为key的hashmap作为查找容器、ArrayList作为枚举容器的混合方法。

第一次作业几乎没有难点,主要就是一些细节方面的内容,只要仔细阅读、正确理解JML即不会出现问题。第一次作业最复杂的方法是query_circle(),我采用了广度优先搜索的方法。

第二次作业主要是各种query_sum方法比较麻烦,并且考虑到10w指令的影响,需要将各个方法时间复杂度控制在O(N)范围内。我采取的策略时person与relation更新时同时更新各个sum,并将其取值缓存。用这种方式,ap与ar时间复杂度为O(N),各个查询方法时间复杂度为O(1),在性能上达到了要求。

第三次作业的qsl和qmp是最困难的两个方法。我的qsl采用的是枚举删点法,也就是如果某个人与两个端点均连通,则将这个人删去,再求两个端点的连通性。如此遍历关系网中的各个人,如果所有的结果均为连通,则两个端点为强连通。根据估计,计算量最高约为10^8数量级,勉强符合要求,也通过了强测。qmp我采用的是堆优化的dijkstra算法,虽然理论复杂度只有O(nlogn),但由于实现时不够注意细节,在找到人时没有提前break,也没有重写person类的hashcode()方法,导致强测中一个点CTLE。修改后快了0.8s有余,这使我反思,不能仅依靠理论复杂度,更应该注意细节方面的问题。

Bug与修复情况

前两次作业在强测与互测中均没有被发现bug。第三次作业强测CTLE一个点(qmp方法),互测没有被发现bug。修复时重写了person的hashcode,简化了访问标记的初始化过程(主要是去除了一个O(N)遍历),并加入了找到时的提前break。成功修复了CTLE的点。

心得体会

首先的感受是来到了JML这一单元后OO作业的负担减轻了许多,从需要自己设计到只需要根据规格来实现方法,的确简单了许多。但仅仅根据规格又是不够的,还需要我们考虑实现上的细节与性能方面的优化。除此之外,我对“契约式”的理解更加深入了,只有接口的提供者与实现者遵守一套共同的规范,才能保证程序的正确性。

这个单元虽然学习了JML,但因为没有真正地动手写过JML代码(只有实验课做了几个JML的填空题),至今只能读JML来实现方法,而无法写出自己的JML。而且对JUnit的使用还不是十分熟练,感觉在形式化验证与单元测试这一方面需要学习的东西还有很多。

BUAA_OO_2020_Unit3_Overview

标签:inf   not   junit   instance   shm   还需要   布尔   mlu   code   

原文地址:https://www.cnblogs.com/littlenyima/p/12917809.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!