一、JML语言理论基础
参考基础教程:https://blog.csdn.net/weixin_41412192/article/details/89527142
- 注释结构
JML以javadoc注释的方式表示规格,每行以@起头。注释方式有两种:行注释//@ annotation
和块注释/*@ annotation @*/
。注释一般放在被注释成分的临近上部。
- 原子表达式
\result :表示非void型方法执行结果
\old(expr) :表示表达式expr在相应方法执行前的取值
\not_assigned(x, y, ...) :表示变量x, y, ...在方法执行过程中不被赋值,常用于后置条件的约束表示
\not_modified(x, y, ...) :类似于\not_assigned(x, y, ...)
\nonnullelements(container) :表示container对象中存储的对象不会有null
- 量化表达式
\forall :全称量词修饰的表达式
(\forall inti,j; 0 <= i && i < j && j < 10; a[i] < a[j])
\exists :存在量词修饰的表达式
(\existsint i; 0 <= i && i < 10; a[i] < 0)
\sum :返回给定范围内的表达式的和
(\sum int i; 0 <= i && i < 5; i)
\product :返回给定范围内的表达式的连乘结果
(\product int i; 0 < i && i < 5; i)
\max :返回给定范围内的表达式 <=!=> 的最大值
(\max int i; 0 <= i && i < 5; i)
\min :返回给定范围内的表达式的最小值
(\min int i; 0 <= i && i < 5; i)
\num_of :返回指定变量中满足相应条件的取值个数
(\num_of int x; 0
- 操作符
<: :子类型关系操作符,E1<:E2表示E1是E2的子类型或者与E2类型相同
<==> :等价关系操作符,b_expr1<==>b_expr2表示b_expr1==b_expr2,但优先级比==低。<=!=>同理
==> :推理操作符,b_expr1==>b_expr2表示b_expr1->b_expr2
\nothing :空集
\everything :全集,当前作用域下能访问到的所有变量
- 方法规格
requires P :前置条件,方法调用者确保P为真
ensures P :后置条件,方法实现者确保方法执行后P为真
assignable x, y, ... :副作用约定,方法在执行过程中对x, y, ...做了修改
二、应用工具链
openjml+solver
参考教程:https://www.cnblogs.com/lutingwang/p/openjml_basic.html
三、架构设计
本单元中三次作业的逻辑结构均已给定,主要分为三层:
Person类:表示社交网络中的每个单一个体,具有id, age, character等属性
Group类:表示社交网络中由一些个体组成的群组
Network类:整个社交网络
四、作业bug
由于一开始几乎是对jml直接进行翻译,几乎未考虑优化问题,导致后面runtime严重超时。通过课下的讨论交流以及学习其他同学的代码,发现可以做如下优化:
- 使用HashMap进行存储以简化遍历查找时的操作,例如:(1)在Person类中,使用HashMap来存储acquaintance,使得在isLinked方法、queryValue方法的查询过程中,可以直接通过id获取Person对象,而无需遍历整个acquaintance数组。(2)在Group类中,使用HashMap来存储其中的people。
- 采用缓存设计以简化查询时的计算,例如:在Group类中,可以添加属性relationsum,在每次向组内添加新的人或关系时,可以直接对relationsum进行修改,避免每次调用getRelationSum方法时重新计算。
- queryMinPath:我用的是未进行优化的dijstra算法,所以性能上不够优越,可以使用堆优化后的算法,即更新最短距离时将其加入小顶堆,以降低复杂度至O(logn)。
五、心得体会
通过对本单元的学习,体会最深的是,jml着重强调单元块的正确性,帮助聚焦于眼下的单元快,使得代码的编写更加规范和模式化。但是在根据jml编写代码的过程中,我个人很容易忽略掉性能方面的不足,不自觉地会在思考如何实现能够使得性能更优方面有所懈怠。